From 7111cbb1033ad47bd7696dc5225dc0f4ae13e08f Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Thu, 3 Aug 2023 12:54:16 +0000 Subject: [PATCH 001/101] Quote and escape desktop entry executable path (#917) --- src/gui/components/wxGameList.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 4f3165ab..69f74870 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1287,17 +1287,18 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { } } } + // 'Icon' accepts spaces in file name, does not accept quoted file paths + // 'Exec' does not accept non-escaped spaces, and can accept quoted file paths const auto desktop_entry_string = fmt::format("[Desktop Entry]\n" - "Name={}\n" - "Comment=Play {} on Cemu\n" - "Exec={} --title-id {:016x}\n" - "Icon={}\n" + "Name={0}\n" + "Comment=Play {0} on Cemu\n" + "Exec={1:?} --title-id {2:016x}\n" + "Icon={3}\n" "Terminal=false\n" "Type=Application\n" "Categories=Game;", title_name, - title_name, _pathToUtf8(exe_path), title_id, _pathToUtf8(icon_path.value_or(""))); @@ -1339,4 +1340,4 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { } #endif } -#endif \ No newline at end of file +#endif From 1d1e1e781b06aad34d616e13ccb47485f3c0db14 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Thu, 3 Aug 2023 14:16:22 +0100 Subject: [PATCH 002/101] Vulkan: Retry instance creation if validation layer is not present (#909) --- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 95386284..cfe7d3f4 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -350,7 +350,15 @@ VulkanRenderer::VulkanRenderer() create_info.ppEnabledLayerNames = m_layerNames.data(); create_info.enabledLayerCount = m_layerNames.size(); - if ((err = vkCreateInstance(&create_info, nullptr, &m_instance)) != VK_SUCCESS) + err = vkCreateInstance(&create_info, nullptr, &m_instance); + + if (err == VK_ERROR_LAYER_NOT_PRESENT) { + cemuLog_log(LogType::Force, "Failed to enable vulkan validation (VK_LAYER_KHRONOS_validation)"); + create_info.enabledLayerCount = 0; + err = vkCreateInstance(&create_info, nullptr, &m_instance); + } + + if (err != VK_SUCCESS) throw std::runtime_error(fmt::format("Unable to create a Vulkan instance: {}", err)); if (!InitializeInstanceVulkan(m_instance)) From 651e5336b465af11ac892f2f3ea3577665307819 Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Thu, 3 Aug 2023 06:45:11 -0700 Subject: [PATCH 003/101] debugger: Add logging breakpoint + misc fixes (#927) --- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 83 ++++++++++---- src/Cafe/HW/Espresso/Debugger/Debugger.h | 6 +- src/gui/debugger/BreakpointWindow.cpp | 107 ++++++++++++------ src/gui/debugger/BreakpointWindow.h | 3 +- src/gui/debugger/DebuggerWindow2.cpp | 11 +- src/gui/debugger/DisasmCtrl.cpp | 51 ++++----- src/gui/debugger/DumpCtrl.cpp | 60 ++++------ .../DebugPPCThreadsWindow.cpp | 4 +- 8 files changed, 193 insertions(+), 132 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index b6417080..0883c436 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -1,5 +1,6 @@ #include "gui/guiWrapper.h" #include "Debugger.h" +#include "Cafe/OS/RPL/rpl_structs.h" #include "Cemu/PPCAssembler/ppcAssembler.h" #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" #include "Cemu/ExpressionParser/ExpressionParser.h" @@ -74,7 +75,7 @@ uint32 debugger_getAddressOriginalOpcode(uint32 address) auto bpItr = debugger_getFirstBP(address); while (bpItr) { - if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_ONE_SHOT) + if (bpItr->isExecuteBP()) return bpItr->originalOpcodeValue; bpItr = bpItr->next; } @@ -121,32 +122,23 @@ void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore) } } -void debugger_createExecuteBreakpoint(uint32 address) +void debugger_createCodeBreakpoint(uint32 address, uint8 bpType) { // check if breakpoint already exists auto existingBP = debugger_getFirstBP(address); - if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_NORMAL)) + if (existingBP && debuggerBPChain_hasType(existingBP, bpType)) return; // breakpoint already exists // get original opcode at address uint32 originalOpcode = debugger_getAddressOriginalOpcode(address); // init breakpoint object - DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, DEBUGGER_BP_T_NORMAL, true); + DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, bpType, true); debuggerBPChain_add(address, bp); debugger_updateExecutionBreakpoint(address); } -void debugger_createSingleShotExecuteBreakpoint(uint32 address) +void debugger_createExecuteBreakpoint(uint32 address) { - // check if breakpoint already exists - auto existingBP = debugger_getFirstBP(address); - if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_ONE_SHOT)) - return; // breakpoint already exists - // get original opcode at address - uint32 originalOpcode = debugger_getAddressOriginalOpcode(address); - // init breakpoint object - DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, DEBUGGER_BP_T_ONE_SHOT, true); - debuggerBPChain_add(address, bp); - debugger_updateExecutionBreakpoint(address); + debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); } namespace coreinit @@ -218,7 +210,7 @@ void debugger_handleSingleStepException(uint64 dr6) } if (catchBP) { - debugger_createSingleShotExecuteBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4); + debugger_createCodeBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT); } } @@ -250,7 +242,7 @@ void debugger_handleEntryBreakpoint(uint32 address) if (!debuggerState.breakOnEntry) return; - debugger_createExecuteBreakpoint(address); + debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); } void debugger_deleteBreakpoint(DebuggerBreakpoint* bp) @@ -298,10 +290,12 @@ void debugger_toggleExecuteBreakpoint(uint32 address) { // delete existing breakpoint debugger_deleteBreakpoint(existingBP); - return; } - // create new - debugger_createExecuteBreakpoint(address); + else + { + // create new breakpoint + debugger_createExecuteBreakpoint(address); + } } void debugger_forceBreak() @@ -327,7 +321,7 @@ void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* b { if (bpItr == bp) { - if (bpItr->bpType == DEBUGGER_BP_T_NORMAL) + if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_LOGGING) { bp->enabled = state; debugger_updateExecutionBreakpoint(address); @@ -486,7 +480,7 @@ bool debugger_stepOver(PPCInterpreter_t* hCPU) return false; } // create one-shot breakpoint at next instruction - debugger_createSingleShotExecuteBreakpoint(initialIP +4); + debugger_createCodeBreakpoint(initialIP + 4, DEBUGGER_BP_T_ONE_SHOT); // step over current instruction (to avoid breakpoint) debugger_stepInto(hCPU); debuggerWindow_moveIP(); @@ -506,8 +500,39 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU) debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i]; } +void DebugLogStackTrace(OSThread_t* thread, MPTR sp); + void debugger_enterTW(PPCInterpreter_t* hCPU) { + // handle logging points + DebuggerBreakpoint* bp = debugger_getFirstBP(hCPU->instructionPointer); + bool shouldBreak = debuggerBPChain_hasType(bp, DEBUGGER_BP_T_NORMAL) || debuggerBPChain_hasType(bp, DEBUGGER_BP_T_ONE_SHOT); + while (bp) + { + if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled) + { + std::wstring logName = !bp->comment.empty() ? L"Breakpoint '"+bp->comment+L"'" : fmt::format(L"Breakpoint at 0x{:08X} (no comment)", bp->address); + std::wstring logContext = fmt::format(L"Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? L" Stack Trace:" : L""); + cemuLog_log(LogType::Force, L"[Debugger] {} was executed! {}", logName, logContext); + if (cemuLog_advancedPPCLoggingEnabled()) + DebugLogStackTrace(coreinitThread_getCurrentThreadDepr(hCPU), hCPU->gpr[1]); + break; + } + bp = bp->next; + } + + // return early if it's only a non-pausing logging breakpoint to prevent a modified debugger state and GUI updates + if (!shouldBreak) + { + uint32 backupIP = debuggerState.debugSession.instructionPointer; + debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; + debugger_stepInto(hCPU, false); + PPCInterpreterSlim_executeInstruction(hCPU); + debuggerState.debugSession.instructionPointer = backupIP; + return; + } + + // handle breakpoints debuggerState.debugSession.isTrapped = true; debuggerState.debugSession.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU); debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; @@ -579,6 +604,20 @@ void debugger_shouldBreak(PPCInterpreter_t* hCPU) void debugger_addParserSymbols(class ExpressionParser& ep) { + const auto module_count = RPLLoader_GetModuleCount(); + const auto module_list = RPLLoader_GetModuleList(); + + std::vector module_tmp(module_count); + for (int i = 0; i < module_count; i++) + { + const auto module = module_list[i]; + if (module) + { + module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR(); + ep.AddConstant(module->moduleName2, module_tmp[i]); + } + } + for (sint32 i = 0; i < 32; i++) ep.AddConstant(fmt::format("r{}", i), debuggerState.debugSession.ppcSnapshot.gpr[i]); } \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.h b/src/Cafe/HW/Espresso/Debugger/Debugger.h index 08cbd90a..717df28a 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.h +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.h @@ -7,6 +7,7 @@ #define DEBUGGER_BP_T_ONE_SHOT 1 // normal breakpoint, deletes itself after trigger (used for stepping) #define DEBUGGER_BP_T_MEMORY_READ 2 // memory breakpoint #define DEBUGGER_BP_T_MEMORY_WRITE 3 // memory breakpoint +#define DEBUGGER_BP_T_LOGGING 4 // logging breakpoint, prints the breakpoint comment and stack trace whenever hit #define DEBUGGER_BP_T_GDBSTUB 1 // breakpoint created by GDBStub #define DEBUGGER_BP_T_DEBUGGER 2 // breakpoint created by Cemu's debugger @@ -42,7 +43,7 @@ struct DebuggerBreakpoint bool isExecuteBP() const { - return bpType == DEBUGGER_BP_T_NORMAL || bpType == DEBUGGER_BP_T_ONE_SHOT; + return bpType == DEBUGGER_BP_T_NORMAL || bpType == DEBUGGER_BP_T_LOGGING || bpType == DEBUGGER_BP_T_ONE_SHOT; } bool isMemBP() const @@ -98,8 +99,9 @@ extern debuggerState_t debuggerState; // new API DebuggerBreakpoint* debugger_getFirstBP(uint32 address); -void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint +void debugger_createCodeBreakpoint(uint32 address, uint8 bpType); void debugger_createExecuteBreakpoint(uint32 address); +void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp); void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite); diff --git a/src/gui/debugger/BreakpointWindow.cpp b/src/gui/debugger/BreakpointWindow.cpp index ecb77428..63b92626 100644 --- a/src/gui/debugger/BreakpointWindow.cpp +++ b/src/gui/debugger/BreakpointWindow.cpp @@ -11,9 +11,11 @@ enum { - MENU_ID_CREATE_MEM_BP_READ = 1, + MENU_ID_CREATE_CODE_BP_EXECUTION = 1, + MENU_ID_CREATE_CODE_BP_LOGGING, + MENU_ID_CREATE_MEM_BP_READ, MENU_ID_CREATE_MEM_BP_WRITE, - + MENU_ID_DELETE_BP, }; enum ItemColumns @@ -118,6 +120,8 @@ void BreakpointWindow::OnUpdateView() const char* typeName = "UKN"; if (bp->bpType == DEBUGGER_BP_T_NORMAL) typeName = "X"; + else if (bp->bpType == DEBUGGER_BP_T_LOGGING) + typeName = "LOG"; else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT) typeName = "XS"; else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ) @@ -211,31 +215,56 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) void BreakpointWindow::OnRightDown(wxMouseEvent& event) { - wxMenu menu; + const auto position = event.GetPosition(); + const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2; + if (index < 0 || index >= m_breakpoints->GetItemCount()) + { + wxMenu menu; + menu.Append(MENU_ID_CREATE_CODE_BP_EXECUTION, _("Create execution breakpoint")); + menu.Append(MENU_ID_CREATE_CODE_BP_LOGGING, _("Create logging breakpoint")); + menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)")); + menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)")); - menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)")); - menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)")); + menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this); + PopupMenu(&menu); + } + else + { + m_breakpoints->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); + m_breakpoints->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); - menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this); - PopupMenu(&menu); + wxMenu menu; + menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint")); + + menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClickSelected), nullptr, this); + PopupMenu(&menu); + } +} + +void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt) +{ + if (evt.GetId() == MENU_ID_DELETE_BP) + { + long sel = m_breakpoints->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (sel != -1) + { + if (sel >= debuggerState.breakpoints.size()) + return; + + auto it = debuggerState.breakpoints.begin(); + std::advance(it, sel); + + debugger_deleteBreakpoint(*it); + + wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); + wxPostEvent(this->m_parent, evt); + } + } } void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt) { - switch (evt.GetId()) - { - case MENU_ID_CREATE_MEM_BP_READ: - MemoryBreakpointDialog(false); - return; - case MENU_ID_CREATE_MEM_BP_WRITE: - MemoryBreakpointDialog(true); - return; - } -} - -void BreakpointWindow::MemoryBreakpointDialog(bool isWrite) -{ - wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Memory breakpoint"), wxEmptyString); + wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Set breakpoint"), wxEmptyString); if (goto_dialog.ShowModal() == wxID_OK) { ExpressionParser parser; @@ -243,22 +272,34 @@ void BreakpointWindow::MemoryBreakpointDialog(bool isWrite) auto value = goto_dialog.GetValue().ToStdString(); std::transform(value.begin(), value.end(), value.begin(), tolower); - + uint32_t newBreakpointAddress = 0; try { debugger_addParserSymbols(parser); - const auto result = (uint32)parser.Evaluate(value); - debug_printf("goto eval result: %x\n", result); - - debugger_createMemoryBreakpoint(result, isWrite == false, isWrite == true); - this->OnUpdateView(); + newBreakpointAddress = parser.IsConstantExpression("0x"+value) ? (uint32)parser.Evaluate("0x"+value) : (uint32)parser.Evaluate(value); } - catch (const std::exception& e) + catch (const std::exception& ex) { - //ctx.errorHandler.printError(nullptr, -1, fmt::format("Unexpected error in expression \"{}\"", expressionString)); - //return EXPRESSION_RESOLVE_RESULT::EXPRESSION_ERROR; - wxMessageBox(e.what(), "Invalid expression"); + wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + return; } - + + switch (evt.GetId()) + { + case MENU_ID_CREATE_CODE_BP_EXECUTION: + debugger_createCodeBreakpoint(newBreakpointAddress, DEBUGGER_BP_T_NORMAL); + break; + case MENU_ID_CREATE_CODE_BP_LOGGING: + debugger_createCodeBreakpoint(newBreakpointAddress, DEBUGGER_BP_T_LOGGING); + break; + case MENU_ID_CREATE_MEM_BP_READ: + debugger_createMemoryBreakpoint(newBreakpointAddress, true, false); + break; + case MENU_ID_CREATE_MEM_BP_WRITE: + debugger_createMemoryBreakpoint(newBreakpointAddress, false, true); + break; + } + + this->OnUpdateView(); } -} \ No newline at end of file +} diff --git a/src/gui/debugger/BreakpointWindow.h b/src/gui/debugger/BreakpointWindow.h index 4851cb52..ac132950 100644 --- a/src/gui/debugger/BreakpointWindow.h +++ b/src/gui/debugger/BreakpointWindow.h @@ -19,8 +19,7 @@ private: void OnRightDown(wxMouseEvent& event); void OnContextMenuClick(wxCommandEvent& evt); - - void MemoryBreakpointDialog(bool isWrite); + void OnContextMenuClickSelected(wxCommandEvent& evt); wxCheckedListCtrl* m_breakpoints; }; \ No newline at end of file diff --git a/src/gui/debugger/DebuggerWindow2.cpp b/src/gui/debugger/DebuggerWindow2.cpp index f8c5c931..969e40bd 100644 --- a/src/gui/debugger/DebuggerWindow2.cpp +++ b/src/gui/debugger/DebuggerWindow2.cpp @@ -118,7 +118,7 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser) const auto comment = element.get("Comment", ""); // calculate absolute address - uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data); + uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL || type == DEBUGGER_BP_T_LOGGING) ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data; uint32 address = module_base_address + relative_address; // don't change anything if there's already a breakpoint @@ -127,7 +127,9 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser) // register breakpoints in debugger if (type == DEBUGGER_BP_T_NORMAL) - debugger_createExecuteBreakpoint(address); + debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); + else if (type == DEBUGGER_BP_T_LOGGING) + debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_LOGGING); else if (type == DEBUGGER_BP_T_MEMORY_READ) debugger_createMemoryBreakpoint(address, true, false); else if (type == DEBUGGER_BP_T_MEMORY_WRITE) @@ -173,7 +175,7 @@ void DebuggerModuleStorage::Save(XMLConfigParser& parser) // check whether the breakpoint is part of the current module being saved RPLModule* address_module; - if (bp->bpType == DEBUGGER_BP_T_NORMAL) address_module = RPLLoader_FindModuleByCodeAddr(bp->address); + if (bp->bpType == DEBUGGER_BP_T_NORMAL || bp->bpType == DEBUGGER_BP_T_LOGGING) address_module = RPLLoader_FindModuleByCodeAddr(bp->address); else if (bp->isMemBP()) address_module = RPLLoader_FindModuleByDataAddr(bp->address); else continue; @@ -259,7 +261,7 @@ void DebuggerWindow2::LoadModuleStorage(const RPLModule* module) bool already_loaded = std::any_of(m_modules_storage.begin(), m_modules_storage.end(), [path](const std::unique_ptr& debug) { return debug->GetFilename() == path; }); if (!path.empty() && !already_loaded) { - m_modules_storage.emplace_back(std::move(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false }))); + m_modules_storage.emplace_back(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false }))->Load(); } } @@ -522,6 +524,7 @@ void DebuggerWindow2::OnToolClicked(wxCommandEvent& event) void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event) { m_breakpoint_window->OnUpdateView(); + m_disasm_ctrl->RefreshControl(); UpdateModuleLabel(); } diff --git a/src/gui/debugger/DisasmCtrl.cpp b/src/gui/debugger/DisasmCtrl.cpp index dededf2c..21f6fc1d 100644 --- a/src/gui/debugger/DisasmCtrl.cpp +++ b/src/gui/debugger/DisasmCtrl.cpp @@ -147,7 +147,7 @@ void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR else if (is_active_bp) background_colour = wxColour(0xFF80A0FF); else if (bp != nullptr) - background_colour = wxColour(0xFF8080FF); + background_colour = wxColour(bp->bpType == DEBUGGER_BP_T_NORMAL ? 0xFF8080FF : 0x80FFFFFF); else if(virtualAddress == m_lastGotoTarget) background_colour = wxColour(0xFFE0E0E0); else @@ -540,8 +540,6 @@ void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position) { debugger_toggleExecuteBreakpoint(*optVirtualAddress); - RefreshControl(); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); wxPostEvent(this->m_parent, evt); } @@ -767,40 +765,31 @@ void DisasmCtrl::GoToAddressDialog() auto value = goto_dialog.GetValue().ToStdString(); std::transform(value.begin(), value.end(), value.begin(), tolower); - const auto module_count = RPLLoader_GetModuleCount(); - const auto module_list = RPLLoader_GetModuleList(); + debugger_addParserSymbols(parser); - std::vector module_tmp(module_count); - for (int i = 0; i < module_count; i++) + // try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234) + if (parser.IsConstantExpression("0x"+value)) { - const auto module = module_list[i]; - if (module) - { - module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR(); - parser.AddConstant(module->moduleName2, module_tmp[i]); - } - } - - double grp_tmp[32]; - PPCSnapshot& ppc_snapshot = debuggerState.debugSession.ppcSnapshot; - for (int i = 0; i < 32; i++) - { - char var_name[32]; - sprintf(var_name, "r%d", i); - grp_tmp[i] = ppc_snapshot.gpr[i]; - parser.AddConstant(var_name, grp_tmp[i]); - } - - try - { - const auto result = (uint32)parser.Evaluate(value); - debug_printf("goto eval result: %x\n", result); + const auto result = (uint32)parser.Evaluate("0x"+value); m_lastGotoTarget = result; CenterOffset(result); - debuggerWindow_updateViewThreadsafe2(); } - catch (const std::exception& ) + else if (parser.IsConstantExpression(value)) { + const auto result = (uint32)parser.Evaluate(value); + m_lastGotoTarget = result; + CenterOffset(result); + } + else + { + try + { + const auto _ = (uint32)parser.Evaluate(value); + } + catch (const std::exception& ex) + { + wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + } } } } \ No newline at end of file diff --git a/src/gui/debugger/DumpCtrl.cpp b/src/gui/debugger/DumpCtrl.cpp index cc1d5fee..16fdd87d 100644 --- a/src/gui/debugger/DumpCtrl.cpp +++ b/src/gui/debugger/DumpCtrl.cpp @@ -214,46 +214,36 @@ void DumpCtrl::GoToAddressDialog() wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString); if (goto_dialog.ShowModal() == wxID_OK) { - try + ExpressionParser parser; + + auto value = goto_dialog.GetValue().ToStdString(); + std::transform(value.begin(), value.end(), value.begin(), tolower); + + debugger_addParserSymbols(parser); + + // try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234) + if (parser.IsConstantExpression("0x"+value)) { - ExpressionParser parser; - - auto value = goto_dialog.GetValue().ToStdString(); - std::transform(value.begin(), value.end(), value.begin(), tolower); - //parser.SetExpr(value); - - const auto module_count = RPLLoader_GetModuleCount(); - const auto module_list = RPLLoader_GetModuleList(); - - std::vector module_tmp(module_count); - for (int i = 0; i < module_count; i++) - { - const auto module = module_list[i]; - if (module) - { - module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR(); - parser.AddConstant(module->moduleName2, module_tmp[i]); - } - } - - double grp_tmp[32]; - PPCSnapshot& ppc_snapshot = debuggerState.debugSession.ppcSnapshot; - for (int i = 0; i < 32; i++) - { - char var_name[32]; - sprintf(var_name, "r%d", i); - grp_tmp[i] = ppc_snapshot.gpr[i]; - parser.AddConstant(var_name, grp_tmp[i]); - } - - const auto result = (uint32)parser.Evaluate(value); - debug_printf("goto eval result: %x\n", result); + const auto result = (uint32)parser.Evaluate("0x"+value); m_lastGotoOffset = result; CenterOffset(result); } - catch (const std::exception& ex) + else if (parser.IsConstantExpression(value)) { - wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + const auto result = (uint32)parser.Evaluate(value); + m_lastGotoOffset = result; + CenterOffset(result); + } + else + { + try + { + const auto _ = (uint32)parser.Evaluate(value); + } + catch (const std::exception& ex) + { + wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + } } } } diff --git a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp index 1cbe1f8e..b93cf94e 100644 --- a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp +++ b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp @@ -280,9 +280,7 @@ void DebugLogStackTrace(OSThread_t* thread, MPTR sp); void DebugPPCThreadsWindow::DumpStackTrace(OSThread_t* thread) { - cemuLog_log(LogType::Force, fmt::format("Dumping stack trace for thread {0:08x} LR: {1:08x}", - memory_getVirtualOffsetFromPointer(thread), - _swapEndianU32(thread->context.lr))); + cemuLog_log(LogType::Force, "Dumping stack trace for thread {0:08x} LR: {1:08x}", memory_getVirtualOffsetFromPointer(thread), _swapEndianU32(thread->context.lr)); DebugLogStackTrace(thread, _swapEndianU32(thread->context.gpr[1])); } From 67819a68d927b22d573ec8e73cae75a70766f27f Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:07:13 +0200 Subject: [PATCH 004/101] nn_act: Handle incorrect slot 0 for PersistentId --- src/Cafe/IOSU/legacy/iosu_act.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Cafe/IOSU/legacy/iosu_act.cpp b/src/Cafe/IOSU/legacy/iosu_act.cpp index fa683c1e..919b7b0f 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.cpp +++ b/src/Cafe/IOSU/legacy/iosu_act.cpp @@ -623,10 +623,19 @@ int iosuAct_thread() } else if (actCemuRequest->requestCode == IOSU_ARC_PERSISTENTID) { - accountIndex = iosuAct_getAccountIndexBySlot(actCemuRequest->accountSlot); - _cancelIfAccountDoesNotExist(); - actCemuRequest->resultU32.u32 = _actAccountData[accountIndex].persistentId; - actCemuRequest->setACTReturnCode(0); + if(actCemuRequest->accountSlot != 0) + { + accountIndex = iosuAct_getAccountIndexBySlot(actCemuRequest->accountSlot); + _cancelIfAccountDoesNotExist(); + actCemuRequest->resultU32.u32 = _actAccountData[accountIndex].persistentId; + actCemuRequest->setACTReturnCode(0); + } + else + { + // F1 Race Stars calls IsSlotOccupied and indirectly GetPersistentId on slot 0 which is not valid + actCemuRequest->resultU32.u32 = 0; + actCemuRequest->setACTReturnCode(0); + } } else if (actCemuRequest->requestCode == IOSU_ARC_COUNTRY) { From 0d96255bae99d074cbe5722c813936b6ed8c3525 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 27 Jul 2023 21:04:42 +0200 Subject: [PATCH 005/101] nn_olv: More work on post API --- src/Cafe/CMakeLists.txt | 2 + src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp | 3 +- src/Cafe/OS/libs/nn_olv/nn_olv.cpp | 190 +--------------- src/Cafe/OS/libs/nn_olv/nn_olv.h | 1 + src/Cafe/OS/libs/nn_olv/nn_olv_Common.h | 35 +++ .../OS/libs/nn_olv/nn_olv_InitializeTypes.cpp | 10 +- src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp | 215 ++++++++++++++++++ src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.h | 16 ++ src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp | 194 ++++++++++++++-- src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h | 186 ++++++++++++++- src/Cafe/TitleList/TitleList.cpp | 2 +- src/Common/precompiled.h | 8 + 12 files changed, 655 insertions(+), 207 deletions(-) create mode 100644 src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp create mode 100644 src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.h diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 59b6aa42..b7656789 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -422,6 +422,8 @@ add_library(CemuCafe OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h OS/libs/nn_olv/nn_olv_PostTypes.cpp OS/libs/nn_olv/nn_olv_PostTypes.h + OS/libs/nn_olv/nn_olv_OfflineDB.cpp + OS/libs/nn_olv/nn_olv_OfflineDB.h OS/libs/nn_pdm/nn_pdm.cpp OS/libs/nn_pdm/nn_pdm.h OS/libs/nn_save/nn_save.cpp diff --git a/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp b/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp index a784e593..b348218f 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp @@ -546,6 +546,7 @@ void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU) { // get parental online control for online features // note: This option is account-bound, the p_acct1 prefix indicates that the account in slot 1 is used + // a non-zero value means network access is restricted through parental access. 0 means allowed // account in slot 1 if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL)) memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed @@ -561,7 +562,7 @@ void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU) { // miiverse restrictions if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL)) - memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed (0 -> no restrictions, 1 -> read only?, 2 -> no access?) + memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0); // data type is guessed (0 -> no restrictions, 1 -> read only, 2 -> no access) } else if (_strcmpi(ucParam->settingName, "s_acct01.uuid") == 0) { diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp index 50036249..25245b5c 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp @@ -5,6 +5,7 @@ #include "nn_olv_DownloadCommunityTypes.h" #include "nn_olv_UploadFavoriteTypes.h" #include "nn_olv_PostTypes.h" +#include "nn_olv_OfflineDB.h" #include "Cafe/OS/libs/proc_ui/proc_ui.h" #include "Cafe/OS/libs/coreinit/coreinit_Time.h" @@ -13,179 +14,6 @@ namespace nn { namespace olv { - struct DownloadedPostData_t - { - /* +0x0000 */ uint32be flags; - /* +0x0004 */ uint32be userPrincipalId; - /* +0x0008 */ char postId[0x20]; // size guessed - /* +0x0028 */ uint64 postDate; - /* +0x0030 */ uint8 feeling; - /* +0x0031 */ uint8 padding0031[3]; - /* +0x0034 */ uint32be regionId; - /* +0x0038 */ uint8 platformId; - /* +0x0039 */ uint8 languageId; - /* +0x003A */ uint8 countryId; - /* +0x003B */ uint8 padding003B[1]; - /* +0x003C */ uint16be bodyText[0x100]; // actual size is unknown - /* +0x023C */ uint32be bodyTextLength; - /* +0x0240 */ uint8 compressedMemoBody[0xA000]; // 40KB - /* +0xA240 */ uint32be compressedMemoBodyRelated; // size of compressed data? - /* +0xA244 */ uint16be topicTag[0x98]; - // app data - /* +0xA374 */ uint8 appData[0x400]; - /* +0xA774 */ uint32be appDataLength; - // external binary - /* +0xA778 */ uint8 externalBinaryUrl[0x100]; - /* +0xA878 */ uint32be externalBinaryDataSize; - // external image - /* +0xA87C */ uint8 externalImageDataUrl[0x100]; - /* +0xA97C */ uint32be externalImageDataSize; - // external url ? - /* +0xA980 */ char externalUrl[0x100]; - // mii - /* +0xAA80 */ uint8 miiData[0x60]; - /* +0xAAE0 */ uint16be miiNickname[0x20]; - /* +0xAB20 */ uint8 unusedAB20[0x14E0]; - - // everything above is part of DownloadedDataBase - // everything below is part of DownloadedPostData - /* +0xC000 */ uint8 uknDataC000[8]; // ?? - /* +0xC008 */ uint32be communityId; - /* +0xC00C */ uint32be empathyCount; - /* +0xC010 */ uint32be commentCount; - /* +0xC014 */ uint8 unused[0x1F4]; - }; // size: 0xC208 - - static_assert(sizeof(DownloadedPostData_t) == 0xC208, ""); - static_assert(offsetof(DownloadedPostData_t, postDate) == 0x0028, ""); - static_assert(offsetof(DownloadedPostData_t, platformId) == 0x0038, ""); - static_assert(offsetof(DownloadedPostData_t, bodyText) == 0x003C, ""); - static_assert(offsetof(DownloadedPostData_t, compressedMemoBody) == 0x0240, ""); - static_assert(offsetof(DownloadedPostData_t, topicTag) == 0xA244, ""); - static_assert(offsetof(DownloadedPostData_t, appData) == 0xA374, ""); - static_assert(offsetof(DownloadedPostData_t, externalBinaryUrl) == 0xA778, ""); - static_assert(offsetof(DownloadedPostData_t, externalImageDataUrl) == 0xA87C, ""); - static_assert(offsetof(DownloadedPostData_t, externalUrl) == 0xA980, ""); - static_assert(offsetof(DownloadedPostData_t, miiData) == 0xAA80, ""); - static_assert(offsetof(DownloadedPostData_t, miiNickname) == 0xAAE0, ""); - static_assert(offsetof(DownloadedPostData_t, unusedAB20) == 0xAB20, ""); - static_assert(offsetof(DownloadedPostData_t, communityId) == 0xC008, ""); - static_assert(offsetof(DownloadedPostData_t, empathyCount) == 0xC00C, ""); - static_assert(offsetof(DownloadedPostData_t, commentCount) == 0xC010, ""); - - const int POST_DATA_FLAG_HAS_BODY_TEXT = (0x0001); - const int POST_DATA_FLAG_HAS_BODY_MEMO = (0x0002); - - - void export_DownloadPostDataList(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(downloadedTopicData, void, 0); // DownloadedTopicData - ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 1); // DownloadedPostData - ppcDefineParamTypePtr(downloadedPostDataSize, uint32be, 2); - ppcDefineParamS32(maxCount, 3); - ppcDefineParamTypePtr(listParam, void, 4); // DownloadPostDataListParam - - maxCount = 0; // DISABLED - - // just some test - for (sint32 i = 0; i < maxCount; i++) - { - DownloadedPostData_t* postData = downloadedPostData + i; - memset(postData, 0, sizeof(DownloadedPostData_t)); - postData->userPrincipalId = 0x1000 + i; - // post id - sprintf(postData->postId, "postid-%04x", i+(GetTickCount()%10000)); - postData->bodyTextLength = 12; - postData->bodyText[0] = 'H'; - postData->bodyText[1] = 'e'; - postData->bodyText[2] = 'l'; - postData->bodyText[3] = 'l'; - postData->bodyText[4] = 'o'; - postData->bodyText[5] = ' '; - postData->bodyText[6] = 'w'; - postData->bodyText[7] = 'o'; - postData->bodyText[8] = 'r'; - postData->bodyText[9] = 'l'; - postData->bodyText[10] = 'd'; - postData->bodyText[11] = '!'; - - postData->miiNickname[0] = 'C'; - postData->miiNickname[1] = 'e'; - postData->miiNickname[2] = 'm'; - postData->miiNickname[3] = 'u'; - postData->miiNickname[4] = '-'; - postData->miiNickname[5] = 'M'; - postData->miiNickname[6] = 'i'; - postData->miiNickname[7] = 'i'; - - postData->topicTag[0] = 't'; - postData->topicTag[1] = 'o'; - postData->topicTag[2] = 'p'; - postData->topicTag[3] = 'i'; - postData->topicTag[4] = 'c'; - - postData->flags = POST_DATA_FLAG_HAS_BODY_TEXT; - } - *downloadedPostDataSize = maxCount; - - osLib_returnFromFunction(hCPU, 0); - } - - void exportDownloadPostData_TestFlags(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 0); - ppcDefineParamU32(testFlags, 1); - - if (((uint32)downloadedPostData->flags) & testFlags) - osLib_returnFromFunction(hCPU, 1); - else - osLib_returnFromFunction(hCPU, 0); - } - - void exportDownloadPostData_GetPostId(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 0); - osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(downloadedPostData->postId)); - } - - void exportDownloadPostData_GetMiiNickname(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 0); - if(downloadedPostData->miiNickname[0] == 0 ) - osLib_returnFromFunction(hCPU, MPTR_NULL); - else - osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(downloadedPostData->miiNickname)); - } - - void exportDownloadPostData_GetTopicTag(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 0); - osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(downloadedPostData->topicTag)); - } - - void exportDownloadPostData_GetBodyText(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 0); - ppcDefineParamWStrBE(strOut, 1); - ppcDefineParamS32(maxLength, 2); - - if (((uint32)downloadedPostData->flags&POST_DATA_FLAG_HAS_BODY_TEXT) == 0) - { - osLib_returnFromFunction(hCPU, 0xC1106800); - return; - } - - memset(strOut, 0, sizeof(uint16be)*maxLength); - sint32 copyLen = std::min(maxLength - 1, (sint32)downloadedPostData->bodyTextLength); - for (sint32 i = 0; i < copyLen; i++) - { - strOut[i] = downloadedPostData->bodyText[i]; - } - strOut[copyLen] = '\0'; - - osLib_returnFromFunction(hCPU, 0); - } - struct PortalAppParam_t { /* +0x1A663B */ char serviceToken[32]; // size is unknown @@ -284,6 +112,10 @@ namespace nn void load() { + g_ReportTypes = 0; + g_IsOnlineMode = false; + g_IsInitialized = false; + g_IsOfflineDBMode = false; loadOliveInitializeTypes(); loadOliveUploadCommunityTypes(); @@ -293,13 +125,6 @@ namespace nn cafeExportRegisterFunc(GetErrorCode, "nn_olv", "GetErrorCode__Q2_2nn3olvFRCQ2_2nn6Result", LogType::None); - osLib_addFunction("nn_olv", "DownloadPostDataList__Q2_2nn3olvFPQ3_2nn3olv19DownloadedTopicDataPQ3_2nn3olv18DownloadedPostDataPUiUiPCQ3_2nn3olv25DownloadPostDataListParam", export_DownloadPostDataList); -// osLib_addFunction("nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", exportDownloadPostData_TestFlags); -// osLib_addFunction("nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetPostId); -// osLib_addFunction("nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetMiiNickname); -// osLib_addFunction("nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetTopicTag); -// osLib_addFunction("nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", exportDownloadPostData_GetBodyText); - osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken); cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadPostDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv28UploadPostDataByPostAppParam", LogType::Force); @@ -314,5 +139,10 @@ namespace nn cafeExportRegisterFunc(UploadedPostData_GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv16UploadedPostDataCFv", LogType::Force); } + void unload() // not called yet + { + OfflineDB_Shutdown(); + } + } } \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv.h b/src/Cafe/OS/libs/nn_olv/nn_olv.h index c608e391..52474b49 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv.h @@ -19,5 +19,6 @@ namespace nn sint32 GetOlvAccessKey(uint32_t* pOutKey); void load(); + void unload(); } } \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_Common.h b/src/Cafe/OS/libs/nn_olv/nn_olv_Common.h index 718c10c3..c598e7ba 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_Common.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_Common.h @@ -69,6 +69,7 @@ namespace nn extern uint32_t g_ReportTypes; extern bool g_IsInitialized; extern bool g_IsOnlineMode; + extern bool g_IsOfflineDBMode; // use offline cache for posts static void InitializeOliveRequest(CurlRequestHelper& req) { @@ -175,5 +176,39 @@ namespace nn bool FormatCommunityCode(char* pOutCode, uint32* outLen, uint32 communityId); sint32 olv_curlformcode_to_error(CURLFORMcode code); + + // convert and copy utf8 string into UC2 big-endian array + template + uint32 SetStringUC2(uint16be(&str)[TLength], std::string_view sv, bool unescape = false) + { + if(unescape) + { + // todo + } + std::wstring ws = boost::nowide::widen(sv); + size_t copyLen = std::min(TLength-1, ws.size()); + for(size_t i=0; i + uint32 SetStringUC2(uint16be(&str)[TLength], const uint16be* strIn) + { + size_t copyLen = TLength-1; + for(size_t i=0; im_Flags & InitializeParam::FLAG_OFFLINE_MODE) == 0) { - g_IsOnlineMode = true; independentServiceToken_t token; diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp new file mode 100644 index 00000000..241630aa --- /dev/null +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp @@ -0,0 +1,215 @@ +#include "nn_olv_Common.h" +#include "nn_olv_PostTypes.h" +#include "nn_olv_OfflineDB.h" +#include "Cemu/ncrypto/ncrypto.h" // for base64 encoder/decoder +#include "util/helpers/helpers.h" +#include "Config/ActiveSettings.h" +#include "Cafe/CafeSystem.h" +#include +#include +#include + +namespace nn +{ + namespace olv + { + std::mutex g_offlineDBMutex; + bool g_offlineDBInitialized = false; + ZArchiveReader* g_offlineDBArchive{nullptr}; + + void OfflineDB_LazyInit() + { + std::scoped_lock _l(g_offlineDBMutex); + if(g_offlineDBInitialized) + return; + // open archive + g_offlineDBArchive = ZArchiveReader::OpenFromFile(ActiveSettings::GetUserDataPath("resources/miiverse/OfflineDB.zar")); + if(!g_offlineDBArchive) + cemuLog_log(LogType::Force, "Failed to open resources/miiverse/OfflineDB.zar. Miiverse posts will not be available"); + g_offlineDBInitialized = true; + } + + void OfflineDB_Shutdown() + { + std::scoped_lock _l(g_offlineDBMutex); + if(!g_offlineDBInitialized) + return; + delete g_offlineDBArchive; + g_offlineDBInitialized = false; + } + + bool CheckForOfflineDBFile(const char* filePath, uint32* fileSize) + { + if(!g_offlineDBArchive) + return false; + ZArchiveNodeHandle fileHandle = g_offlineDBArchive->LookUp(filePath); + if (!g_offlineDBArchive->IsFile(fileHandle)) + return false; + if(fileSize) + *fileSize = g_offlineDBArchive->GetFileSize(fileHandle); + return true; + } + + bool LoadOfflineDBFile(const char* filePath, std::vector& fileData) + { + fileData.clear(); + if(!g_offlineDBArchive) + return false; + ZArchiveNodeHandle fileHandle = g_offlineDBArchive->LookUp(filePath); + if (!g_offlineDBArchive->IsFile(fileHandle)) + return false; + fileData.resize(g_offlineDBArchive->GetFileSize(fileHandle)); + g_offlineDBArchive->ReadFromFile(fileHandle, 0, fileData.size(), fileData.data()); + return true; + } + + void TryLoadCompressedMemoImage(DownloadedPostData& downloadedPostData) + { + const unsigned char tgaHeader_320x120_32BPP[] = {0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x1,0x78,0x0,0x20,0x8}; + std::string memoImageFilename = fmt::format("memo/{}", (char*)downloadedPostData.downloadedDataBase.postId); + std::vector bitmaskCompressedImg; + if (!LoadOfflineDBFile(memoImageFilename.c_str(), bitmaskCompressedImg)) + return; + if (bitmaskCompressedImg.size() != (320*120)/8) + return; + std::vector decompressedImage; + decompressedImage.resize(sizeof(tgaHeader_320x120_32BPP) + 320 * 120 * 4); + memcpy(decompressedImage.data(), tgaHeader_320x120_32BPP, sizeof(tgaHeader_320x120_32BPP)); + uint8* pOut = decompressedImage.data() + sizeof(tgaHeader_320x120_32BPP); + for(int i=0; i<320*120; i++) + { + bool isWhite = (bitmaskCompressedImg[i/8] & (1 << (i%8))) != 0; + if(isWhite) + { + pOut[0] = pOut[1] = pOut[2] = pOut[3] = 0xFF; + } + else + { + pOut[0] = pOut[1] = pOut[2] = 0; + pOut[3] = 0xFF; + } + pOut += 4; + } + // store compressed image + uLongf compressedDestLen = 40960; + int r = compress((uint8*)downloadedPostData.downloadedDataBase.compressedMemoBody, &compressedDestLen, decompressedImage.data(), decompressedImage.size()); + if( r != Z_OK) + return; + downloadedPostData.downloadedDataBase.compressedMemoBodySize = compressedDestLen; + downloadedPostData.downloadedDataBase.SetFlag(DownloadedDataBase::FLAGS::HAS_BODY_MEMO); + } + + void CheckForExternalImage(DownloadedPostData& downloadedPostData) + { + std::string externalImageFilename = fmt::format("image/{}.jpg", (char*)downloadedPostData.downloadedDataBase.postId); + uint32 fileSize; + if (!CheckForOfflineDBFile(externalImageFilename.c_str(), &fileSize)) + return; + strcpy((char*)downloadedPostData.downloadedDataBase.externalImageDataUrl, externalImageFilename.c_str()); + downloadedPostData.downloadedDataBase.SetFlag(DownloadedDataBase::FLAGS::HAS_EXTERNAL_IMAGE); + downloadedPostData.downloadedDataBase.externalImageDataSize = fileSize; + } + + nnResult _Async_OfflineDB_DownloadPostDataListParam_DownloadPostDataList(coreinit::OSEvent* event, DownloadedTopicData* downloadedTopicData, DownloadedPostData* downloadedPostData, uint32be* postCountOut, uint32 maxCount, DownloadPostDataListParam* param) + { + scope_exit _se([&](){coreinit::OSSignalEvent(event);}); + + uint64 titleId = CafeSystem::GetForegroundTitleId(); + + memset(downloadedTopicData, 0, sizeof(DownloadedTopicData)); + memset(downloadedPostData, 0, sizeof(DownloadedPostData) * maxCount); + *postCountOut = 0; + + const char* postXmlFilename = nullptr; + if(titleId == 0x0005000010143400 || titleId == 0x0005000010143500 || titleId == 0x0005000010143600) + postXmlFilename = "PostList_WindWakerHD.xml"; + + if (!postXmlFilename) + return OLV_RESULT_SUCCESS; + + // load post XML + std::vector xmlData; + if (!LoadOfflineDBFile(postXmlFilename, xmlData)) + return OLV_RESULT_SUCCESS; + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_buffer(xmlData.data(), xmlData.size()); + if (!result) + return OLV_RESULT_SUCCESS; + // collect list of all post xml nodes + std::vector postXmlNodes; + for (pugi::xml_node postNode = doc.child("posts").child("post"); postNode; postNode = postNode.next_sibling("post")) + postXmlNodes.push_back(postNode); + + // randomly select up to maxCount posts + srand(GetTickCount()); + uint32 postCount = 0; + while(!postXmlNodes.empty() && postCount < maxCount) + { + uint32 index = rand() % postXmlNodes.size(); + pugi::xml_node& postNode = postXmlNodes[index]; + + auto& addedPost = downloadedPostData[postCount]; + memset(&addedPost, 0, sizeof(DownloadedPostData)); + if (!ParseXML_DownloadedPostData(addedPost, postNode) ) + continue; + TryLoadCompressedMemoImage(addedPost); + CheckForExternalImage(addedPost); + postCount++; + // remove from post list + postXmlNodes[index] = postXmlNodes.back(); + postXmlNodes.pop_back(); + } + *postCountOut = postCount; + return OLV_RESULT_SUCCESS; + } + + nnResult OfflineDB_DownloadPostDataListParam_DownloadPostDataList(DownloadedTopicData* downloadedTopicData, DownloadedPostData* downloadedPostData, uint32be* postCountOut, uint32 maxCount, DownloadPostDataListParam* param) + { + OfflineDB_LazyInit(); + + memset(downloadedTopicData, 0, sizeof(DownloadedTopicData)); + downloadedTopicData->communityId = param->communityId; + *postCountOut = 0; + + if(param->_HasFlag(DownloadPostDataListParam::FLAGS::SELF_ONLY)) + return OLV_RESULT_SUCCESS; // the offlineDB doesn't contain any self posts + + StackAllocator doneEvent; + coreinit::OSInitEvent(doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + auto asyncTask = std::async(std::launch::async, _Async_OfflineDB_DownloadPostDataListParam_DownloadPostDataList, doneEvent.GetPointer(), downloadedTopicData, downloadedPostData, postCountOut, maxCount, param); + coreinit::OSWaitEvent(doneEvent); + nnResult r = asyncTask.get(); + return r; + } + + nnResult _Async_OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(coreinit::OSEvent* event, DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize) + { + scope_exit _se([&](){coreinit::OSSignalEvent(event);}); + + if (!_this->TestFlags(_this, DownloadedDataBase::FLAGS::HAS_EXTERNAL_IMAGE)) + return OLV_RESULT_MISSING_DATA; + + // not all games may use JPEG files? + std::string externalImageFilename = fmt::format("image/{}.jpg", (char*)_this->postId); + std::vector jpegData; + if (!LoadOfflineDBFile(externalImageFilename.c_str(), jpegData)) + return OLV_RESULT_FAILED_REQUEST; + + memcpy(imageDataOut, jpegData.data(), jpegData.size()); + *imageSizeOut = jpegData.size(); + + return OLV_RESULT_SUCCESS; + } + + nnResult OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize) + { + StackAllocator doneEvent; + coreinit::OSInitEvent(doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + auto asyncTask = std::async(std::launch::async, _Async_OfflineDB_DownloadPostDataListParam_DownloadExternalImageData, doneEvent.GetPointer(), _this, imageDataOut, imageSizeOut, maxSize); + coreinit::OSWaitEvent(doneEvent); + nnResult r = asyncTask.get(); + return r; + } + + } +} \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.h b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.h new file mode 100644 index 00000000..ed790479 --- /dev/null +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.h @@ -0,0 +1,16 @@ +#pragma once +#include "Cafe/OS/libs/nn_common.h" +#include "nn_olv_Common.h" + +namespace nn +{ + namespace olv + { + void OfflineDB_Init(); + void OfflineDB_Shutdown(); + + nnResult OfflineDB_DownloadPostDataListParam_DownloadPostDataList(DownloadedTopicData* downloadedTopicData, DownloadedPostData* downloadedPostData, uint32be* postCountOut, uint32 maxCount, DownloadPostDataListParam* param); + nnResult OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize); + + } +} \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp index 5056f2fe..722e5584 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp @@ -1,5 +1,6 @@ #include "Cafe/OS/libs/nn_olv/nn_olv_Common.h" #include "nn_olv_PostTypes.h" +#include "nn_olv_OfflineDB.h" #include "Cemu/ncrypto/ncrypto.h" // for base64 decoder #include "util/helpers/helpers.h" #include @@ -9,41 +10,28 @@ namespace nn { namespace olv { - - template - uint32 SetStringUC2(uint16be(&str)[TLength], std::string_view sv, bool unescape = false) - { - if(unescape) - { - // todo - } - std::wstring ws = boost::nowide::widen(sv); - size_t copyLen = std::min(TLength-1, ws.size()); - for(size_t i=0; i 0) obj.SetFlag(DownloadedDataBase::FLAGS::HAS_BODY_TEXT); } + if(tokenNode = xmlNode.child("topic_tag"); tokenNode) + { + SetStringUC2(obj.topicTag, tokenNode.child_value(), true); + } if(tokenNode = xmlNode.child("feeling_id"); tokenNode) { obj.feeling = ConvertString(tokenNode.child_value()); if(obj.feeling < 0 || obj.feeling >= 5) { - cemuLog_log(LogType::Force, "DownloadedDataBase::ParseXml: feeling_id out of range"); + cemuLog_log(LogType::Force, "[Olive-XML] DownloadedDataBase::ParseXml: feeling_id out of range"); return false; } } @@ -52,7 +40,7 @@ namespace nn std::string_view id_sv = tokenNode.child_value(); if(id_sv.size() > 22) { - cemuLog_log(LogType::Force, "DownloadedDataBase::ParseXml: id too long"); + cemuLog_log(LogType::Force, "[Olive-XML] DownloadedDataBase::ParseXml: id too long"); return false; } memcpy(obj.postId, id_sv.data(), id_sv.size()); @@ -67,7 +55,7 @@ namespace nn obj.SetFlag(DownloadedDataBase::FLAGS::IS_NOT_AUTOPOST); else { - cemuLog_log(LogType::Force, "DownloadedDataBase::ParseXml: is_autopost has invalid value"); + cemuLog_log(LogType::Force, "[Olive-XML] DownloadedDataBase::ParseXml: is_autopost has invalid value"); return false; } } @@ -116,6 +104,36 @@ namespace nn { obj.countryId = ConvertString(tokenNode.child_value()); } + if(tokenNode = xmlNode.child("painting"); tokenNode) + { + if(pugi::xml_node subNode = tokenNode.child("content"); subNode) + { + std::vector paintingData = NCrypto::base64Decode(subNode.child_value()); + if (paintingData.size() > 0xA000) + { + cemuLog_log(LogType::Force, "[Olive-XML] DownloadedDataBase painting content is too large"); + return false; + } + memcpy(obj.compressedMemoBody, paintingData.data(), paintingData.size()); + obj.SetFlag(DownloadedDataBase::FLAGS::HAS_BODY_MEMO); + } + if(pugi::xml_node subNode = tokenNode.child("size"); subNode) + { + obj.compressedMemoBodySize = ConvertString(subNode.child_value()); + } + } + if(tokenNode = xmlNode.child("app_data"); tokenNode) + { + std::vector appData = NCrypto::base64Decode(tokenNode.child_value()); + if (appData.size() > 0x400) + { + cemuLog_log(LogType::Force, "[Olive-XML] DownloadedDataBase AppData is too large"); + return false; + } + memcpy(obj.appData, appData.data(), appData.size()); + obj.appDataLength = appData.size(); + obj.SetFlag(DownloadedDataBase::FLAGS::HAS_APP_DATA); + } return true; } @@ -256,6 +274,121 @@ namespace nn return 0; } + nnResult DownloadedDataBase::DownloadExternalImageData(DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize) + { + if(g_IsOfflineDBMode) + return OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(_this, imageDataOut, imageSizeOut, maxSize); + + if(!g_IsOnlineMode) + return OLV_RESULT_OFFLINE_MODE_REQUEST; + if (!TestFlags(_this, FLAGS::HAS_EXTERNAL_IMAGE)) + return OLV_RESULT_MISSING_DATA; + + cemuLog_logDebug(LogType::Force, "DownloadedDataBase::DownloadExternalImageData not implemented"); + return OLV_RESULT_FAILED_REQUEST; // placeholder error + } + + nnResult DownloadPostDataListParam::GetRawDataUrl(DownloadPostDataListParam* _this, char* urlOut, uint32 urlMaxSize) + { + if(!g_IsOnlineMode) + return OLV_RESULT_OFFLINE_MODE_REQUEST; + //if(_this->communityId == 0) + // cemuLog_log(LogType::Force, "DownloadPostDataListParam::GetRawDataUrl called with invalid communityId"); + + // get base url + std::string baseUrl; + baseUrl.append(g_DiscoveryResults.apiEndpoint); + //baseUrl.append(fmt::format("/v1/communities/{}/posts", (uint32)_this->communityId)); + cemu_assert_debug(_this->communityId == 0); + baseUrl.append(fmt::format("/v1/posts.search", (uint32)_this->communityId)); + + // "v1/posts.search" + + // build parameter string + std::string params; + + // this function behaves differently for the Wii U menu? Where it can lookup posts by titleId? + if(_this->titleId != 0) + { + cemu_assert_unimplemented(); // Wii U menu mode + } + + // todo: Generic parameters. Which includes: language_id, limit, type=text/memo + + // handle postIds + for(size_t i=0; i<_this->MAX_NUM_POST_ID; i++) + { + if(_this->searchPostId[i].str[0] == '\0') + continue; + cemu_assert_unimplemented(); // todo + // todo - postId parameter + // handle filters + if(_this->_HasFlag(DownloadPostDataListParam::FLAGS::WITH_MII)) + params.append("&with_mii=1"); + if(_this->_HasFlag(DownloadPostDataListParam::FLAGS::WITH_EMPATHY)) + params.append("&with_empathy_added=1"); + if(_this->bodyTextMaxLength != 0) + params.append(fmt::format("&max_body_length={}", _this->bodyTextMaxLength)); + } + + if(_this->titleId != 0) + params.append(fmt::format("&title_id={}", (uint64)_this->titleId)); + + if (_this->_HasFlag(DownloadPostDataListParam::FLAGS::FRIENDS_ONLY)) + params.append("&by=friend"); + if (_this->_HasFlag(DownloadPostDataListParam::FLAGS::FOLLOWERS_ONLY)) + params.append("&by=followings"); + if (_this->_HasFlag(DownloadPostDataListParam::FLAGS::SELF_ONLY)) + params.append("&by=self"); + + if(!params.empty()) + params[0] = '?'; // replace the leading ampersand + + baseUrl.append(params); + if(baseUrl.size()+1 > urlMaxSize) + return OLV_RESULT_NOT_ENOUGH_SIZE; + strncpy(urlOut, baseUrl.c_str(), urlMaxSize); + return OLV_RESULT_SUCCESS; + } + + nnResult DownloadPostDataList(DownloadedTopicData* downloadedTopicData, DownloadedPostData* downloadedPostData, uint32be* postCountOut, uint32 maxCount, DownloadPostDataListParam* param) + { + if(g_IsOfflineDBMode) + return OfflineDB_DownloadPostDataListParam_DownloadPostDataList(downloadedTopicData, downloadedPostData, postCountOut, maxCount, param); + memset(downloadedTopicData, 0, sizeof(DownloadedTopicData)); + downloadedTopicData->communityId = param->communityId; + *postCountOut = 0; + + char urlBuffer[2048]; + if (NN_RESULT_IS_FAILURE(DownloadPostDataListParam::GetRawDataUrl(param, urlBuffer, sizeof(urlBuffer)))) + return OLV_RESULT_INVALID_PARAMETER; + + /* + CurlRequestHelper req; + req.initate(urlBuffer, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE); + InitializeOliveRequest(req); + bool reqResult = req.submitRequest(); + if (!reqResult) + { + long httpCode = 0; + curl_easy_getinfo(req.getCURL(), CURLINFO_RESPONSE_CODE, &httpCode); + cemuLog_log(LogType::Force, "Failed request: {} ({})", urlBuffer, httpCode); + if (!(httpCode >= 400)) + return OLV_RESULT_FAILED_REQUEST; + } + pugi::xml_document doc; + if (!doc.load_buffer(req.getReceivedData().data(), req.getReceivedData().size())) + { + cemuLog_log(LogType::Force, fmt::format("Invalid XML in community download response")); + return OLV_RESULT_INVALID_XML; + } + */ + + *postCountOut = 0; + + return OLV_RESULT_SUCCESS; + } + void loadOlivePostAndTopicTypes() { cafeExportRegisterFunc(GetSystemTopicDataListFromRawData, "nn_olv", "GetSystemTopicDataListFromRawData__Q3_2nn3olv6hiddenFPQ4_2nn3olv6hidden29DownloadedSystemTopicDataListPQ4_2nn3olv6hidden24DownloadedSystemPostDataPUiUiPCUcT4", LogType::None); @@ -279,6 +412,8 @@ namespace nn cafeExportRegisterFunc(DownloadedDataBase::GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); cafeExportRegisterFunc(DownloadedDataBase::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); cafeExportRegisterFunc(DownloadedDataBase::GetMiiData2, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); + cafeExportRegisterFunc(DownloadedDataBase::DownloadExternalImageData, "nn_olv", "DownloadExternalImageData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi", LogType::None); + cafeExportRegisterFunc(DownloadedDataBase::GetExternalImageDataSize, "nn_olv", "GetExternalImageDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); // DownloadedPostData getters cafeExportRegisterFunc(DownloadedPostData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None); @@ -305,6 +440,23 @@ namespace nn cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicData, "nn_olv", "GetDownloadedSystemTopicData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::None); cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostData, "nn_olv", "GetDownloadedSystemPostData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFiT1", LogType::None); + // DownloadPostDataListParam constructor and getters + cafeExportRegisterFunc(DownloadPostDataListParam::Construct, "nn_olv", "__ct__Q3_2nn3olv25DownloadPostDataListParamFv", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetLanguageId, "nn_olv", "SetLanguageId__Q3_2nn3olv25DownloadPostDataListParamFUc", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchKey, "nn_olv", "SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCwUc", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchKeySingle, "nn_olv", "SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCw", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchPid, "nn_olv", "SetSearchPid__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetPostId, "nn_olv", "SetPostId__Q3_2nn3olv25DownloadPostDataListParamFPCcUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetPostDate, "nn_olv", "SetPostDate__Q3_2nn3olv25DownloadPostDataListParamFL", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetPostDataMaxNum, "nn_olv", "SetPostDataMaxNum__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::SetBodyTextMaxLength, "nn_olv", "SetBodyTextMaxLength__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); + + // URL and downloading functions + cafeExportRegisterFunc(DownloadPostDataListParam::GetRawDataUrl, "nn_olv", "GetRawDataUrl__Q3_2nn3olv25DownloadPostDataListParamCFPcUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataList, "nn_olv", "DownloadPostDataList__Q2_2nn3olvFPQ3_2nn3olv19DownloadedTopicDataPQ3_2nn3olv18DownloadedPostDataPUiUiPCQ3_2nn3olv25DownloadPostDataListParam", LogType::None); + } } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h index 3ca4f87e..e6078a7a 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h @@ -1,5 +1,6 @@ #pragma once #include +#include "nn_olv_Common.h" namespace nn { @@ -154,8 +155,11 @@ namespace nn return OLV_RESULT_INVALID_PTR; if (maxLength == 0) return OLV_RESULT_NOT_ENOUGH_SIZE; + if (!TestFlags(_this, FLAGS::HAS_BODY_TEXT)) + return OLV_RESULT_MISSING_DATA; + memset(bodyTextOut, 0, maxLength * sizeof(uint16)); uint32 outputLength = std::min(_this->bodyTextLength, maxLength); - olv_wstrncpy((char16_t*)bodyTextOut, (char16_t*)_this->bodyText, _this->bodyTextLength); + olv_wstrncpy((char16_t*)bodyTextOut, (char16_t*)_this->bodyText, outputLength); return OLV_RESULT_SUCCESS; } @@ -213,9 +217,19 @@ namespace nn return _this->postId; } - // todo: // DownloadExternalImageData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi + static nnResult DownloadExternalImageData(DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize); + // GetExternalImageDataSize__Q3_2nn3olv18DownloadedDataBaseCFv + static uint32 GetExternalImageDataSize(DownloadedDataBase* _this) + { + if (!TestFlags(_this, FLAGS::HAS_EXTERNAL_IMAGE)) + return 0; + return _this->externalImageDataSize; + } + + // todo: + // DownloadExternalImageData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi (implement downloading) // DownloadExternalBinaryData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi // GetExternalBinaryDataSize__Q3_2nn3olv18DownloadedDataBaseCFv }; @@ -425,6 +439,174 @@ namespace nn static_assert(sizeof(DownloadedSystemTopicDataList) == 0xC1000); } + + struct DownloadPostDataListParam + { + static constexpr size_t MAX_NUM_SEARCH_PID = 12; + static constexpr size_t MAX_NUM_SEARCH_KEY = 5; + static constexpr size_t MAX_NUM_POST_ID = 20; + + enum class FLAGS + { + FRIENDS_ONLY = 0x01, // friends only + FOLLOWERS_ONLY = 0x02, // followers only + SELF_ONLY = 0x04, // self only + ONLY_TYPE_TEXT = 0x08, + ONLY_TYPE_MEMO = 0x10, + UKN_20 = 0x20, + WITH_MII = 0x40, // with mii + WITH_EMPATHY = 0x80, // with yeahs added + UKN_100 = 0x100, + UKN_200 = 0x200, // "is_delay" parameter + UKN_400 = 0x400, // "is_hot" parameter + + + }; + + struct SearchKey + { + uint16be str[152]; + }; + + struct PostId + { + char str[32]; + }; + + betype flags; + uint32be communityId; + uint32be searchPid[MAX_NUM_SEARCH_PID]; + uint8 languageId; + uint8 hasLanguageId_039; + uint8 padding03A[2]; + uint32be postDataMaxNum; + SearchKey searchKeyArray[MAX_NUM_SEARCH_KEY]; + PostId searchPostId[MAX_NUM_POST_ID]; + uint64be postDate; // OSTime? + uint64be titleId; // only used by System posts? + uint32be bodyTextMaxLength; + uint8 padding8C4[1852]; + + bool _HasFlag(FLAGS flag) + { + return ((uint32)flags.value() & (uint32)flag) != 0; + } + + void _SetFlags(FLAGS flag) + { + flags = (FLAGS)((uint32)flags.value() | (uint32)flag); + } + + // constructor and getters + // __ct__Q3_2nn3olv25DownloadPostDataListParamFv + static DownloadPostDataListParam* Construct(DownloadPostDataListParam* _this) + { + memset(_this, 0, sizeof(DownloadPostDataListParam)); + return _this; + } + + // SetFlags__Q3_2nn3olv25DownloadPostDataListParamFUi + static nnResult SetFlags(DownloadPostDataListParam* _this, FLAGS flags) + { + // todo - verify flag combos + _this->flags = flags; + return OLV_RESULT_SUCCESS; + } + + // SetLanguageId__Q3_2nn3olv25DownloadPostDataListParamFUc + static nnResult SetLanguageId(DownloadPostDataListParam* _this, uint8 languageId) + { + _this->languageId = languageId; + _this->hasLanguageId_039 = 1; + return OLV_RESULT_SUCCESS; + } + + // SetCommunityId__Q3_2nn3olv25DownloadPostDataListParamFUi + static nnResult SetCommunityId(DownloadPostDataListParam* _this, uint32 communityId) + { + _this->communityId = communityId; + return OLV_RESULT_SUCCESS; + } + + // SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCwUc + static nnResult SetSearchKey(DownloadPostDataListParam* _this, const uint16be* searchKey, uint8 searchKeyIndex) + { + if (searchKeyIndex >= MAX_NUM_SEARCH_KEY) + return OLV_RESULT_INVALID_PARAMETER; + memset(&_this->searchKeyArray[searchKeyIndex], 0, sizeof(SearchKey)); + if(olv_wstrnlen((const char16_t*)searchKey, 152) > 50) + { + cemuLog_log(LogType::Force, "DownloadPostDataListParam::SetSearchKey: searchKey is too long\n"); + return OLV_RESULT_INVALID_PARAMETER; + } + SetStringUC2(_this->searchKeyArray[searchKeyIndex].str, searchKey); + return OLV_RESULT_SUCCESS; + } + + // SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCw + static nnResult SetSearchKeySingle(DownloadPostDataListParam* _this, const uint16be* searchKey) + { + return SetSearchKey(_this, searchKey, 0); + } + + // SetSearchPid__Q3_2nn3olv25DownloadPostDataListParamFUi + static nnResult SetSearchPid(DownloadPostDataListParam* _this, uint32 searchPid) + { + if(_this->_HasFlag(FLAGS::FRIENDS_ONLY) || _this->_HasFlag(FLAGS::FOLLOWERS_ONLY) || _this->_HasFlag(FLAGS::SELF_ONLY)) + return OLV_RESULT_INVALID_PARAMETER; + _this->searchPid[0] = searchPid; + return OLV_RESULT_SUCCESS; + } + + // SetPostId__Q3_2nn3olv25DownloadPostDataListParamFPCcUi + static nnResult SetPostId(DownloadPostDataListParam* _this, const char* postId, uint32 postIdIndex) + { + if (postIdIndex >= MAX_NUM_POST_ID) + return OLV_RESULT_INVALID_PARAMETER; + memset(&_this->searchPostId[postIdIndex], 0, sizeof(PostId)); + if (strlen(postId) > 22) + { + cemuLog_log(LogType::Force, "DownloadPostDataListParam::SetPostId: postId is too long\n"); + return OLV_RESULT_INVALID_PARAMETER; + } + strcpy(_this->searchPostId[postIdIndex].str, postId); + return OLV_RESULT_SUCCESS; + } + + // SetPostDate__Q3_2nn3olv25DownloadPostDataListParamFL + static nnResult SetPostDate(DownloadPostDataListParam* _this, uint64 postDate) + { + _this->postDate = postDate; + return OLV_RESULT_SUCCESS; + } + + // SetPostDataMaxNum__Q3_2nn3olv25DownloadPostDataListParamFUi + static nnResult SetPostDataMaxNum(DownloadPostDataListParam* _this, uint32 postDataMaxNum) + { + if(postDataMaxNum == 0) + return OLV_RESULT_INVALID_PARAMETER; + _this->postDataMaxNum = postDataMaxNum; + return OLV_RESULT_SUCCESS; + } + + // SetBodyTextMaxLength__Q3_2nn3olv25DownloadPostDataListParamFUi + static nnResult SetBodyTextMaxLength(DownloadPostDataListParam* _this, uint32 bodyTextMaxLength) + { + if(bodyTextMaxLength >= 256) + return OLV_RESULT_INVALID_PARAMETER; + _this->bodyTextMaxLength = bodyTextMaxLength; + return OLV_RESULT_SUCCESS; + } + + // GetRawDataUrl__Q3_2nn3olv25DownloadPostDataListParamCFPcUi + static nnResult GetRawDataUrl(DownloadPostDataListParam* _this, char* urlOut, uint32 urlMaxSize); + }; + + static_assert(sizeof(DownloadPostDataListParam) == 0x1000); + + // parsing functions + bool ParseXML_DownloadedPostData(DownloadedPostData& obj, pugi::xml_node& xmlNode); + void loadOlivePostAndTopicTypes(); } } \ No newline at end of file diff --git a/src/Cafe/TitleList/TitleList.cpp b/src/Cafe/TitleList/TitleList.cpp index 7f42d17c..2e50cbf9 100644 --- a/src/Cafe/TitleList/TitleList.cpp +++ b/src/Cafe/TitleList/TitleList.cpp @@ -9,7 +9,7 @@ bool sTLInitialized{ false }; fs::path sTLCacheFilePath; // lists for tracking known titles -// note: The list may only contain titles with valid meta data. Entries loaded from the cache may not have been parsed yet, but they will use a cached value for titleId and titleVersion +// note: The list may only contain titles with valid meta data (except for certain system titles). Entries loaded from the cache may not have been parsed yet, but they will use a cached value for titleId and titleVersion std::mutex sTLMutex; std::vector sTLList; std::vector sTLListPending; diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 56f31a03..7152f2c1 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -485,6 +485,14 @@ bool future_is_ready(std::future& f) #endif } +// replace with std::scope_exit once available +struct scope_exit +{ + std::function f_; + explicit scope_exit(std::function f) noexcept : f_(std::move(f)) {} + ~scope_exit() { if (f_) f_(); } +}; + // helper function to cast raw pointers to std::atomic // this is technically not legal but works on most platforms as long as alignment restrictions are met and the implementation of atomic doesnt come with additional members From 6268a24a4b66dcdee6f5a466240976557a0814dd Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 27 Jul 2023 21:22:53 +0200 Subject: [PATCH 006/101] Fix crash in title manager --- src/gui/components/wxTitleManagerList.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index 7ba8d037..257f84d2 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -1005,7 +1005,8 @@ void wxTitleManagerList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt wxTitleManagerList::TitleEntry entry(entryType, entryFormat, titleInfo.GetPath()); ParsedMetaXml* metaInfo = titleInfo.GetMetaInfo(); - + if(titleInfo.IsSystemDataTitle()) + return; // dont show system data titles for now entry.location_uid = titleInfo.GetUID(); entry.title_id = titleInfo.GetAppTitleId(); std::string name = metaInfo->GetLongName(GetConfig().console_language.GetValue()); From 0f469eb2b93147d981a61357e25763459122dcea Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 27 Jul 2023 21:45:00 +0200 Subject: [PATCH 007/101] Small cleanup + Fix memory base logged as 0 --- src/Cafe/CafeSystem.cpp | 74 ++++++++++++++++--- .../Latte/Renderer/OpenGL/OpenGLRenderer.cpp | 3 - .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 1 - src/gui/GameUpdateWindow.cpp | 14 +--- src/main.cpp | 65 ---------------- 5 files changed, 67 insertions(+), 90 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 6726a62c..ce46dc71 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -4,17 +4,16 @@ #include "Cafe/GameProfile/GameProfile.h" #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" +#include "Cafe/HW/Espresso/Debugger/Debugger.h" +#include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "audio/IAudioAPI.h" #include "audio/IAudioInputAPI.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" - #include "config/ActiveSettings.h" #include "Cafe/TitleList/GameInfo.h" -#include "util/helpers/SystemException.h" #include "Cafe/GraphicPack/GraphicPack2.h" - +#include "util/helpers/SystemException.h" +#include "Common/cpu_features.h" #include "input/InputManager.h" - #include "Cafe/CafeSystem.h" #include "Cafe/TitleList/TitleList.h" #include "Cafe/TitleList/GameInfo.h" @@ -22,14 +21,9 @@ #include "Cafe/OS/libs/snd_core/ax.h" #include "Cafe/OS/RPL/rpl.h" #include "Cafe/HW/Latte/Core/Latte.h" - #include "Cafe/Filesystem/FST/FST.h" - #include "Common/FileStream.h" - #include "GamePatch.h" - -#include #include "HW/Espresso/Debugger/GDBStub.h" #include "Cafe/IOSU/legacy/iosu_ioctl.h" @@ -70,6 +64,15 @@ // dependency to be removed #include "gui/guiWrapper.h" +#include + +#if BOOST_OS_LINUX +#include +#elif BOOST_OS_MACOS +#include +#include +#endif + std::string _pathToExecutable; std::string _pathToBaseExecutable; @@ -441,17 +444,66 @@ namespace CafeSystem GameInfo2 sGameInfo_ForegroundTitle; - // initialize all subsystems which are persistent and don't depend on a game running + + static void _CheckForWine() + { + #if BOOST_OS_WINDOWS + const HMODULE hmodule = GetModuleHandleA("ntdll.dll"); + if (!hmodule) + return; + + const auto pwine_get_version = (const char*(__cdecl*)())GetProcAddress(hmodule, "wine_get_version"); + if (pwine_get_version) + { + cemuLog_log(LogType::Force, "Wine version: {}", pwine_get_version()); + } + #endif + } + + void logCPUAndMemoryInfo() + { + std::string cpuName = g_CPUFeatures.GetCPUName(); + if (!cpuName.empty()) + cemuLog_log(LogType::Force, "CPU: {}", cpuName); + #if BOOST_OS_WINDOWS + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + uint32 memoryInMB = (uint32)(statex.ullTotalPhys / 1024LL / 1024LL); + cemuLog_log(LogType::Force, "RAM: {}MB", memoryInMB); + #elif BOOST_OS_LINUX + struct sysinfo info {}; + sysinfo(&info); + cemuLog_log(LogType::Force, "RAM: {}MB", ((static_cast(info.totalram) * info.mem_unit) / 1024LL / 1024LL)); + #elif BOOST_OS_MACOS + int64_t totalRam; + size_t size = sizeof(totalRam); + int result = sysctlbyname("hw.memsize", &totalRam, &size, NULL, 0); + if (result == 0) + cemuLog_log(LogType::Force, "RAM: {}MB", (totalRam / 1024LL / 1024LL)); + #endif + } + + // initialize all subsystems which are persistent and don't depend on a game running void Initialize() { if (s_initialized) return; s_initialized = true; // init core systems + cemuLog_log(LogType::Force, "------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING); fsc_init(); memory_init(); + cemuLog_log(LogType::Force, "Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base); PPCCore_init(); RPLLoader_InitState(); + cemuLog_log(LogType::Force, "mlc01 path: {}", _pathToUtf8(ActiveSettings::GetMlcPath())); + _CheckForWine(); + // CPU and RAM info + logCPUAndMemoryInfo(); + cemuLog_log(LogType::Force, "Used CPU extensions: {}", g_CPUFeatures.GetCommaSeparatedExtensionList()); + // misc systems + rplSymbolStorage_init(); // allocate memory for all SysAllocators // must happen before COS module init, but also before iosu::kernel::Initialize() SysAllocatorContainer::GetInstance().Initialize(); diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index 4dfdc52b..5269be64 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -363,9 +363,6 @@ void OpenGLRenderer::NotifyLatteCommandProcessorIdle() glFlush(); } - -bool IsRunningInWine(); - void OpenGLRenderer::GetVendorInformation() { // example vendor strings: diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index cfe7d3f4..937e3266 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -179,7 +179,6 @@ std::vector VulkanRenderer::GetDevices() } -bool IsRunningInWine(); void VulkanRenderer::DetermineVendor() { VkPhysicalDeviceProperties2 properties{}; diff --git a/src/gui/GameUpdateWindow.cpp b/src/gui/GameUpdateWindow.cpp index 40bf546e..e90c9dc7 100644 --- a/src/gui/GameUpdateWindow.cpp +++ b/src/gui/GameUpdateWindow.cpp @@ -34,8 +34,6 @@ std::string _GetTitleIdTypeStr(TitleId titleId) return "Unknown"; } -bool IsRunningInWine(); - bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) { m_title_info = TitleInfo(metaPath); @@ -130,15 +128,11 @@ bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) } } - // checking size is buggy on Wine (on Steam Deck this would return values too small to install bigger updates) - we therefore skip this step - if(!IsRunningInWine()) + const fs::space_info targetSpace = fs::space(ActiveSettings::GetMlcPath()); + if (targetSpace.free <= m_required_size) { - const fs::space_info targetSpace = fs::space(ActiveSettings::GetMlcPath()); - if (targetSpace.free <= m_required_size) - { - auto string = wxStringFormat(_("Not enough space available.\nRequired: {0} MB\nAvailable: {1} MB"), L"%lld %lld", (m_required_size / 1024 / 1024), (targetSpace.free / 1024 / 1024)); - throw std::runtime_error(string); - } + auto string = wxStringFormat(_("Not enough space available.\nRequired: {0} MB\nAvailable: {1} MB"), L"%lld %lld", (m_required_size / 1024 / 1024), (targetSpace.free / 1024 / 1024)); + throw std::runtime_error(string); } return true; diff --git a/src/main.cpp b/src/main.cpp index 032c23bc..f7a66bf9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,6 @@ #include "util/crypto/aes128.h" #include "gui/MainWindow.h" #include "Cafe/OS/RPL/rpl.h" -#include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "Cafe/OS/libs/gx2/GX2.h" #include "Cafe/OS/libs/coreinit/coreinit_Thread.h" #include "Cafe/HW/Latte/Core/LatteOverlay.h" @@ -60,66 +59,6 @@ std::atomic_bool g_isGPUInitFinished = false; std::wstring executablePath; -void logCPUAndMemoryInfo() -{ - std::string cpuName = g_CPUFeatures.GetCPUName(); - if (!cpuName.empty()) - cemuLog_log(LogType::Force, "CPU: {}", cpuName); - - #if BOOST_OS_WINDOWS - MEMORYSTATUSEX statex; - statex.dwLength = sizeof(statex); - GlobalMemoryStatusEx(&statex); - uint32 memoryInMB = (uint32)(statex.ullTotalPhys / 1024LL / 1024LL); - cemuLog_log(LogType::Force, "RAM: {}MB", memoryInMB); - #elif BOOST_OS_LINUX - struct sysinfo info {}; - sysinfo(&info); - cemuLog_log(LogType::Force, "RAM: {}MB", ((static_cast(info.totalram) * info.mem_unit) / 1024LL / 1024LL)); - #elif BOOST_OS_MACOS - int64_t totalRam; - size_t size = sizeof(totalRam); - int result = sysctlbyname("hw.memsize", &totalRam, &size, NULL, 0); - if (result == 0) - cemuLog_log(LogType::Force, "RAM: {}MB", (totalRam / 1024LL / 1024LL)); - #endif -} - -bool g_running_in_wine = false; -bool IsRunningInWine() -{ - return g_running_in_wine; -} - -void checkForWine() -{ - #if BOOST_OS_WINDOWS - const HMODULE hmodule = GetModuleHandleA("ntdll.dll"); - if (!hmodule) - return; - - const auto pwine_get_version = (const char*(__cdecl*)())GetProcAddress(hmodule, "wine_get_version"); - if (pwine_get_version) - { - g_running_in_wine = true; - cemuLog_log(LogType::Force, "Wine version: {}", pwine_get_version()); - } - #else - g_running_in_wine = false; - #endif -} - -void infoLog_cemuStartup() -{ - cemuLog_log(LogType::Force, "------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING); - cemuLog_log(LogType::Force, "Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base); - cemuLog_log(LogType::Force, "mlc01 path: {}", _pathToUtf8(ActiveSettings::GetMlcPath())); - checkForWine(); - // CPU and RAM info - logCPUAndMemoryInfo(); - cemuLog_log(LogType::Force, "Used CPU extensions: {}", g_CPUFeatures.GetCommaSeparatedExtensionList()); -} - // some implementations of _putenv dont copy the string and instead only store a pointer // thus we use a helper to keep a permanent copy std::vector sPutEnvMap; @@ -189,16 +128,12 @@ void CemuCommonInit() g_config.Load(); if (NetworkConfig::XMLExists()) n_config.Load(); - // symbol storage - rplSymbolStorage_init(); // parallelize expensive init code std::future futureInitAudioAPI = std::async(std::launch::async, []{ IAudioAPI::InitializeStatic(); IAudioInputAPI::InitializeStatic(); return 0; }); std::future futureInitGraphicPacks = std::async(std::launch::async, []{ GraphicPack2::LoadAll(); return 0; }); InputManager::instance().load(); futureInitAudioAPI.wait(); futureInitGraphicPacks.wait(); - // log Cemu startup info - infoLog_cemuStartup(); // init Cafe system CafeSystem::Initialize(); // init title list From 911573e0ddf368d839fee3f56bcb61a318574da3 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:00:01 +0200 Subject: [PATCH 008/101] TitleList: Use narrower filter for identifying data titles Previous code accidentally caught some game updates and dlc titles --- src/Cafe/TitleList/AppType.h | 23 +++++++++++++++++++++++ src/Cafe/TitleList/TitleInfo.h | 5 ++--- 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/Cafe/TitleList/AppType.h diff --git a/src/Cafe/TitleList/AppType.h b/src/Cafe/TitleList/AppType.h new file mode 100644 index 00000000..deca7560 --- /dev/null +++ b/src/Cafe/TitleList/AppType.h @@ -0,0 +1,23 @@ +#pragma once + +enum class APP_TYPE : uint32 +{ + GAME = 0x80000000, + GAME_UPDATE = 0x0800001B, + GAME_DLC = 0x0800000E, + // data titles + VERSION_DATA_TITLE = 0x10000015, + DRC_FIRMWARE = 0x10000013, + DRC_TEXTURE_ATLAS = 0x1000001A, +}; + +// allow direct comparison with uint32 +inline bool operator==(APP_TYPE lhs, uint32 rhs) +{ + return static_cast(lhs) == rhs; +} + +inline bool operator==(uint32 lhs, APP_TYPE rhs) +{ + return lhs == static_cast(rhs); +} diff --git a/src/Cafe/TitleList/TitleInfo.h b/src/Cafe/TitleList/TitleInfo.h index b8b781a4..9b8fa722 100644 --- a/src/Cafe/TitleList/TitleInfo.h +++ b/src/Cafe/TitleList/TitleInfo.h @@ -3,6 +3,7 @@ #include "Cafe/Filesystem/fsc.h" #include "config/CemuConfig.h" // for CafeConsoleRegion. Move to NCrypto? #include "TitleId.h" +#include "AppType.h" #include "ParsedMetaXml.h" enum class CafeTitleFileType @@ -122,9 +123,7 @@ public: if(!IsValid()) return false; uint32 appType = GetAppType(); - if(appType == 0) - return false; // not a valid app_type, but handle this in case some users use placeholder .xml data with fields zeroed-out - return ((appType>>24)&0x80) == 0; + return appType == APP_TYPE::DRC_FIRMWARE || appType == APP_TYPE::DRC_TEXTURE_ATLAS || appType == APP_TYPE::VERSION_DATA_TITLE; } // API which requires parsed meta data or cached info From a17111e6b0e4802044c90f4bedd66478de689070 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:53:46 +0200 Subject: [PATCH 009/101] TitleManager: Improvements for .wua conversion - Print more detailed paths in confirmation dialogue - Prefer the title right clicked by the user - When sourcing titles from other .wua files, use the correct subpath Fix include path --- src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp | 2 +- src/Cafe/TitleList/TitleInfo.h | 2 +- src/gui/components/wxTitleManagerList.cpp | 42 +++++++++++--------- src/gui/components/wxTitleManagerList.h | 2 +- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp index 241630aa..e6cea082 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp @@ -3,7 +3,7 @@ #include "nn_olv_OfflineDB.h" #include "Cemu/ncrypto/ncrypto.h" // for base64 encoder/decoder #include "util/helpers/helpers.h" -#include "Config/ActiveSettings.h" +#include "config/ActiveSettings.h" #include "Cafe/CafeSystem.h" #include #include diff --git a/src/Cafe/TitleList/TitleInfo.h b/src/Cafe/TitleList/TitleInfo.h index 9b8fa722..da430adc 100644 --- a/src/Cafe/TitleList/TitleInfo.h +++ b/src/Cafe/TitleList/TitleInfo.h @@ -148,7 +148,7 @@ public: return m_parsedMetaXml; } - std::string GetPrintPath() const; // formatted path for log writing + std::string GetPrintPath() const; // formatted path including type and WUA subpath. Intended for logging and user-facing information std::string GetInstallPath() const; // installation subpath, relative to storage base. E.g. "usr/title/.../..." or "sys/title/.../..." static std::string GetUniqueTempMountingPath(); diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index 257f84d2..6572a702 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -242,7 +242,7 @@ boost::optional wxTitleManagerList::GetTi return {}; } -void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId) +void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 rightClickedUID) { TitleInfo titleInfo_base; TitleInfo titleInfo_update; @@ -269,22 +269,26 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId) { if (!titleInfo_base.IsValid()) { - titleInfo_base = TitleInfo(data->entry.path); - } - else - { - // duplicate entry + titleInfo_base = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); + if(data->entry.location_uid == rightClickedUID) + break; // prefer the users selection } } if (hasUpdateTitleId && data->entry.title_id == updateTitleId) { if (!titleInfo_update.IsValid()) { - titleInfo_update = TitleInfo(data->entry.path); + titleInfo_update = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); + if(data->entry.location_uid == rightClickedUID) + break; } else { - // duplicate entry + // if multiple updates are present use the newest one + if (titleInfo_update.GetAppTitleVersion() < data->entry.version) + titleInfo_update = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); + if(data->entry.location_uid == rightClickedUID) + break; } } } @@ -293,7 +297,9 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId) { if (data->entry.title_id == aocTitleId) { - titleInfo_aoc = TitleInfo(data->entry.path); + titleInfo_aoc = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); + if(data->entry.location_uid == rightClickedUID) + break; } } @@ -301,23 +307,23 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId) msg.append("\n \n"); if (titleInfo_base.IsValid()) - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game: {}"))), _pathToUtf8(titleInfo_base.GetPath()))); + msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game:\n{}"))), titleInfo_base.GetPrintPath())); else - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game: Not installed"))))); + msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game:\nNot installed"))))); - msg.append("\n"); + msg.append("\n\n"); if (titleInfo_update.IsValid()) - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update: {}"))), _pathToUtf8(titleInfo_update.GetPath()))); + msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update:\n{}"))), titleInfo_update.GetPrintPath())); else - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update: Not installed"))))); + msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update:\nNot installed"))))); - msg.append("\n"); + msg.append("\n\n"); if (titleInfo_aoc.IsValid()) - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC: {}"))), _pathToUtf8(titleInfo_aoc.GetPath()))); + msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC:\n{}"))), titleInfo_aoc.GetPrintPath())); else - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC: Not installed"))))); + msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC:\nNot installed"))))); const int answer = wxMessageBox(wxString::FromUTF8(msg), _("Confirmation"), wxOK | wxCANCEL | wxCENTRE | wxICON_QUESTION, this); if (answer != wxOK) @@ -884,7 +890,7 @@ void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event) break; case kContextMenuConvertToWUA: - OnConvertToCompressedFormat(entry.value().title_id); + OnConvertToCompressedFormat(entry.value().title_id, entry.value().location_uid); break; } } diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/components/wxTitleManagerList.h index 706de2e7..547310c2 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/components/wxTitleManagerList.h @@ -108,7 +108,7 @@ private: [[nodiscard]] boost::optional GetTitleEntry(const fs::path& path); bool VerifyEntryFiles(TitleEntry& entry); - void OnConvertToCompressedFormat(uint64 titleId); + void OnConvertToCompressedFormat(uint64 titleId, uint64 rightClickedUID); bool DeleteEntry(long index, const TitleEntry& entry); void RemoveItem(long item); From 22bf6420d265399d8a31d518026e39328ab15812 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Tue, 8 Aug 2023 22:22:22 +0100 Subject: [PATCH 010/101] Log platform info (#931) --- src/Cafe/CafeSystem.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index ce46dc71..10b49c60 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -484,6 +484,28 @@ namespace CafeSystem #endif } + void logPlatformInfo() + { + const char* platform = NULL; + #if BOOST_OS_WINDOWS + platform = "Windows"; + #elif BOOST_OS_LINUX + if (getenv ("APPIMAGE")) + platform = "Linux (AppImage)"; + else if (getenv ("SNAP")) + platform = "Linux (Snap)"; + else if (platform = getenv ("container")) + if (strcmp (platform, "flatpak") == 0) + platform = "Linux (Flatpak)"; + else + platform = "Linux"; + #elif BOOST_OS_MACOS + platform = "MacOS"; + #endif + cemuLog_log(LogType::Force, "Platform: {}", platform); + + } + // initialize all subsystems which are persistent and don't depend on a game running void Initialize() { @@ -501,6 +523,7 @@ namespace CafeSystem _CheckForWine(); // CPU and RAM info logCPUAndMemoryInfo(); + logPlatformInfo(); cemuLog_log(LogType::Force, "Used CPU extensions: {}", g_CPUFeatures.GetCommaSeparatedExtensionList()); // misc systems rplSymbolStorage_init(); From 890df997cb57bdfd92d93e30ed046127d81583b8 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Tue, 8 Aug 2023 22:23:18 +0100 Subject: [PATCH 011/101] Simplify appstream summary description (#932) --- dist/linux/info.cemu.Cemu.metainfo.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dist/linux/info.cemu.Cemu.metainfo.xml b/dist/linux/info.cemu.Cemu.metainfo.xml index f056555b..ef59427f 100644 --- a/dist/linux/info.cemu.Cemu.metainfo.xml +++ b/dist/linux/info.cemu.Cemu.metainfo.xml @@ -3,15 +3,15 @@ info.cemu.Cemu Cemu - Software to emulate Wii U games and applications on PC - Software zum emulieren von Wii U Spielen und Anwendungen auf dem PC - Application pour émuler des jeux et applications Wii U sur PC - Applicatie om Wii U spellen en applicaties te emuleren op PC - Πρόγραμμα προσομοίωσης παιχνιδιών και εφαρμογών Wii U στον υπολογιστή - Software para emular juegos y aplicaciones de Wii U en PC - Software para emular jogos e aplicativos de Wii U no PC - Software per emulare giochi e applicazioni per Wii U su PC - Ojelmisto Wii U -pelien ja -sovellusten emulointiin PC:llä + Nintendo Wii U Emulator + Nintendo Wii U Emulator + Émulateur Nintendo Wii U + Nintendo Wii U Emulator + Εξομοιωτής Nintendo Wii U + Emulador de Nintendo Wii U + Emulador Nintendo Wii U + Emulatore Nintendo Wii U + Nintendo Wii U Emulaattori Cemu Project info.cemu.Cemu.desktop CC0-1.0 From 892ae13680a30d71b489ca754ddaa2b17828f85e Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 13 Aug 2023 14:48:54 +0200 Subject: [PATCH 012/101] Log Windows version + Fix logging crash on Linux --- src/Cafe/CafeSystem.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 10b49c60..8c2344ce 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -484,26 +484,53 @@ namespace CafeSystem #endif } + #if BOOST_OS_WINDOWS + std::string GetWindowsNamedVersion(uint32& buildNumber) + { + static char productName[256]; + HKEY hKey; + DWORD dwType = REG_SZ; + DWORD dwSize = sizeof(productName); + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) + { + if (RegQueryValueExA(hKey, "ProductName", NULL, &dwType, (LPBYTE)productName, &dwSize) != ERROR_SUCCESS) + strcpy(productName, "Windows"); + RegCloseKey(hKey); + } + OSVERSIONINFO osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + buildNumber = osvi.dwBuildNumber; + return std::string(productName); + } + #endif + void logPlatformInfo() { + std::string buffer; const char* platform = NULL; #if BOOST_OS_WINDOWS - platform = "Windows"; + uint32 buildNumber; + std::string windowsVersionName = GetWindowsNamedVersion(buildNumber); + buffer = fmt::format("{} (Build {})", windowsVersionName, buildNumber); + platform = buffer.c_str(); #elif BOOST_OS_LINUX if (getenv ("APPIMAGE")) platform = "Linux (AppImage)"; else if (getenv ("SNAP")) platform = "Linux (Snap)"; else if (platform = getenv ("container")) + { if (strcmp (platform, "flatpak") == 0) platform = "Linux (Flatpak)"; + } else platform = "Linux"; #elif BOOST_OS_MACOS platform = "MacOS"; #endif cemuLog_log(LogType::Force, "Platform: {}", platform); - } // initialize all subsystems which are persistent and don't depend on a game running From 85aa4f095b119e98620451a0c19c80f656d944a6 Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Tue, 15 Aug 2023 07:37:37 +0000 Subject: [PATCH 013/101] Linux/MacOS: Add wiimote support via HIDAPI (#934) --- CMakeLists.txt | 9 ++ src/input/CMakeLists.txt | 38 ++++-- src/input/InputManager.h | 3 + .../api/Wiimote/WiimoteControllerProvider.cpp | 128 +++++++----------- src/input/api/Wiimote/WiimoteMessages.h | 2 +- .../api/Wiimote/hidapi/HidapiWiimote.cpp | 55 ++++++++ src/input/api/Wiimote/hidapi/HidapiWiimote.h | 23 ++++ vcpkg.json | 4 + 8 files changed, 173 insertions(+), 89 deletions(-) create mode 100644 src/input/api/Wiimote/hidapi/HidapiWiimote.cpp create mode 100644 src/input/api/Wiimote/hidapi/HidapiWiimote.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 57615e86..34a28a06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,9 @@ if (WIN32) option(ENABLE_XINPUT "Enables the usage of XInput" ON) option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON) add_compile_definitions(HAS_DIRECTINPUT) + set(ENABLE_WIIMOTE ON) +elseif (UNIX) + option(ENABLE_HIDAPI "Build with HIDAPI" ON) endif() option(ENABLE_SDL "Enables the SDLController backend" ON) @@ -155,6 +158,12 @@ if (ENABLE_DISCORD_RPC) target_include_directories(discord-rpc INTERFACE ./dependencies/discord-rpc/include) endif() +if (ENABLE_HIDAPI) + find_package(hidapi REQUIRED) + set(ENABLE_WIIMOTE ON) + add_compile_definitions(HAS_HIDAPI) +endif () + if(UNIX AND NOT APPLE) if(ENABLE_FERAL_GAMEMODE) add_compile_definitions(ENABLE_FERAL_GAMEMODE) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index ecb88cd2..9f542371 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -44,18 +44,6 @@ add_library(CemuInput set_property(TARGET CemuInput PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") if(WIN32) - # Native wiimote (Win32 only for now) - target_sources(CemuInput PRIVATE - api/Wiimote/WiimoteControllerProvider.h - api/Wiimote/windows/WinWiimoteDevice.cpp - api/Wiimote/windows/WinWiimoteDevice.h - api/Wiimote/WiimoteControllerProvider.cpp - api/Wiimote/WiimoteMessages.h - api/Wiimote/NativeWiimoteController.h - api/Wiimote/NativeWiimoteController.cpp - api/Wiimote/WiimoteDevice.h - ) - # XInput target_sources(CemuInput PRIVATE api/XInput/XInputControllerProvider.cpp @@ -73,6 +61,29 @@ if(WIN32) ) endif() +if (ENABLE_WIIMOTE) + target_sources(CemuInput PRIVATE + api/Wiimote/WiimoteControllerProvider.h + api/Wiimote/WiimoteControllerProvider.cpp + api/Wiimote/WiimoteMessages.h + api/Wiimote/NativeWiimoteController.h + api/Wiimote/NativeWiimoteController.cpp + api/Wiimote/WiimoteDevice.h + ) + if (ENABLE_HIDAPI) + target_sources(CemuInput PRIVATE + api/Wiimote/hidapi/HidapiWiimote.cpp + api/Wiimote/hidapi/HidapiWiimote.h + ) + elseif (WIN32) + target_sources(CemuInput PRIVATE + api/Wiimote/windows/WinWiimoteDevice.cpp + api/Wiimote/windows/WinWiimoteDevice.h + ) + endif() +endif () + + target_include_directories(CemuInput PUBLIC "../") target_link_libraries(CemuInput PRIVATE @@ -87,6 +98,9 @@ target_link_libraries(CemuInput PRIVATE pugixml::pugixml SDL2::SDL2 ) +if (ENABLE_HIDAPI) + target_link_libraries(CemuInput PRIVATE hidapi::hidapi) +endif() if (ENABLE_WXWIDGETS) target_link_libraries(CemuInput PRIVATE wx::base wx::core) diff --git a/src/input/InputManager.h b/src/input/InputManager.h index 848c4810..345f7ba0 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -3,6 +3,9 @@ #if BOOST_OS_WINDOWS #include "input/api/DirectInput/DirectInputControllerProvider.h" #include "input/api/XInput/XInputControllerProvider.h" +#endif + +#if defined(HAS_HIDAPI) || BOOST_OS_WINDOWS #include "input/api/Wiimote/WiimoteControllerProvider.h" #endif diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.cpp b/src/input/api/Wiimote/WiimoteControllerProvider.cpp index a742a9d3..0ebf88aa 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.cpp +++ b/src/input/api/Wiimote/WiimoteControllerProvider.cpp @@ -2,7 +2,9 @@ #include "input/api/Wiimote/NativeWiimoteController.h" #include "input/api/Wiimote/WiimoteMessages.h" -#if BOOST_OS_WINDOWS +#ifdef HAS_HIDAPI +#include "input/api/Wiimote/hidapi/HidapiWiimote.h" +#elif BOOST_OS_WINDOWS #include "input/api/Wiimote/windows/WinWiimoteDevice.h" #endif @@ -36,7 +38,7 @@ std::vector> WiimoteControllerProvider::get_cont { // only add unknown, connected devices to our list const bool is_new_device = std::none_of(m_wiimotes.cbegin(), m_wiimotes.cend(), - [&device](const auto& it) { return *it.device == *device; }); + [device](const auto& it) { return *it.device == *device; }); if (is_new_device) { m_wiimotes.push_back(std::make_unique(device)); @@ -163,9 +165,7 @@ void WiimoteControllerProvider::reader_thread() { case kStatus: { -#ifdef WIIMOTE_DEBUG - printf("WiimoteControllerProvider::read_thread: kStatus\n"); -#endif + cemuLog_logDebug(LogType::Force,"WiimoteControllerProvider::read_thread: kStatus"); new_state.buttons = (*(uint16*)data) & (~0x60E0); data += 2; new_state.flags = *data; @@ -183,9 +183,7 @@ void WiimoteControllerProvider::reader_thread() if (HAS_FLAG(new_state.flags, kExtensionConnected)) { -#ifdef WIIMOTE_DEBUG - printf("\tExtension flag is set\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension flag is set"); if(new_state.m_extension.index() == 0) request_extension(index); } @@ -199,9 +197,7 @@ void WiimoteControllerProvider::reader_thread() break; case kRead: { -#ifdef WIIMOTE_DEBUG - printf("WiimoteControllerProvider::read_thread: kRead\n"); -#endif + cemuLog_logDebug(LogType::Force,"WiimoteControllerProvider::read_thread: kRead"); new_state.buttons = (*(uint16*)data) & (~0x60E0); data += 2; const uint8 error_flag = *data & 0xF, size = (*data >> 4) + 1; @@ -209,10 +205,9 @@ void WiimoteControllerProvider::reader_thread() if (error_flag) { + // 7 means that wiimote is already enabled or not available -#ifdef WIIMOTE_DEBUG - printf("Received error on data read 0x%x\n", error_flag); -#endif + cemuLog_logDebug(LogType::Force,"Received error on data read {:#x}", error_flag); continue; } @@ -220,9 +215,7 @@ void WiimoteControllerProvider::reader_thread() data += 2; if (address == (kRegisterCalibration & 0xFFFF)) { -#ifdef WIIMOTE_DEBUG - printf("Calibration received\n"); -#endif + cemuLog_logDebug(LogType::Force,"Calibration received"); cemu_assert(size == 8); @@ -255,17 +248,10 @@ void WiimoteControllerProvider::reader_thread() { if (size == 0xf) { -#ifdef WIIMOTE_DEBUG - printf("Extension type received but no extension connected\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension type received but no extension connected"); continue; } - -#ifdef WIIMOTE_DEBUG - printf("Extension type received\n"); -#endif - cemu_assert(size == 6); auto be_type = *(betype*)data; data += 6; // 48 @@ -274,42 +260,38 @@ void WiimoteControllerProvider::reader_thread() switch (be_type.value()) { case kExtensionNunchuck: -#ifdef WIIMOTE_DEBUG - printf("\tNunchuck\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension Type Received: Nunchuck"); new_state.m_extension = NunchuckData{}; break; case kExtensionClassic: -#ifdef WIIMOTE_DEBUG - printf("\tClassic\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension Type Received: Classic"); new_state.m_extension = ClassicData{}; break; case kExtensionClassicPro: - break; + cemuLog_logDebug(LogType::Force,"Extension Type Received: Classic Pro"); + break; case kExtensionGuitar: - break; + cemuLog_logDebug(LogType::Force,"Extension Type Received: Guitar"); + break; case kExtensionDrums: - break; + cemuLog_logDebug(LogType::Force,"Extension Type Received: Drums"); + break; case kExtensionBalanceBoard: - break; + cemuLog_logDebug(LogType::Force,"Extension Type Received: Balance Board"); + break; case kExtensionMotionPlus: - //m_motion_plus = true; -#ifdef WIIMOTE_DEBUG - printf("\tMotion plus detected\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension Type Received: MotionPlus"); set_motion_plus(index, true); new_state.m_motion_plus = MotionPlusData{}; break; case kExtensionPartialyInserted: -#ifdef WIIMOTE_DEBUG - printf("\tExtension only partially inserted!\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension only partially inserted"); new_state.m_extension = {}; request_status(index); break; default: - new_state.m_extension = {}; + cemuLog_logDebug(LogType::Force,"Unknown extension: {:#x}", be_type.value()); + new_state.m_extension = {}; break; } @@ -319,9 +301,7 @@ void WiimoteControllerProvider::reader_thread() else if (address == (kRegisterExtensionCalibration & 0xFFFF)) { cemu_assert(size == 0x10); -#ifdef WIIMOTE_DEBUG - printf("Extension calibration received\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension calibration received"); std::visit( overloaded { @@ -337,9 +317,7 @@ void WiimoteControllerProvider::reader_thread() std::array zero{}; if (memcmp(zero.data(), data, zero.size()) == 0) { -#ifdef WIIMOTE_DEBUG - printf("\tExtension calibration data is zero!\n"); -#endif + cemuLog_logDebug(LogType::Force,"Extension calibration data is zero"); return; } @@ -372,15 +350,23 @@ void WiimoteControllerProvider::reader_thread() } else { -#ifdef WIIMOTE_DEBUG - printf("Unhandled read data received\n"); -#endif - continue; + cemuLog_logDebug(LogType::Force,"Unhandled read data received"); + continue; } update_report = true; } break; + case kAcknowledge: + { + new_state.buttons = *(uint16*)data & (~0x60E0); + data += 2; + const auto report_id = *data++; + const auto error = *data++; + if (error) + cemuLog_logDebug(LogType::Force, "Error {:#x} from output report {:#x}", error, report_id); + break; + } case kDataCore: { // 30 BB BB @@ -476,10 +462,7 @@ void WiimoteControllerProvider::reader_thread() orientation /= tmp;*/ mp.orientation = orientation; -#ifdef WIIMOTE_DEBUG - printf("\tmp: %.2lf %.2lf %.2lf\n", mp.orientation.x, mp.orientation.y, - mp.orientation.z); -#endif + cemuLog_logDebug(LogType::Force,"MotionPlus: {:.2f}, {:.2f} {:.2f}", mp.orientation.x, mp.orientation.y, mp.orientation.z); }, [data](NunchuckData& nunchuck) mutable { @@ -553,12 +536,11 @@ void WiimoteControllerProvider::reader_thread() zero3, zero4 ); -#ifdef WIIMOTE_DEBUG - printf("\tn: %d,%d | %lf - %lf | %.2lf %.2lf %.2lf\n", nunchuck.z, nunchuck.c, - nunchuck.axis.x, nunchuck.axis.y, - RadToDeg(nunchuck.acceleration.x), RadToDeg(nunchuck.acceleration.y), - RadToDeg(nunchuck.acceleration.z)); -#endif + cemuLog_logDebug(LogType::Force,"Nunchuck: Z={}, C={} | {}, {} | {:.2f}, {:.2f}, {:.2f}", + nunchuck.z, nunchuck.c, + nunchuck.axis.x, nunchuck.axis.y, + RadToDeg(nunchuck.acceleration.x), RadToDeg(nunchuck.acceleration.y), + RadToDeg(nunchuck.acceleration.z)); }, [data](ClassicData& classic) mutable { @@ -592,11 +574,11 @@ void WiimoteControllerProvider::reader_thread() classic.trigger = classic.raw_trigger; classic.trigger /= 31.0f; -#ifdef WIIMOTE_DEBUG - printf("\tc: %d | %lf - %lf | %lf - %lf | %lf - %lf\n", classic.buttons, - classic.left_axis.x, classic.left_axis.y, classic.right_axis.x, - classic.right_axis.y, classic.trigger.x, classic.trigger.y); -#endif + cemuLog_logDebug(LogType::Force,"Classic Controller: Buttons={:b} | {}, {} | {}, {} | {}, {}", + classic.buttons, classic.left_axis.x, classic.left_axis.y, + classic.right_axis.x, classic.right_axis.y, classic.trigger.x, + classic.trigger.y); + } }, new_state.m_extension); @@ -609,9 +591,7 @@ void WiimoteControllerProvider::reader_thread() break; } default: -#ifdef WIIMOTE_DEBUG - printf("unhandled input packet id %d for wiimote\n", data[0]); -#endif + cemuLog_logDebug(LogType::Force,"unhandled input packet id {} for wiimote {}", id, index); } // update motion data @@ -694,7 +674,6 @@ void WiimoteControllerProvider::parse_acceleration(WiimoteState& wiimote_state, tmp -= calib.zero; acceleration = (wiimote_state.m_acceleration / tmp); - //printf("%d, %d, %d\n", (int)m_acceleration.x, (int)m_acceleration.y, (int)m_acceleration.z); const float pi_2 = (float)std::numbers::pi / 2.0f; wiimote_state.m_roll = std::atan2(acceleration.z, acceleration.x) - pi_2; } @@ -713,7 +692,6 @@ void WiimoteControllerProvider::rotate_ir(WiimoteState& wiimote_state) i++; if (!dot.visible) continue; - //printf("%d:\t%.02lf | %.02lf\n", i, dot.pos.x, dot.pos.y); // move to center, rotate and move back dot.pos -= 0.5f; dot.pos.x = (dot.pos.x * cos) + (dot.pos.y * (-sin)); @@ -973,9 +951,7 @@ void WiimoteControllerProvider::update_report_type(size_t index) else report_type = kDataCore; -#ifdef WIIMOTE_DEBUG - printf("Setting report type to %d\n", report_type); -#endif + cemuLog_logDebug(LogType::Force,"Setting report type to {}", report_type); send_packet(index, {kType, 0x04, report_type}); state.ir_camera.mode = set_ir_camera(index, true); diff --git a/src/input/api/Wiimote/WiimoteMessages.h b/src/input/api/Wiimote/WiimoteMessages.h index 712c3a5c..32dd4658 100644 --- a/src/input/api/Wiimote/WiimoteMessages.h +++ b/src/input/api/Wiimote/WiimoteMessages.h @@ -8,7 +8,7 @@ enum InputReportId : uint8 kStatus = 0x20, kRead = 0x21, - kWrite = 0x22, + kAcknowledge = 0x22, kDataCore = 0x30, kDataCoreAcc = 0x31, diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp new file mode 100644 index 00000000..7baad55d --- /dev/null +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -0,0 +1,55 @@ +#include "HidapiWiimote.h" + +static constexpr uint16 WIIMOTE_VENDOR_ID = 0x057e; +static constexpr uint16 WIIMOTE_PRODUCT_ID = 0x0306; +static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330; +static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22; + +HidapiWiimote::HidapiWiimote(hid_device* dev, uint64_t identifier) + : m_handle(dev), m_identifier(identifier) { + +} + +bool HidapiWiimote::write_data(const std::vector &data) { + return hid_write(m_handle, data.data(), data.size()) >= 0; +} + +std::optional> HidapiWiimote::read_data() { + std::array read_data{}; + const auto result = hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH); + if (result < 0) + return {}; + return {{read_data.cbegin(), read_data.cbegin() + result}}; +} + +std::vector HidapiWiimote::get_devices() { + std::vector wiimote_devices; + hid_init(); + const auto device_enumeration = hid_enumerate(WIIMOTE_VENDOR_ID, 0x0); + + for (auto it = device_enumeration; it != nullptr; it = it->next){ + if (it->product_id != WIIMOTE_PRODUCT_ID && it->product_id != WIIMOTE_MP_PRODUCT_ID) + continue; + auto dev = hid_open_path(it->path); + if (!dev){ + cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, boost::nowide::narrow(hid_error(nullptr))); + } + else { + // Enough to have a unique id for each device within a session + uint64_t id = (static_cast(it->interface_number) << 32) | + (static_cast(it->usage_page) << 16) | + (it->usage); + wiimote_devices.push_back(std::make_shared(dev, id)); + } + } + hid_free_enumeration(device_enumeration); + return wiimote_devices; +} + +bool HidapiWiimote::operator==(WiimoteDevice& o) const { + return m_identifier == static_cast(o).m_identifier; +} + +HidapiWiimote::~HidapiWiimote() { + hid_close(m_handle); +} diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.h b/src/input/api/Wiimote/hidapi/HidapiWiimote.h new file mode 100644 index 00000000..6bd90dac --- /dev/null +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class HidapiWiimote : public WiimoteDevice { +public: + HidapiWiimote(hid_device* dev, uint64_t identifier); + ~HidapiWiimote() override; + + bool write_data(const std::vector &data) override; + std::optional> read_data() override; + bool operator==(WiimoteDevice& o) const override; + + static std::vector get_devices(); + +private: + hid_device* m_handle; + uint64_t m_identifier; + +}; + +using WiimoteDevice_t = HidapiWiimote; \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index d0facf8b..940ed748 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -26,6 +26,10 @@ "boost-static-string", "boost-random", "fmt", + { + "name": "hidapi", + "platform": "!windows" + }, "libpng", "glm", { From d8b9a74d861dbf857d2debc00f9ba12ea890c6ad Mon Sep 17 00:00:00 2001 From: GaryOderNichts <12049776+GaryOderNichts@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:52:06 +0200 Subject: [PATCH 014/101] Latte: rendertarget is a bitmask (#942) --- src/Cafe/HW/Latte/Core/Latte.h | 4 ++++ src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index ed2116d0..f13abca0 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -84,6 +84,10 @@ extern uint8* gxRingBufferReadPtr; // currently active read pointer (gx2 ring bu void LatteTextureLoader_estimateAccessedDataRange(LatteTexture* texture, sint32 sliceIndex, sint32 mipIndex, uint32& addrStart, uint32& addrEnd); // render target + +#define RENDER_TARGET_TV (1 << 0) +#define RENDER_TARGET_DRC (1 << 2) + void LatteRenderTarget_updateScissorBox(); void LatteRenderTarget_trackUpdates(); diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index 1d9adfe3..3a52f641 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -1048,9 +1048,9 @@ void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uin } } - if (renderTarget == 4 && g_renderer->IsPadWindowActive()) + if ((renderTarget & RENDER_TARGET_DRC) && g_renderer->IsPadWindowActive()) LatteRenderTarget_copyToBackbuffer(texView, true); - if ((renderTarget == 1 && !showDRC) || (renderTarget == 4 && showDRC)) + if (((renderTarget & RENDER_TARGET_TV) && !showDRC) || ((renderTarget & RENDER_TARGET_DRC) && showDRC)) LatteRenderTarget_copyToBackbuffer(texView, false); } From 5e84862e287b0403bc5d9f853020738d858af22d Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Thu, 31 Aug 2023 01:29:12 +0000 Subject: [PATCH 015/101] [Linux/MacOS] Further Wiimote changes for parity with Windows (#945) --- src/gui/input/InputSettings2.cpp | 2 +- .../settings/WiimoteControllerSettings.cpp | 3 ++- .../input/settings/WiimoteControllerSettings.h | 2 +- src/input/CMakeLists.txt | 1 + src/input/InputManager.h | 2 +- src/input/api/Wiimote/hidapi/HidapiWiimote.cpp | 10 ++++++---- src/input/api/Wiimote/hidapi/HidapiWiimote.h | 5 +++-- src/input/emulated/EmulatedController.cpp | 17 ++++++++--------- 8 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp index bc3d33e0..e34c9241 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/input/InputSettings2.cpp @@ -976,7 +976,7 @@ void InputSettings2::on_controller_settings(wxCommandEvent& event) case InputAPI::Keyboard: break; - #if BOOST_OS_WINDOWS + #ifdef SUPPORTS_WIIMOTE case InputAPI::Wiimote: { const auto wiimote = std::dynamic_pointer_cast(controller); wxASSERT(wiimote); diff --git a/src/gui/input/settings/WiimoteControllerSettings.cpp b/src/gui/input/settings/WiimoteControllerSettings.cpp index a1ea4ecf..5bc20269 100644 --- a/src/gui/input/settings/WiimoteControllerSettings.cpp +++ b/src/gui/input/settings/WiimoteControllerSettings.cpp @@ -14,7 +14,7 @@ #include "gui/components/wxInputDraw.h" #include "gui/input/InputAPIAddWindow.h" -#if BOOST_OS_WINDOWS +#ifdef SUPPORTS_WIIMOTE WiimoteControllerSettings::WiimoteControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller) : wxDialog(parent, wxID_ANY, _("Controller settings"), position, wxDefaultSize, @@ -56,6 +56,7 @@ WiimoteControllerSettings::WiimoteControllerSettings(wxWindow* parent, const wxP // Motion m_use_motion = new wxCheckBox(box, wxID_ANY, _("Use motion")); m_use_motion->SetValue(m_settings.motion); + m_use_motion->SetValue(m_settings.motion); m_use_motion->Enable(m_controller->has_motion()); row_sizer->Add(m_use_motion, 0, wxALL, 5); diff --git a/src/gui/input/settings/WiimoteControllerSettings.h b/src/gui/input/settings/WiimoteControllerSettings.h index b519b9e5..d5214efe 100644 --- a/src/gui/input/settings/WiimoteControllerSettings.h +++ b/src/gui/input/settings/WiimoteControllerSettings.h @@ -1,6 +1,6 @@ #pragma once -#if BOOST_OS_WINDOWS +#ifdef SUPPORTS_WIIMOTE #include #include diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 9f542371..53b4dc3b 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -62,6 +62,7 @@ if(WIN32) endif() if (ENABLE_WIIMOTE) + target_compile_definitions(CemuInput PUBLIC SUPPORTS_WIIMOTE) target_sources(CemuInput PRIVATE api/Wiimote/WiimoteControllerProvider.h api/Wiimote/WiimoteControllerProvider.cpp diff --git a/src/input/InputManager.h b/src/input/InputManager.h index 345f7ba0..715d8f2e 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -5,7 +5,7 @@ #include "input/api/XInput/XInputControllerProvider.h" #endif -#if defined(HAS_HIDAPI) || BOOST_OS_WINDOWS +#ifdef SUPPORTS_WIIMOTE #include "input/api/Wiimote/WiimoteControllerProvider.h" #endif diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp index 7baad55d..898e6cf4 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -5,8 +5,8 @@ static constexpr uint16 WIIMOTE_PRODUCT_ID = 0x0306; static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330; static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22; -HidapiWiimote::HidapiWiimote(hid_device* dev, uint64_t identifier) - : m_handle(dev), m_identifier(identifier) { +HidapiWiimote::HidapiWiimote(hid_device* dev, uint64_t identifier, std::string_view path) + : m_handle(dev), m_identifier(identifier), m_path(path) { } @@ -35,11 +35,12 @@ std::vector HidapiWiimote::get_devices() { cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, boost::nowide::narrow(hid_error(nullptr))); } else { + hid_set_nonblocking(dev, true); // Enough to have a unique id for each device within a session uint64_t id = (static_cast(it->interface_number) << 32) | (static_cast(it->usage_page) << 16) | (it->usage); - wiimote_devices.push_back(std::make_shared(dev, id)); + wiimote_devices.push_back(std::make_shared(dev, id, it->path)); } } hid_free_enumeration(device_enumeration); @@ -47,7 +48,8 @@ std::vector HidapiWiimote::get_devices() { } bool HidapiWiimote::operator==(WiimoteDevice& o) const { - return m_identifier == static_cast(o).m_identifier; + auto const& other_mote = static_cast(o); + return m_identifier == other_mote.m_identifier && other_mote.m_path == m_path; } HidapiWiimote::~HidapiWiimote() { diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.h b/src/input/api/Wiimote/hidapi/HidapiWiimote.h index 6bd90dac..7b91dbbe 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.h +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.h @@ -5,7 +5,7 @@ class HidapiWiimote : public WiimoteDevice { public: - HidapiWiimote(hid_device* dev, uint64_t identifier); + HidapiWiimote(hid_device* dev, uint64_t identifier, std::string_view path); ~HidapiWiimote() override; bool write_data(const std::vector &data) override; @@ -16,7 +16,8 @@ public: private: hid_device* m_handle; - uint64_t m_identifier; + const uint64_t m_identifier; + const std::string m_path; }; diff --git a/src/input/emulated/EmulatedController.cpp b/src/input/emulated/EmulatedController.cpp index 8712bcf0..e254db34 100644 --- a/src/input/emulated/EmulatedController.cpp +++ b/src/input/emulated/EmulatedController.cpp @@ -2,7 +2,7 @@ #include "input/api/Controller.h" -#if BOOST_OS_WINDOWS +#ifdef SUPPORTS_WIIMOTE #include "input/api/Wiimote/NativeWiimoteController.h" #endif @@ -131,15 +131,15 @@ bool EmulatedController::has_second_motion() const if(controller->use_motion()) { // if wiimote has nunchuck connected, we use its acceleration - #if BOOST_OS_WINDOWS - if(controller->api() == InputAPI::Wiimote) + #if SUPPORTS_WIIMOTE + if(controller->api() == InputAPI::Wiimote) { if(((NativeWiimoteController*)controller.get())->get_extension() == NativeWiimoteController::Nunchuck) { return true; } } - #endif + #endif motion++; } } @@ -156,7 +156,7 @@ MotionSample EmulatedController::get_second_motion_data() const if (controller->use_motion()) { // if wiimote has nunchuck connected, we use its acceleration - #if BOOST_OS_WINDOWS + #ifdef SUPPORTS_WIIMOTE if (controller->api() == InputAPI::Wiimote) { if (((NativeWiimoteController*)controller.get())->get_extension() == NativeWiimoteController::Nunchuck) @@ -211,12 +211,11 @@ void EmulatedController::add_controller(std::shared_ptr controll { controller->connect(); - #if BOOST_OS_WINDOWS - if (const auto wiimote = std::dynamic_pointer_cast(controller)) { + #ifdef SUPPORTS_WIIMOTE + if (const auto wiimote = std::dynamic_pointer_cast(controller)) { wiimote->set_player_index(m_player_index); } - #endif - + #endif std::scoped_lock lock(m_mutex); m_controllers.emplace_back(std::move(controller)); } From 2abf1c2059a95cedc9f2156ca311ad0c5b68799b Mon Sep 17 00:00:00 2001 From: jn64 <23169302+jn64@users.noreply.github.com> Date: Sat, 2 Sep 2023 11:57:21 +0800 Subject: [PATCH 016/101] Disable auto-update on Linux/macOS (#955) It's not implemented yet --- src/gui/GeneralSettings2.cpp | 3 +++ src/gui/GettingStartedDialog.cpp | 3 +++ src/gui/MainWindow.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index ad767ad1..59f0e5ee 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -166,6 +166,9 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook) m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates")); m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup")); second_row->Add(m_auto_update, 0, botflag, 5); +#if BOOST_OS_LINUX || BOOST_OS_MACOS + m_auto_update->Disable(); +#endif second_row->AddSpacer(10); m_save_screenshot = new wxCheckBox(box, wxID_ANY, _("Save screenshot")); m_save_screenshot->SetToolTip(_("Pressing the screenshot key (F12) will save a screenshot directly to the screenshots folder")); diff --git a/src/gui/GettingStartedDialog.cpp b/src/gui/GettingStartedDialog.cpp index c84582b8..69f429b0 100644 --- a/src/gui/GettingStartedDialog.cpp +++ b/src/gui/GettingStartedDialog.cpp @@ -146,6 +146,9 @@ wxPanel* GettingStartedDialog::CreatePage2() m_update = new wxCheckBox(sizer->GetStaticBox(), wxID_ANY, _("Automatically check for updates")); option_sizer->Add(m_update, 0, wxALL, 5); +#if BOOST_OS_LINUX || BOOST_OS_MACOS + m_update->Disable(); +#endif sizer->Add(option_sizer, 1, wxEXPAND, 5); page2_sizer->Add(sizer, 0, wxALL | wxEXPAND, 5); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 95d9bcdb..74591c58 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -2254,6 +2254,9 @@ void MainWindow::RecreateMenu() //helpMenu->Append(MAINFRAME_MENU_ID_HELP_WEB, wxT("&Visit website")); //helpMenu->AppendSeparator(); m_check_update_menu = helpMenu->Append(MAINFRAME_MENU_ID_HELP_UPDATE, _("&Check for updates")); +#if BOOST_OS_LINUX || BOOST_OS_MACOS + m_check_update_menu->Enable(false); +#endif helpMenu->Append(MAINFRAME_MENU_ID_HELP_GETTING_STARTED, _("&Getting started")); helpMenu->AppendSeparator(); helpMenu->Append(MAINFRAME_MENU_ID_HELP_ABOUT, _("&About Cemu")); From d7f0d679047e9fe87de6b36cdd7f2be8b9800477 Mon Sep 17 00:00:00 2001 From: Gloria <32610623+yeah-its-gloria@users.noreply.github.com> Date: Wed, 6 Sep 2023 04:59:50 +0200 Subject: [PATCH 017/101] Add a pairing utility for Wiimotes to Cemu (#941) --- src/gui/CMakeLists.txt | 6 + src/gui/MainWindow.cpp | 1 + src/gui/PairingDialog.cpp | 236 +++++++++++++++++++++ src/gui/PairingDialog.h | 38 ++++ src/gui/input/panels/WiimoteInputPanel.cpp | 24 ++- src/gui/input/panels/WiimoteInputPanel.h | 1 + 6 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 src/gui/PairingDialog.cpp create mode 100644 src/gui/PairingDialog.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 90dc91c0..19ce95dc 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -97,6 +97,8 @@ add_library(CemuGui MemorySearcherTool.h PadViewFrame.cpp PadViewFrame.h + PairingDialog.cpp + PairingDialog.h TitleManager.cpp TitleManager.h windows/PPCThreadsViewer @@ -170,3 +172,7 @@ if (ENABLE_WXWIDGETS) # PUBLIC because wx/app.h is included in CemuApp.h target_link_libraries(CemuGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc) endif() + +if(WIN32) + target_link_libraries(CemuGui PRIVATE bthprops) +endif() diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 74591c58..6fa72801 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -2160,6 +2160,7 @@ void MainWindow::RecreateMenu() m_memorySearcherMenuItem->Enable(false); toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager")); toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, _("&Download Manager")); + m_menuBar->Append(toolsMenu, _("&Tools")); // cpu timer speed menu diff --git a/src/gui/PairingDialog.cpp b/src/gui/PairingDialog.cpp new file mode 100644 index 00000000..f90e6d13 --- /dev/null +++ b/src/gui/PairingDialog.cpp @@ -0,0 +1,236 @@ +#include "gui/wxgui.h" +#include "gui/PairingDialog.h" + +#if BOOST_OS_WINDOWS +#include +#endif + +wxDECLARE_EVENT(wxEVT_PROGRESS_PAIR, wxCommandEvent); +wxDEFINE_EVENT(wxEVT_PROGRESS_PAIR, wxCommandEvent); + +PairingDialog::PairingDialog(wxWindow* parent) + : wxDialog(parent, wxID_ANY, _("Pairing..."), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX) +{ + auto* sizer = new wxBoxSizer(wxVERTICAL); + m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(350, 20), wxGA_HORIZONTAL); + m_gauge->SetValue(0); + sizer->Add(m_gauge, 0, wxALL | wxEXPAND, 5); + + auto* rows = new wxFlexGridSizer(0, 2, 0, 0); + rows->AddGrowableCol(1); + + m_text = new wxStaticText(this, wxID_ANY, _("Searching for controllers...")); + rows->Add(m_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); + + { + auto* right_side = new wxBoxSizer(wxHORIZONTAL); + + m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); + m_cancelButton->Bind(wxEVT_BUTTON, &PairingDialog::OnCancelButton, this); + right_side->Add(m_cancelButton, 0, wxALL, 5); + + rows->Add(right_side, 1, wxALIGN_RIGHT, 5); + } + + sizer->Add(rows, 0, wxALL | wxEXPAND, 5); + + SetSizerAndFit(sizer); + Centre(wxBOTH); + + Bind(wxEVT_CLOSE_WINDOW, &PairingDialog::OnClose, this); + Bind(wxEVT_PROGRESS_PAIR, &PairingDialog::OnGaugeUpdate, this); + + m_thread = std::thread(&PairingDialog::WorkerThread, this); +} + +PairingDialog::~PairingDialog() +{ + Unbind(wxEVT_CLOSE_WINDOW, &PairingDialog::OnClose, this); +} + +void PairingDialog::OnClose(wxCloseEvent& event) +{ + event.Skip(); + + m_threadShouldQuit = true; + if (m_thread.joinable()) + m_thread.join(); +} + +void PairingDialog::OnCancelButton(const wxCommandEvent& event) +{ + Close(); +} + +void PairingDialog::OnGaugeUpdate(wxCommandEvent& event) +{ + PairingState state = (PairingState)event.GetInt(); + + switch (state) + { + case PairingState::Pairing: + { + m_text->SetLabel(_("Found controller. Pairing...")); + m_gauge->SetValue(50); + break; + } + + case PairingState::Finished: + { + m_text->SetLabel(_("Successfully paired the controller.")); + m_gauge->SetValue(100); + m_cancelButton->SetLabel(_("Close")); + break; + } + + case PairingState::NoBluetoothAvailable: + { + m_text->SetLabel(_("Failed to find a suitable Bluetooth radio.")); + m_gauge->SetValue(0); + m_cancelButton->SetLabel(_("Close")); + break; + } + + case PairingState::BluetoothFailed: + { + m_text->SetLabel(_("Failed to search for controllers.")); + m_gauge->SetValue(0); + m_cancelButton->SetLabel(_("Close")); + break; + } + + case PairingState::PairingFailed: + { + m_text->SetLabel(_("Failed to pair with the found controller.")); + m_gauge->SetValue(0); + m_cancelButton->SetLabel(_("Close")); + break; + } + + case PairingState::BluetoothUnusable: + { + m_text->SetLabel(_("Please use your system's Bluetooth manager instead.")); + m_gauge->SetValue(0); + m_cancelButton->SetLabel(_("Close")); + break; + } + + + default: + { + break; + } + } +} + +void PairingDialog::WorkerThread() +{ + const std::wstring wiimoteName = L"Nintendo RVL-CNT-01"; + const std::wstring wiiUProControllerName = L"Nintendo RVL-CNT-01-UC"; + +#if BOOST_OS_WINDOWS + const GUID bthHidGuid = {0x00001124,0x0000,0x1000,{0x80,0x00,0x00,0x80,0x5F,0x9B,0x34,0xFB}}; + + const BLUETOOTH_FIND_RADIO_PARAMS radioFindParams = + { + .dwSize = sizeof(BLUETOOTH_FIND_RADIO_PARAMS) + }; + + HANDLE radio = INVALID_HANDLE_VALUE; + HBLUETOOTH_RADIO_FIND radioFind = BluetoothFindFirstRadio(&radioFindParams, &radio); + if (radioFind == nullptr) + { + UpdateCallback(PairingState::NoBluetoothAvailable); + return; + } + + BluetoothFindRadioClose(radioFind); + + BLUETOOTH_RADIO_INFO radioInfo = + { + .dwSize = sizeof(BLUETOOTH_RADIO_INFO) + }; + + DWORD result = BluetoothGetRadioInfo(radio, &radioInfo); + if (result != ERROR_SUCCESS) + { + UpdateCallback(PairingState::NoBluetoothAvailable); + return; + } + + const BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = + { + .dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS), + + .fReturnAuthenticated = FALSE, + .fReturnRemembered = FALSE, + .fReturnUnknown = TRUE, + .fReturnConnected = FALSE, + + .fIssueInquiry = TRUE, + .cTimeoutMultiplier = 5, + + .hRadio = radio + }; + + BLUETOOTH_DEVICE_INFO info = + { + .dwSize = sizeof(BLUETOOTH_DEVICE_INFO) + }; + + while (!m_threadShouldQuit) + { + HBLUETOOTH_DEVICE_FIND deviceFind = BluetoothFindFirstDevice(&searchParams, &info); + if (deviceFind == nullptr) + { + UpdateCallback(PairingState::BluetoothFailed); + return; + } + + while (!m_threadShouldQuit) + { + if (info.szName == wiimoteName || info.szName == wiiUProControllerName) + { + BluetoothFindDeviceClose(deviceFind); + + UpdateCallback(PairingState::Pairing); + + wchar_t passwd[6] = { radioInfo.address.rgBytes[0], radioInfo.address.rgBytes[1], radioInfo.address.rgBytes[2], radioInfo.address.rgBytes[3], radioInfo.address.rgBytes[4], radioInfo.address.rgBytes[5] }; + DWORD bthResult = BluetoothAuthenticateDevice(nullptr, radio, &info, passwd, 6); + if (bthResult != ERROR_SUCCESS) + { + UpdateCallback(PairingState::PairingFailed); + return; + } + + bthResult = BluetoothSetServiceState(radio, &info, &bthHidGuid, BLUETOOTH_SERVICE_ENABLE); + if (bthResult != ERROR_SUCCESS) + { + UpdateCallback(PairingState::PairingFailed); + return; + } + + UpdateCallback(PairingState::Finished); + return; + } + + BOOL nextDevResult = BluetoothFindNextDevice(deviceFind, &info); + if (nextDevResult == FALSE) + { + break; + } + } + + BluetoothFindDeviceClose(deviceFind); + } +#else + UpdateCallback(PairingState::BluetoothUnusable); +#endif +} + +void PairingDialog::UpdateCallback(PairingState state) +{ + auto* event = new wxCommandEvent(wxEVT_PROGRESS_PAIR); + event->SetInt((int)state); + wxQueueEvent(this, event); +} \ No newline at end of file diff --git a/src/gui/PairingDialog.h b/src/gui/PairingDialog.h new file mode 100644 index 00000000..6c7612d1 --- /dev/null +++ b/src/gui/PairingDialog.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include + +class PairingDialog : public wxDialog +{ +public: + PairingDialog(wxWindow* parent); + ~PairingDialog(); + +private: + enum class PairingState + { + Pairing, + Finished, + NoBluetoothAvailable, + BluetoothFailed, + PairingFailed, + BluetoothUnusable + }; + + void OnClose(wxCloseEvent& event); + void OnCancelButton(const wxCommandEvent& event); + void OnGaugeUpdate(wxCommandEvent& event); + + void WorkerThread(); + void UpdateCallback(PairingState state); + + wxStaticText* m_text; + wxGauge* m_gauge; + wxButton* m_cancelButton; + + std::thread m_thread; + bool m_threadShouldQuit = false; +}; diff --git a/src/gui/input/panels/WiimoteInputPanel.cpp b/src/gui/input/panels/WiimoteInputPanel.cpp index fbc4e325..050baad1 100644 --- a/src/gui/input/panels/WiimoteInputPanel.cpp +++ b/src/gui/input/panels/WiimoteInputPanel.cpp @@ -1,5 +1,6 @@ #include "gui/input/panels/WiimoteInputPanel.h" +#include #include #include #include @@ -11,6 +12,7 @@ #include "input/emulated/WiimoteController.h" #include "gui/helpers/wxHelpers.h" #include "gui/components/wxInputDraw.h" +#include "gui/PairingDialog.h" constexpr WiimoteController::ButtonId g_kFirstColumnItems[] = { @@ -36,10 +38,18 @@ WiimoteInputPanel::WiimoteInputPanel(wxWindow* parent) bold_font.MakeBold(); auto* main_sizer = new wxBoxSizer(wxVERTICAL); + auto* horiz_main_sizer = new wxBoxSizer(wxHORIZONTAL); - auto* extensions_sizer = new wxBoxSizer(wxHORIZONTAL); - extensions_sizer->Add(new wxStaticText(this, wxID_ANY, _("Extensions:"))); - extensions_sizer->AddSpacer(10); + auto* pair_button = new wxButton(this, wxID_ANY, _("Pair a Wii or Wii U controller")); + pair_button->Bind(wxEVT_BUTTON, &WiimoteInputPanel::on_pair_button, this); + horiz_main_sizer->Add(pair_button); + horiz_main_sizer->AddSpacer(10); + + auto* extensions_sizer = new wxBoxSizer(wxHORIZONTAL); + horiz_main_sizer->Add(extensions_sizer, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL)); + + extensions_sizer->Add(new wxStaticText(this, wxID_ANY, _("Extensions:"))); + extensions_sizer->AddSpacer(10); m_motion_plus = new wxCheckBox(this, wxID_ANY, _("MotionPlus")); m_motion_plus->Bind(wxEVT_CHECKBOX, &WiimoteInputPanel::on_extension_change, this); @@ -54,7 +64,7 @@ WiimoteInputPanel::WiimoteInputPanel(wxWindow* parent) m_classic->Hide(); extensions_sizer->Add(m_classic); - main_sizer->Add(extensions_sizer, 0, wxEXPAND | wxALL, 5); + main_sizer->Add(horiz_main_sizer, 0, wxEXPAND | wxALL, 5); main_sizer->Add(new wxStaticLine(this), 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 5); m_item_sizer = new wxGridBagSizer(); @@ -254,3 +264,9 @@ void WiimoteInputPanel::load_controller(const EmulatedControllerPtr& emulated_co set_active_device_type(wiimote->get_device_type()); } } + +void WiimoteInputPanel::on_pair_button(wxCommandEvent& event) +{ + PairingDialog pairing_dialog(this); + pairing_dialog.ShowModal(); +} diff --git a/src/gui/input/panels/WiimoteInputPanel.h b/src/gui/input/panels/WiimoteInputPanel.h index a7aed99b..0810fbc3 100644 --- a/src/gui/input/panels/WiimoteInputPanel.h +++ b/src/gui/input/panels/WiimoteInputPanel.h @@ -25,6 +25,7 @@ private: void on_volume_change(wxCommandEvent& event); void on_extension_change(wxCommandEvent& event); + void on_pair_button(wxCommandEvent& event); wxGridBagSizer* m_item_sizer; From 4d1864c8a110dba382805ae2f26cec4da9984719 Mon Sep 17 00:00:00 2001 From: Cemu-Language CI Date: Thu, 7 Sep 2023 23:35:58 +0000 Subject: [PATCH 018/101] Update translation files --- bin/resources/de/cemu.mo | Bin 14625 -> 27890 bytes bin/resources/ko/cemu.mo | Bin 15670 -> 62866 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/resources/de/cemu.mo b/bin/resources/de/cemu.mo index ee2bb29503f1ffa67c1640100e9be11e723d4a3c..70dbb2cb3863647139b90f81460f29f7a247566f 100644 GIT binary patch literal 27890 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 PWdTD*esTr_C>j_5`d1C6 diff --git a/bin/resources/ko/cemu.mo b/bin/resources/ko/cemu.mo index edaec86320836d12b27ba7c7f4d51b8057265ead..c6ae3216759029622f959cba118805e537823801 100644 GIT binary patch literal 62866 zcmca7#4?qEfq~%(69a<`0|SGNI0M6dW(I~BJ&-5^!wp*o1{MYehMTqw47>~s4EJmq z7M7~a@2FmN+4F#NP-VBln6U|_X_(ELzZ5=txELDcEmF)&CoFfdr!F);8mFffGL zF);8kFfgRrF)(m3Fff!r#p~@L=J(q{?44uBz#zoHz_8Gcfq{d8fnkds1A`y~1H)cB z1_o9J28Q!?3=E=_tT z7#JAR?IHH}*fTJ&F)%R9wuiW5kv#(gI|Bp5T6>6pw%SAN+iwrC?;@0c9jfo8J;Z$< z>=_t@85kIt9U$h2J3!p4)5X!f9fY{^c00|#YsQOR`NchA#KApTzF0P)8jsQIU%=3j-n<2ICj1~vDi0|SF90|Nu2BgB2`ju3YlI6~ZG z;6T}}4P7r@|IzjB4 z?8Lwz&cML1*olEbjDdk+H&opvCx|<4L+$$j75@V@kJTAs9*;A`eo<$LISS4Y|5!Lf z?D2Aj_&>xM68?$K5cd=~L+mSus_%1VV322EU|8S`@z(*U_+@8^JDxg2!u1zaov;hU zUSk)CKkZx~>Br55fkBpmfx!nVpXCCvx6}m^el0GLc$neBz!1p5z_8MVfq|KUf#J6c zBs~5@_3^tx%$0J5=u>iKV31>AU@&uq=nr*eV6b9fU`TUiV5kM2m=E{h8x76d)*-RTySGxXlGzxxZwsdH^Cj^p4sjYb*tSO7@Qaw z81}g{Ft~uyr3V9pBLf42n+F4f4Fdy1hX*8_j(RXKC^9fG-12~g#~%+!JTQ4e(jAW{ z#5_SyNW7?cLc-n9lYzmMfq@|qs;=D=qOZpj67JJHA^utK332aUPe}Y9h0^CdA>ni# zO23Ax|L+M&ue@Fm`y{;}@uubl$v1{x5P5emNV*92f`nI;7sQ=$UJ!Q_dO^zFYAAn( z7bHFwc|pScpcf=wZhJw})f+EJdSv#7m@DlKF;B@GqR!MCl1|*b85qnM7#LE#A>lXM zn}H#nfq`LzHv@wo0|SGE4+BFMs2ucRV6bFhV7TN1aW9WA14AfC-WTG&JYNQenG6gJ z6~2&g5%Po3(tZ$ks`x>|$Jq}OFX4WWaEyi0sZjA;C|&FasRt^d@>8Ma&Gmz%i)DV0 z@Z1KKKMXbhv>yY5Jp%*7eW{UP=nL1{OCNPI^5GcZgCrAL1T1_Mx47Qn#Z z!@$5`7{I{b&cMJ>7XXR>V*!xxyB`1vr%wS43|b5f4D5kWa|0O|>_F);kbxn9fq|hl z5E8!E0wLwWmq3Vr*@7VM;R}Mqi)0Wa{V4=7FeorEFgOH3>`4q_U|0*P?}8xiR0?Kb zux4OjFbIa|%L#^f_y6Fyv=NW>M(#K9HeF3WOIn>k|S=PoW_U42GccAOzyh86gY|X$%Yu zS3(#Vau^sGtV1F4D?%aZ{#GbN-v=no7zXjbP#7eBjl&oi)EO8UoWmgYXNNH`NHH)l zl!rm`!K5&VIqSn9;e0d<;_t^$@o!-e^LfG{<&A7O#N9>V5cTchkZ@ZX&cKk&z`(FN zoPoiOfq{WPf`P%4fq@|`0usIpA|UEEML^I1|mlU;>KAXo!C87zPG& z1_p+>7>K#kVj%H(2uh!ffyDO(DF12Q5+YGyhSF8>5O+7mL;Twt4@p;(;vw<7 zHXh=igYl4fxDpSs=RQ>ZTd4S_cu2YT7s_W(fcRG^0TTc62@DJ=3=9nF2@v=6K*c8~ zK+@&x1W5aGO#&o5E+#8?Re?6geWFo|#%tVNL%Mu~>bwR}^B|_Xg zKaqjK0@OZDWMJ6Cz`*b<5t3gQCqeYDNrITWGYOI&PbNXkd6dM!z{J47@G1#X{=7|s zxPNvsBpobDhQ#BRWJvg3NQQ*x)ntgd50fGF|GQ*}JN_m^!ihTtVxMRVBwSTfAm$mQ zK*HZC1>&FB6i7NMOo8ZYOM#d_A%%gVje&t-P6`7&Q4`u2xeel_@Bza z;K<0p5S#|_$JBI4`7t*gV&2hoNdCH&4he?`=@9#0rbGPy8!9i70r9U?21K1=2E=}? z42ZdA84&e$84&k{XF$Rs5lW|JK=MsCRDWXzBwV^OAmK7S1Ck$?LCw7hRreIC|6K+o z9ejq$OJ+jCO*IqZUeioSx#FG)abIF4#Qv;Ih`aMMA?_{9gt)f}D&Lg}3HSa?i2G(` zGB8Ag>UpRQnO|{>jgS)N8GIkZ@lNRd*l{QVt%^gZPs< zA3_V{L(G%Shm`B8`H*&XWIh9fGXn#|JgEGmd`LXK%7?h~M?R#U{+|y?PYMMPe`^;& zjH@RE(MTq@+pA0E3g1kzUCD`!n?5m5}&;V5PRkpK>WWNYTibuz8wXSdh%)k z14AhT1H;<_NP0*vgqT-R2#M$JLWnsV3KdY6i1`l-A>sJ3kbyy$fq{Xehygqf zU{(Yve*=mj@mx{_so$m*LDU^8g4CNIiXiz`qZrcfh%Sc2&)i~&yY>`A>Z>cokn-$* zF{J#FDuKAesRZJ_R4Biz1QKp*OCavsRsu2aSP3K@JcWwCFM*`z|0R&{7A%FtpG+yl zpPHqR_%|EQjO^ zzH*2?O68Dn(Sq`gp|o{5BpjT}A^Jk0`jW~aeBlkY{9IxLU!$;K9Ja@U;@s|FNrrv{x#s z7#M;W7#OxzLDX|pLuk!vh&r!o28Ndm3=A#R3=Clm3=DZS5c#7u3=F=Y`nZOHL7kC- z!K4<_Ux}%Mm^-HqlD=-%F);KoFfd5hGcf2dFfgpDhor;n^^kTne*>hw=h(miZXZ@O zFfc3ywPPC~=`OI5fx(=SfuXVylI}J(LHu{E2~r+>Zi2*%elsMz?V2Is;@%ADkAyZu z>Y>tRNIIGgm0!~gNhiCSA>nxus{c+iB;21iL(&bAn_aB0x>VS1(ME6 zS|H_pWeX%7*S0|V`IB3q?rnjD%jXsbhGtOzr2knlF_fP`;u2PFKeJ0RgTsRI%}b2=d5bFc&ApOYPs zaJc}buR!H*bTBZ?Wnf@<&;fC0T_+^IrgTEWeM={#-n-ig36F1`knmvdf|w`S#lSF) zfq_95#Ajk)*wV$o;K0DZu%ic3PW|kGxKpMV5^h?(koI_BFQmNe>1ANBWnf@f*2}xL2efLTmLy{OQmSahH2P#2oK_h`smck4~ds1sJ`BQNclUt9}*tx`XS}k^?pcvKJSP4?;BJ-%LItM zToWMWFaHEce$$%(DZiqj^63*G_T@}~ls6?4Anq@p07*Y}6Cmm9>;#B=9zy9i6Brmw z85tP9L(S8j1WA{clOX21O@gQongj{ISSXzarSqY5#UzM78Ye;gH*pdqUe`>5gy+6V z5dU3)ia&;``#K5YKY_`Ra$a;Y#Q(aJA^NQ+L+tmR3`vI(lOg%Ob}|ElK4@HYGNhcm z45hz9Y4ItLdeU+V#GKSA5Od3?K+J1}(i5gY(&6kW5O=SEiXWc>ardPukZ^l31yZm4 znF2|_5>p}O8cv0n=M1I&rb5h%gz~eX{8Fg;wyBVCoG}&R--S@|ol_yVpUN~yzBHQ#@sGnai2Z)kAmJMcRhKahl1{3oLEOI_s(v?=J~<7NZm&;+ z`0EW+9mjM?K9iXavCn=wL|x!?hTAA?9X6>5`cc_4P9$;m|u1V*g60_`#VF|DT77KY{A| zGZW%J_F0f}R$&&ze!E!^@dPMcISXR$q*)MmuA2p^|1Qpg`1j{5NH}uOhPX?3HblML zY)F2xnhj|`dd-HEZ?Ur>?kk@Sso(o&L&~Q|vmyR_HXD*}zt4u`f95$5|8dQM)Xx%7 ze!v_^zb9@E1H&u^28OwFApW$N3sLVo7h+!YT!=Y2b0O|6p3A_H4H`d$s{06)|38<3 zp`U?)fo&e7{GB!r;_fT+AnD`zJji&@KPYWHA7X#Te8@P!^7)W(d@vspj{oLE>{VC* zq1BhD7B z`?e6`-oFbW?Recqko*w62-1G4UIYn`wTmF`JG}^!E-o#C*!yk~B>dPHL;6QDa5|8We_@c z8N}X{WsvZ0fQnCD1_{SGP-nw zn?@@k=_U|LC#-~&kNGPh_Rd)e85du>5;89QZzW{hGIbTi-&0mW!eiwsi2FCJg1B$r zDoFeG1eE^03gU0Q)eH<9K;!MJA@2OX8j@ex*Ff||t%3L_Wevo<>NSvb)42wc&wJNE z>WSrRAn9!bRNwA35c7{h>AO(zuWKOg`@aTKzOb!@@b#dyDU`NZ3u*T`uZ4tb!dggp z=B|aL!^X9ca$@~j$h_I}wG0eZp!w}}kaYQA9V8zKt%ro0#(Ic9EZ0Nq4OkB`uVOtU z{Cd_y?Ag5@l21>photMrP?~WAq~9UG0iw@g1H^qH8zANuZh*u~#|DVLn;Rhc=KBUn zxTS5gWMGJ3U|?9Z5i*|tdm|*AGB!cNyI~W=+^$U!cP!t;z_5dXf#J|528K!o z28Qa*5c|GvhJ+i>7DzcHyai&8=@v*j@`chlTOj$O8>(*k7Kpx+TOjdyVGE?ZySD`r zp2Axp>BM_0#Qk+p{<^J@bog*9q#we!4bl(x*ak_*b=x5N4sC<=o{<{c3ImOCKooOVF`pRfZGUgbL=@!Plq zk}fAf)lJ(0DX+KefW-IP9gzNq=uSwxEORHsAN@Na>E+~3i2Hs+X{KEe_i*llxKnl) zL|$tbB)u3y`3buq?bdWCe;ZW&-d&LJJ+%wst{b}`_57V(koaNT4T%?p-H?21vl~+0 z1@4B}le`2(KF)}bbIRw#Xd<2q?oR2`#RqYXoI~N~; zxO3GJi2Jr2fyDb!sQeSC{A(!x>k){%{v3gXkHArgdfB6pa@FQ2#60(-5c$BPknxzf zqY(R=jzZki2~|JyC}jL}$x(>?496hm2p)r|mpKMW_sYj0@}b8d_NN_#lyg=uLg^P!^WGnWgg3)+NIfNY9AaSXAnm;5 za}fO-&q3_j0p%Y(2Z`@9=OF3!6;%8yl>P%1XFdQ%^io%dD9IBfe> zi2qMQ>E~A=?qa$ z1A{*U14H;N1_oCK28QLgAo=O-Eyy|wt=o`rDS^`Mw;}3g-DY6mWn^G@aGQalfPsPG z-5p3g#@~g^o7dfi%;!G73rSC^_aX682&LEFXJF`MU|@K8ACfN0A3)OA!UvFc*!>5P z^*hQBA^yvL$iQ$2G=KaMqCWl+14A7n14HK{Nc)iE38dX-`UFxwWj}$0!{H~8@V@c{ z60Q%QK+@6ICy?@8>M5iga(xPs&wL7r|JtXJ_?Zu-*Fx#tPa)$JC!a#%?afn2`}6Zt zNW7>$gV<~E3?lCcr9GZO;=}(LWIiJM8DxBO3zUEN83V&61_lPT=L`&6LDBLYQZDwt zfVh9n3yAx6zJQc3XQBK@FCguzZ!aL}PUR)U{dO-Q`6=Ngq#s)Z6~Fcpk`8{ngqSD% z3eta2e+BVx#Vd$A+g?G!bmpfKJYU{zW6i5T~$!J z4l3RR<#&FDxT6=!-vpK42Br6YhQ$Ao&yaYy_!;8w2cIF~{`WH^9xcB>XkRFu_yuBb z=@*DQD!)MDtKkb|zG~qYNPItp%D?{tG5;G>AM00$y_{bm=|}71SYINPx0I>OkurgQ0vMMg|5&sQDl{5SC+NVEDqwz;F?&h8H9VT2sNuz_5Ucf#D$| z0|O5e1A{ChWK0jVK5!Nj1A`q@JybbEJ|hFe3kC*;1_lO(TBse)ObiSm`^N81$GRbpvQ4!4f7&I|XVrLkQG- z5TBKifnf#6ZBQ{#{Q?pPtsB#Yih=lDObiVF85tPPfjA5d3^`D@_cJmu%!8^c2Bj4y z1_p7c9B3@!G-zIwiGkrfR3D5|V`N~M%EZ7hA2eRe$iR@p$iOfGs_znLj*N+cp`3|< zL4py|_k-zM&cwiQnu&oyh>?NeDxX#4D5YNcK zAO$r)6XZrvI%i^FSj@=4pv%O-uoCK)g^ZB4EO;K3iGg7Ofr3 zT;Ci928Kcg28Jvq28L;%J|rUpLn|W#gAWq}!v!R@kqitB!b}VdDNr%cIug)c4v;z4 zP_d_ z0j&jPU|?utg493Dq2dof`3AHOnUR4(nu&oSn~8y;ijje#18O#C&fS+0QU-=ILHb}I zt?5h*44**^1_p-7P&JbwILp>t{!%nDLkRWKRe=8FMLo_1;!#zd@h7tw_ zh7*ho44#Y(41u6H1+7(usy9Y*&sI>{XMnWzLE|f+wK9)DBLjmA)GW~2evmsrbIqMlF%aJui4Ed|@IFQchH9vIA(RHK zrPc@KJ5c!uwQm9_UKkh{;-P9jFfcI4fzlHb1H&062JkvXka?i>+xtOzg^7W|iIIU} zEh7Vi0n`kTde&IFay zQ2ss!NL%_dBLjmd)E#_G3=A1ic@;(m1{+YGfbvg5)r3OnUPcCn6HvKnpmGqDADI{! zQlawpjF7c1qD%}7PeEk_XkP{c14A$)1H&Cg28Qp93=I5G{h+;ArA!PA{}>n;)-W(I zJOr&d1I0HejeyDpMo7OBv^N3d^IeRPaUl?UCTQIPBc#0{2sIBRo&nMWDmS5Gr$Oyt zP&vWKz!1*Jz>v!XS^H$f$iPs?z`zg->UV%NGB7aYGBPl129@8Sy;)GP2u22m#Za?A zeH9i)28Jk*93un68xRe}O;G=X)|-KZ=7Z`;CI*HR3=9lQnHU&anHU&$GB7Y~2K9@f zYGa}1o@HcU_{GS;z{$wKzy+$CnHU%v85tNRf%;rb3=G#n27>k_LG^+5#DVs!eT4FN zF+loHAZ3D3u`EUghLfPO18Rp2Bc!izmyv-%laYa8I>>#Xa*7c$7qp#;f#ER&1H&Y! z{uWRkfU+B)W~hSdbEp`|t~#i=HzNas2onQ?A`=6{ZBW?`l4M|D@M2E73=9kcj0_C@P&v>#E-@xZe;}KYfgzF!(uchWReKav|1dEy90Rpm zm>3woLB%DR7#LPV*?vq63~^94jJgDM&s0VR1}7#424+SE22&;mh8ji&hK~#k49QFk z46aO&eKEH|Yh@T17)~)VFl+*~SwVFh0|UbmMg|6bs5u{?G|c>G3=9m#3=9m9nHU(- zm>}~n0!$1H3qbWTBLjmH)PFGb-;nf!)`A^pVqnMh1p1By(pmGBA7r?TurAtR;H@vWx*T5Av1~GEM^(0#j_D zFlJ<6XaTJ+2c>C7NFQ$_SeAi-2bBI87#KW3;mp9m;LZpcb3e+&!0?KJfnf%yJY-~G zNMeG_^?|g3#y;mTF)%1Y-K7I+ze3q;P`^B6Vqhp@Vqkc~$iN`P#K7Rp$iVQ7k%8en zRNZq>IRaG+GLsd`e$B|hz{149zz*s+F)%P(2etD+?Mx;H1}RV*k`c1@3#5k|)K+JL z%;~NH^>ab(FVKEjs97L2pm|Bq+9l9_TF_pUYfKCbe4sWdNDULDKL}C>!l1psR~Q%= z6qpzoG?*9|_JQ&@0|Ns$6J%WJAQJ;aB&Z$-=>=(EU|{HEU|{G5_4Sw_Yb8O#CqeZ! zD1I0j7!E_#g7(=S0F~`b3=C_T7#RE+85n*rFfdGHWMHUaVqn}SE69dCvXn0;_fQ+4h#6j2+WH%!N!%+qXh8`wJ{~aWL z3bfA$)L&$P^zA`n_d#uZ(7s3}28Lut28INvncqQuEGQeK&Kv3%6D9_RjiB%b2|_U! zs0|KfgD68L1_oOu$hgb}M#wr&&|bS*s2K|w85s70{0~wL#h|^;8Ppt0Q!?`v90NSz%+Lsh{Jfk>1~sRm)Wqz9{LH+PVg@zmfKUcC z=Zw_kY=yM^B8Ad|l*E!$ka$LBUTSeFgPL=GUU7a-szOd;UV3R_I#{Hj5+q)fnwghb zqTrU9ld9{OoSa$=(w2`Y3UP%qOvWcaJv}ooT|vV?I4HFszoCX%tf0ciSs zQ*-l+D$xW(GKx|YQ?Lr^23O`KXB6evxsX3`7sSIi^`Q>>z`H3kAmV$3$USfJ`5rdjbX>I|8R7fr@DoV{O zQ3!JM1&Nd-XE3O_reu~Vq$lR4Dijpur)B1(!i0(v3oAduu z%wmw%ip&xQHMhjF{Gv>dD?owEpypPTng=r2tu!a6IJqb_HIG5fJ;Dg$BX^KRNM3SJ z%}Xsx%uy&#Eh)*&OD|?nb5AYGO$2Fyh$s}7Bo>u`603WDNxp(hW^rP1E`ypolIs)- z5=)XZKqDaAj^%eoiri zT1aAMIW(3-QY%VIi&7Pm6O%Ji70MHf@*wFJCJxHtNr}nX3~EpjkoL^n)FN1+8v;sF zi0m1X54IsRueh`TRKTQy3kZee#2k>tq2MA8xey3TEy_%*RLIOLNlh=xEU8q;PlE(K zC=7}j)WR}T%fTrM9G&1204`JlE`VT)62YY|nlex#M0hVVFD;)z4N{CNlqKe5ra(%k zoW#o1A_a}&ocwZ42DR|a>`a9MaB&+BO;S;*MfnVBc{!O$$)!a(3LYUL0s0^!SfMDj zur#%}q?kb?zW|iN5_2>eY@pGh04iYY80-}C@)dIPQ%ZAEixoh{T51Y|okD3|c3ysY z9#n$CQ6agsxFkPUAvXt9NR?zjQbM9aK~ZX1W`1cgBqL-(ax=u)8HvRTNvWxM3TgSJ zc_|8sc`2ZjlardFkeR2)0Lpfb5ejAbnaQc(q?202fKWd3mYH zC8;T({FkWU=jZ972{S7ovADQAzbM5SoD4IIAyz49fQ$jv4!KFGMG6H_MGBChR>;p& zsLU@dQV7q?R0vhbEiHyPGA}U+6b7IwwIn067*;g#$b=M`Aoax{D>5Nz3hHC9P!XuIcT7ns zR!D^8l!C;P3{a4Q%>?-xtXm-^v$z11utBLo0pvE2umY$=CQavp63_6%~L5Q*+WZ!CK*(v@{tU3kq^7K@kIK2Z5>+$D&lQLyJ-qb8;%7 zuF6O(0SlHV=9PfFodRw&fZdpupOcec4l)B|uoYKzEdvEcfa@Hx3@HW$EVwZUt^kpo z0S?&8{89xhu7@@!6{>3$z}0JJibAzjMXf!9V{v6(vO+Pq+*e4>&n?KzNo8;>QOHS6 zEG|*V&r4OvEG{lhRX{Bd7{JX?2FKFkk|I#$&fr*Dk`E>nG>TG_^K)}SwO5KJM5qW- z?SVODwL%b6=&w>6@#)TxV2MSz~ESsSS2b$^vOhIBLpfWW%HLoPKh`}iz%*#nlD}k|#GSf3k7{IM^1r0Zl z4p0Lfq`^VCZekPkXTo!HmK!th9uDxP%u^-)^hn5Wu|B5CFUq3mX>7X7ZqD6 zR2$WTwSk)M3gF-Z1&*&1L@*f~BoNz5@)a^miV;Ps9#;S;*{3Ri+hLg{ATK2dJ9}|le5>s+B^D;qMp(MXZ5vx6*G7?hYrKZ5EVMQ*Ne3aq_ zqCz1tFTW%MTql)eK!N~N%_Tw$!_?f;oWzp+B0VlHP~`-wnL%`@0;qZ^DN0O9%}p%I zE(WCxKTy*rFC|}(3lxqZLqO4yn3k5AlbHyrEWu>~TouISu)3}&6`aGMRVt_e1($;m zH>Rd2gnGjJj(QNCkir$h2G>OJ+7(<;p*KP@^AuEl{GA z`9A{oT2@6=bXgi;>=_Q=NwQW z<(!ibDjxE4KvgHmSWuM<@-Zmm6oX9!m%SwnpnL-E@H*$`rDdjr>RfP(f&t8jHO8DF z%_fi_wE2`;QIeVmDq|61dI~|Hl1w2vCo?%4+&zIdD8PQtEC%Nja9U9SbyGmCP_WnY z^Gb^Hb3o>R=$zCdBqpo}U5qRSZ}T}rwC1E1feRA|4-(;txu9VYke8E6!TB;p0opiE zEJ@_@$V>-ioU+86($r!|+(8SUL{Kr92}8Ld%DO{9GbdF6 zWEE0~Kr}!)j|z#!1)x??5hw*hEXyoWNXbtv29?~opx!z-nL_0A)4&{1V++;x{Ib-d zoWx3yeaRWH)>*MaNosONUS@J)4sr_|qzIRz${?+QlEji!kOk1{Rv|4hGbc5L3q=T2 z;$+rV zt9)1^wh~n8f|9u+7nf^MQGSsXs6JqD&c~@eHz(P^P@%Xq3D&YK0^5qBpDQG>C_S|V zG%k{1#Z_%si!czDmcc;|DcTf4vUwQp2an7-7bO;FfI9#T&fxkEN-5+*#;78IJP z#h}7IsZs%y3(8V+@(WUn6uChCAJE7Xv;%;aUCL66Div}-p#%05IOr5KGV_viN>frn z4ggiq;5ruU5@^x{yQQEgA2e#H$>0nfL<2>e6^xftsgMZi0Kq~SmQ(XVL0g&%s;)sr z3n+J{7lFE;dJN9s4lI-cRiFlj;5cJ&(Je?!VQ}$r24VCjpNo$(xH;mInwV0RpO?x2 z85RVO3W6Gph(?~ap(X=1VN;NBT4HHV2?KNh5ki5+en2CtDbT?UkPj82k($1-3@)j; z`3x?p#mPmP;NClfOKMqWaw-F8TrMdmRRJ>KrvPft7r_KmK#b)4q7;PyP;W2~q8wCy zfO>ExsYRfc4tN*>B}YR?kHO=>njj+}IzWL44QkMsSrMq(D1w!|kkkP#wm}sMw9n-N z>DWPvsNB@NQqT}JL@79GGm909O7lSDIG|VpB>|{6a`RJC!J`BqOLOx}i&H^EsKxn3 zpjHQ{Jq7NWgCtAx3mBm79%$bS)?Z0Z%u7zq0p~a5${*xnPg1V3As^2eS-j3uvSZYzHj)f%>ONW2D9Q3@-Vg0;w1@237*@<)0@NP@=>!RYY)i|`0gZ5iTVJKcsVR{31d2gWO$KUe<>!=w1|vXeB`GsKJ+(-o zBqK2o97~XKRE6Tqs#FG-qQr8@a38p91RkGc0FCyhg8BoXjvUDPRLGDJc;Je`r4%%D z?-~){8Wf`79}wc{@5cpgUO*CgB6uz)89X?cmXn_dF5MyJbZ%)*NoGL~tX9v=gZ7h) z^|-i#5WQS0g_5GuR0dQ#f>R+w8@Y)UnYpF83Xt(ch2k90h*?o-UP)$dDyTSBD9bD= zDNW2#NUluINiEhyGbRL-{GdZp;3hSwb)^9A-xYycVc;QMP)H^xrltC1CL#4^7%-r2Cry#TH7^Cx$YXGY&f2(wdiDy5u+dX+Vuegqz+|9x zdtyoosM*5+69(1TpsbvlSE7d^2rAZ!N|Qlp9aRF{Vnqm*fa={61*o}t4B!S2gj7h* zPf3L`!HrXJy9pGSpkPX^C@BIJ`T2R^>JdD`1lA16b0})7G7G??DOsmtCT8Y>hUgW*mSpCnqAM*a0*}X)!#693qJ&+Di?E;F+L##}~R7F7afu=Oq_Bn46fMngZ3TrvRN%0rxV&lPBP@diVqgXcEFLH8(LQ zmB9^MaYHGE+|**wFk~@<8)(1{Tp)l07(ABE;FezmD*h7{(lg6a^N{KmQ1cCx1nW@DLZbhl7RjD8f zG;i)!l$n~B0-knY0Ci-*gaWv_N-R-uk1ztY7hLl5QZ+QeQn02fv{{QR25qV^xT}Gu z&Oz${Kr_(6C8bFU;0ytpCuIN;;ABt?VS;8y5G;rwXfhQviwG76^-VyUpgo)<&@dHf z#v9ZI0?l%Rl1>41_En)IKRq2Zxs+I}kdg}SbLc66`e@(=D`-w9DHW~;JaFe666B+x ztq|hq#NZCDQb81GjR1^BtQCMP9su6|A z3>wWyOi59IjP#ae7K7S=`9n{Xq7B+7?*47odU+G?)Pm zM?Ef> z9v8UTn^~-o3!40Zw9rATrBX8z%QEwe5JPs5A}=Su95h;7!= z0!rA$ppHqtLRw-@aVpK{z&%S8a`N-DixqM*v%%A?>P4xLWlEq>0FNku2F5`NA}zBB zG?JW|n+lEo!cx$1b8%`(X@LT047|7mG${*CNg1g*1>lJjP-+KhPD(8vjmHoMWFdt1(<6SAuFX|jlR?r z1!yk|+Ft;*v_L5rJfxjj0-m%*p1>-pEJ)Skf{x1NAo<=(LA|+8kAOc*Fj0mcG zG7<~G^Mv_D5I2BE#z9@N@>Gx$AuC5$U;!l03<%)HW6 z$hV~XQbvPGJw@V<~38Hqsp}m;Du@s60~m1J2e%Q1Q7%E3}BJ`(h~F`KnC!_HVB!= z-~(G)=>wWe2bJonDGWY|C8>GIl?=#>-9V)#tQo-IlbTkd0G`zL0hPU=GHbco=*^4MJb=gao{3hb{wA&~OE3JAJ^$~(}& z31$B9S!K<7=?9!Z6HPFNtlwHi= zo1d4@;F}Lh`T;qm#SFfnfg4@$%sF^S7PP3L2qIdL1Ikgwkm;e43{ZlERJxF+1Z3R` zV&Rh_`V?&{xPJsH4{|~ES0Y%OJ%b-;xu;)hB52xB!7nv4JtHZ_tVIK_0rU%TbT=|I1v{AmB$=9*Uz(nwP+S1YSa37-xPnp( zOF=U*;3*abUnj&|suj4k?&}2B4$Y{kDX2UJjex`=P{o;337-&5P0<7?LbPZY{6OvC z(&TIge{Tl=v@{0)JO+Qr=u2oH6)AX6G0{i&w!j5TK=oAP!_uu$Unr5j1=YnW1C|0CnS17y`hHdsFjDK%t0O zhJ<8k06dj=rZ7NPE+nNYB$k600)Q$$q~Sb;oYazHP-+MD%s_L!pwRGlbMtiebo5bx zbble8u9A$@BFJhsFbiCbDHLTEXM_Bm4sFpVCFbR&rszSYo517npec38(l3zpplTXa z#h2tmwC3c4m+nCwln5R_fRy;)r2)_d40$Q~`9IE$_$^oqb4k$`3 zNzF^i2U!AM7|9S&l$n>DS&*26SbxY60A2hYP?Vnz9`*|?O)X7LVF+^cg{(JW2m&ot zU7lPPff`Lb>koj zte7DvH8&N!fDviYu>x%V86gWw(2xoNMKZrE6{IG=EEO^wgQ!?x#Syp}3m!{^EE)xc z9N0tP0yqe?WKlsazX%k1P)>PfN=XJo5M=cTj0UA0aB~kdz5yy)6hQ4hNCyctrLIr{ zS{j&|mX?}a0@jmX1a(plsP2R;XT{rZU8dA2^)aS#BB&#D4-bt?mB@Y4dhsm zLrQZ>K!IEgq7^i%4Qe&PqPd`OD=ErMEdwcrmda?|TF9yia7`YRUjlB(}B#1IUcnqh!c!cYq2Zg3nxR#)Vt7J+AcAZlQe;D#(z9JJ&_0X`TGTS^R`MTT)e zWko4yf(Nv`6ST|*>YJie(EJv3LJraw0OwDTFN<{`NemK(kRm80CmAw36%1J;3bz$J z-~tzf@-4@@CYF;|k937)A0TcgJSR|E-9n9(2}s7yfA>Z0z1o1 z9$ZqHlWL`)=9!$I2NH$wd_X&yU{b-kpus$-0?0%WLvU#kcpVQzaA|QtYF-KhY`0J_ zWW@nEl)wpr0V)FFg!sC^dl1krEJH{bLkM__5tITo4k3%U7(zg!K$(y=a-d-aXuM?> zgJKcX=1omu0PQqVa0X=~FcY*c61+W$AtVD7o}iKxwCy7xv7{(9KTiR+)2OJ}iUBkh zuThW)S_-TQ-8BZvilAHub}qPe4R1GrMlYcoRuYky(?bhocsmHbKCM_mLkTputE8i# z1af_9UWpR8b)p2CwN%pN@&z@Kia{<07lt5vLG=b?RVye#gSzs1U}uKG+oB*Bg4!~m z8Qpb`xhLf{e(Iz5~U zo3vBNO9d_5Ni3=aR}0{k1fUUQ(3T@eo`jY|;Kq@jf+J||2BrYyqfE%^0_5@fVo*Vu&Y6Blj0H#AACkkCbh4?MPEvwhI*NU&aaei3-_9AbG2RGk$UI3_^R2TnDhE)&SViQuUO z@Z?S=s8<479f4SX4o+ndCqQdO&~6X#>>b=8h?x!W`WMhLd2kO4GQJGg4Bg5C3KCFN z2kNeYHen^_=ar>`_KKFq1wv8q5=|txzKSoQ22pL733|3;1EV>?}7?bP|F(H4g)uB z!5#${R!AiVX!9tfRj&_k(1VH>kZG{!0$GQ)`wJulX-~nE9k{9lEqzSO0WGoxH3JjV zQ^7mlKq)veGp`stg#?Zhkb{bKkjBU%Zh-VGTzs61^*{lY30kRCl%JQLQ>g&1aX~wl zK&#G5z!?&pCQ?yS1T@QmT7{VyA{4|9WEbHF%6dcP^ixSgQ6+lfq1#mmeRG~OEIUm%r2Zb|2 zKX{>BF?h`!L;+#Vs6&vTRR$TLg}Klrs1Rp@2WJ()&1+DoAVMFUXecs4PXWA;88Scs zDS$H(qf3yrMW9Y9II}-~8ZWAZP4(r2 zYOvHy(5R(CVje6!K@(OPpp!PB8w3@~6N?pcQ;X7Jrb2=hJl??NRFt2cng24YF1Od{_l29fCHHr=%iQse=O-q5-z)saPRAGtWZLP#aOvJFQ{Dr-EpY^D*aLulQY1ZH5o!cTgpJAZZHO< zZw}6J;4vU@;|jLN4?c(wAN2thSViC}n<1nq6EtCkSaKdxS&+&Q8tljr3R$oVT4e$* z!ob5GNTO+(d64+f2lXDngkD8%4pIT@J3UCBLT?LAuTziOdpaKyQS9*E~QONcJ$aWfr&;rPa7-Z)F zlnc@e5rFhBzzq-ZPz|W?1DjfuS`1xk1*)%$K|>L^_7a2^gZBA?ri>Xt%mUbSn*y|T z2@(Yj<|Gv*f>+vu3i#3z(Auud)M6_JL=T%G40Iq7Ll|h!Ap`ix5zxYXkdqLl7jes1` zu}vlU3TdFtqL7tw(0ZU4yeb7$N5U4-6(Ck@f!p$##R?gj=^4l^a>zpdoK(a<&3!Nj+%kr2=U8aX6^cpa2=!1ZNY70CaQ{EE1Vo%mCVv04lT)>)auYR?v2b z#IjUyY6VprD9dxewHA2i3%D(fWt<^DFVzZEBO01j)GCw}!v`Di@_aeP)VAYQ=V8^ z3~m8KNBBW2wjnJc&?GSU+!By6pu_@M)dew2N1-?~FF92qH!-gg+{7vdFWG{g0Rl6t z1l)`RABK`w0_&=Q`_1qnGqBKm~@Tmn*Q$an!%KTE$iZ{@*#k_Qdk|M|sGFae)aug(@;d5|kXQY6~b|Cv# zGE+c%20=S>!DTBbfIzKN@M@rvR6Q<7(9V|>$i_9$57&JK#nWhC5(cmZnwQ9h#x!{_Kx@2GQ{X}n z-H?sM&^|A8T_HG?LI<$)pzGu^L5t#2i&7yid{8|C+7AUPxR(8q1Xcq! zDixY9Qd7XKABD_fP$!NHeJK)ji~^j^z>POp7cdp(4bUiyJp-so#*mbgU#^gwnalt= z00Y`rgO2ntfVcKDAQc0kC{2RoQm7g|=yK*_g>rBrOG*WI?ovRb3b~1S;DsuR49T$V z*r4_neC0n}6x;@e8pr_I@x}m}TLLv37{E(X88Y&7QyDU$nzSepZ9rIo6FgUdziv=!StjW42wj)N zl2j`NBLgF2T?0#910w}PQ!4`lZ39aK11`{NNL_H>2z-u-m4ZiNa`ua!ZI7EKE7)X| zloVL$>nG*pr|abAaHw&h| z-n>Oa%~6v9#N5!J@Os0_*PHf&L=>VSGcvIZZ)eVV)3QAz z_Vucj3U9Ved9|SdB09I{?X;z!T>>cF(1_Q|d%z;Ecl5ko(D7#OmbZ;-6yEl)c{6v% z+pZlNYEGatJ_AA--b`dvt;6{t!v**+XCV3o$;n^ zio%;|J76NOH*bB@v<50U<;}D$Z{}`!-QDpPWJ*uZo4I=w-b`Kire(*Qt|fRCEtvSG ztry~ckga=Bje5Os&a0iRAOjSlF^?&YWq1QoKMiK}>y=YpuUheX*&5s?QbEO=t|hM* zbiAHCN8xq<%vU=$yq!4(Zl4-x{O0wPDX$lFzHQm?df6HV5NA`%tL-~9)Lb+fUTv80 zxV06`eABe{&4M{#=If>{uNN$NyB!kdm6;FRU6$?$6X zj@R>Myq&UF;cd@KWC>7|E<;iJW=12}L|09QH;YROJ9EnGm0Q3{;76#wf;bWsV6f2BP;=L0 zcr$m;>kTs$-n1+MJK0^6;dOuOo4I>HMuMcf=e(IW@$J+ZVCzA$Q>MJ>TA~0-l&u}F zcT9ORXV2?}a}+?{>7NNU5!K6Ydse*JIRjGyEP^;{^>u&Co7oMomru}8^U!2?vurLn z6MAYgyqVYfdI2;OBDgT;dTKI2J@&S_0hH0Y8}yNL9eBhKq!{D`koLJ--ZpoDik_)0 zARnrM#rC}349R<5nhbBJ&Vl7kZ%u|b-Amq1>r{XnuA%0m$pG36_oi#f+tx;y=fSGL za<8{_yq>@2&5R}B@bb|FbuAqOJm1V(2|ir&&F&R%TYEv8yLKpmG9W1B`e-shmsUV^1QUrh!?7JRj-;Z1WV*b-k&1~ug4T)`#b+x{)Dd#5P8nZ5Kq1qz0W1J3j~C2&wGkrndeV|N%O}2>wngFff}YpCQ(iA`1giw4y#-6b z%-7vZ-gM7Vc+)-Sb$<&)Wd52r-E-bRqdN$cM;hMDgC?k8^k7*4lG9KNgJrWf(Cq|2QC2#v?yqOD0pCAEnh6F1G zm5wuZfSIp*8eT8&0jKB?O$JC3d$VbV!kb+aK;iLf)81E`7ig%V$h}^+=Jn(`uQzN& z6^HP^x#{(a9SU#Sdci>n(h3%Q+ueX%Xoi3ajFwkh*MRlCUcckb@`g9FCxASl@OH+I zH{0fDsD+{jGg7?>mW70$0;FhrGk1o<>or?mZQ1~;r6JWOc)2(?<4wyP4YhDhhBq7bLg)o^UN4-Zp%$fy{Y2Y0v!=Y+w3b2RP0NC} z&2tsrG_83%b*(0Y4JesF>yf!zUN36_9c&9LR#qs0s^G~T3U8*jy_ws?V5jhAY7eMd z0SUbBngW$(cr$m)n*}Z4rYN{*-#G)ClHW9~c{6L}n|TuyK=HI-ssd;}_SL4nuNTgF zy>ySln+^LwS*2x;!kdjPZ)Pue)3x<=cgO4QCGcZz-^}d+1;*>`8{SOY0y*9e5_k-d z_7s#-KoJC256fEK%-y2!dMBvhRd_S6iji$Z^xdGfvRd_pf%bNvrP#dT)9w=EXTl1#1 zn*q{9MXDL0jaFz?2zJxr6`+fcm%@_P&|5K?g1|L*X^VyO2r`>``!@VgP5B z1(2wMmM-AIQQ{8Hh3bZsN*Y>vTAHXW`Zo(2-^^MGs^LM2efb0hQ0+6f2Wk<>`H)uV z+o?0&bkBLU37VT;?d-7!%|oMU0$12zw}YFmI5bt)f`S#C1m`Hc0asVho(tH%1yf%y zpP+!#S_V+&p1bAslquj7Z$T-gx)zqSLHQ7u)eNA#18$Ei!0R-HH@mk$!iM4P)ETdv zW`J_}o90H4si5L;!5nbz+A!zUrZo(Z(wG5MBP`y-@MhkG*Zni0l*X%VJ#XeM1vT|R zqNr^Hv=9gDQ-Ib9pn84YL{Md~1J(3q))ocO*(M6FH?@GuKZreW_1mU0yxz0`R5o@k zfu-s|q23ZRwSkjmlp z{7#S@s6u)(YbBVMnp+BrRTMQEs+tV1H*a~fU@F6#ISby*-Sc+p94tv`0jzd{S0#!J zZzgYewPS+uoJ>TIPaEnKvCX zK(!;1{ZPfzCn&s`yaR6I+#Ut+jK!PnD?nu#RMFfPh1bh_UQb?vqz>8#LwC~L9%Kn5 zlVR1G9;jFaHE|o)fU?G`4Gpg+&sBJ{VbANP8Erq6@H-X4$o+>C#>Ll zGke0Dc@sf3INI^P@YvJvbP?I_I^P94RE;y?gfF$t(F~cW=(;d{R?seq=5rU z9*D*UE;lH`+<;UmfXi}_xoCwb*pHw(3o>T>X8MHJ>(+q!5p%b^ZP@_In2@Fm7x--8 z*BkmF6#+;GY~Lv;RljL&1XWY>TA`I9_k3dlM4P3sJh z0k2y+Ua#4r0PbaNQFsmN_wZ2TIeR+|@Ay+ERiHr-E~+!s{6mK+%k|>Vs8eid+HA>OKWfzG>OW^=iYM*Bj=%nKuEHWFf-`ptS#bYR~KC zJqqxiCMW_RRWKK*1G{Mpw3qf~ZiB+>K_VN+0JcJ2<8vI>;EUT@e48Y7;&=gq7w zpkZ{#7#pZd-`1-D(fWD!JdH@La-q%$lMor zKx5WQ_-HJ04+>(u0!kGC8gqaQML`NOZ~_G57Jq@54(YI4u-nMK2 zhdPq*>*bB0wj^k_uxkpm%MTu9Yi@kq53cz^^B!*-*MR3Tw!H3MqVTp6+TH*+pcz0( z2RvvG>P3NuFSgAA$Hy{|76wRh2ueF|W~_KUch8%-JCOUc8}@-x2do1+w}%03`jFwx zf|l27c7SsfcwU7IKDq*JAHLq!@VbAd0%%qMG^q%hX4&|rwF6QrPklRc>g)a%aEb=E zOAzgOE-pw}11bkQX1tlT5{p$^=e+J|08MB>CtTljtpR%h6siiZ=WhVbdd+JA4~Hz6 zs_|+AxIzZC`9TxjTNJ=~xCJzXiSCEF$TiNJom<}a%}{`t45}gLuK~>=Lp!8x(4GNk zmIX}-$Xl>+Z}=!Qw4nxy6-dDZnQH~*tJf>1fCpZXyMbLhPzp2#L@a>^_drc`&{}}E zGpB$`Sjdbs(g+u*&j>CQwty^zL@&HE4vJbxq4{>^6hz1?fGU9+S#B0s!c)TX)Ow%auCr$MJdcd^%rPT z3Q`-qojC`bUci+Cs6G0+Ybl~-!1a2@gtt>c?Fy6{1U$$NNpP?GTR_8jP@QOAMD3|6 zGN85FK}~9KCkZ@}4JyJQ6*oNJ!RL=aee(rNk)|_2y{xt#1*kZ+zn}gnunwn+Ny+D2TEX=hJ)L+;F^5t423t~Pz4QkzX9cY_&f=? z!8ivTW000VDAJeCPyjX0n&*O=tF;W6qqE>)ZD?lz+GD_?4;s7B6f%E}!kaA}psBu@ zQ$QU(9dPX2u#&j|G={&=>(U4}zCcK<5zfSpm`lv4xOg z&=@S_d})Y!kgKXP3qS@yCNFSWgXj-Ks$fuPEm#6-NkCh=Sp5SU(p}I38xMz+5gm*vwlB z^3S#@Z?=KD%Ak3{*Q<8ForV}R0Tne!OR3&~m4aFU2$ov_bXFMJ8U-J3{ibCOXkZ95 z@B|*C0W}VnHzOd$up9?W5&IiL**b)qoY%|NfT9dCE&v{YTQC*e za@q}=t9-q1324v>RL?>x_E#IGzF7cDOQ1s2JYtobzhy8t5n;!`n#>3@C?Z zL#jz|M{3g+P;Cw=nnBGg#L_E_P7Infq`|51X5j|V^a->PiL{E!A9;}xxU=201Dfif z6f`vw!9!V<0#y$YgK|KP6L9l)2dF0mSQR_4yYPz37ALX3njivxAr zLDM4><{)Px@XAxrQ1}~A)eBnw1xoCotN>d+2X3SG&(wg13TSa1c-9;oh~OFk<|2r* zQ4B*YBt)=4OA66>;8lL$3=fVLkn=&kBhU~Lr~`z&8WCK6A+5xLN6U2Nxeb(FA1JAS z)+m5`Ptf6e9qkff{wYBz$qf#Ak& zIDHMY!3Yw5wP6lub_Kp-feUx97Ni_hw}E@pZ$Qg1!Aqq;MuJDf^tcd#gBK|0py+u(0N(NkU6Zy z2uil7?N-!8h!}Q8EmhIYL-1~O= z3~>6Kwi`@=M&9RjfJPz~&IRk5+3;pb4{RI?)B;=30x}+yRM+o-20bEZK*qz|Gq>S& zFKoaI+7|^ab6Vbj z*V94kBSb9#whpWv;kh@^3^)h0I0U2sI>!$4FlpSK1YItq|O$6>rTOC~}^ zxVS)Tcoy#gcLO0~&7gP&jVOW!`ytggB$t3n7^I*8#~f(zAMA=XJKn$-=)3_18=^o! zI0;htgX00>t)+Xwjls5FEa3wlu7G<0&GHrnQ0Q&ig47oWE!Rd`iw!w_98&dxTnL&| znl%L!ouE;KrX`?8Bxr@=wv}%dZUk8m%bh(Coha)*z@dZGNd{e=3R>yDc?Y;<0bcG6 z>N-JY02M$BSe7?{JN=L#g=KGW`x)G#+0wuOSsD(dz%z;9EgPWbEjX4t72wl?;N{Gq zWe8i|bkBiS5DF;GDR8Y1uA^a#ecphUCxOP8w!H1y!2lci0vE}zHgA11X94CI8q#8O zaD#gKTIfo0&^Y9qB@^}ZpnT9GZ7378mKf9_!qU!1jE*6q0W?4X3KP&IHgp}x>unux zW`fpNK_U~ho{i!4h7F)W)@5rHUT~c(b4pWo8Y$DidkI2(pL}JOYkbD35hJ4mf&1BbIAGyEFPXz$^ew zgZO4E8Hi zM+0cQ2(I8L3k@MrLO=~~}2-2znMJ`B; zfKm;pjRWq#gBn@LiweOF%w;X0a`)BtJ+GH-01Y?4?b`8Xb`NNS5~wr0v*k_K67V27 zXfPVKz6G=_@O5{`n;A=xma>56<3KGm$U@^c^QOFBIzz$J1ze$k_hcaD!KpLg6$xrr z9n`D=EmejUDT)lB@#9%5LCuQS&^04SO;B{@dJG`Vb9>-*GGr+}?Cf?(n1Bk4H&a_) zchA5y{MF{IuQovHo^2~%Z)-tvGt5bPT#%9m)VwpO^>yL`^=IcS0C5d#eVw3<4^Vs{ z3LIGZIs?=q1FvE3(Et@Mpb5C{C7@}|g>yh_>nDI#_)hJ4y=)J|>yC~$8&!)MTq9H5b5(CQoL z_#SvG2y{mas7(n8J-AwES+WM2O5x)0O%BlF3p(cq*;#_xSOj&nP{$OZc7o?Nkc(H? zz6j_tY|s*&H=9<17xRM**wpfB;|4GfzOZ z2fhJstOKXJ4J%%EuXqh=P0wiow~D}xJy0DFTJW)80(i(1v^)~LmZW@`!DtN&LC}Tqop3io| zr=}J(&j1QH$R;Id{R%4KUr(L`uHRwXl^EXk?Rc|%BDkpnPuS4bBx*IjW5ess;0;r- zB}pLnPumJg+u*5u&>k#s>k`zDelu+=1E`I*aRYo=1$cxN)UtwBD3A&k6c11V_{w|G zd>T|UXbm!GL&v-|km3TocMG(>YA0xd1TGk^39c7FEnslJ8MNdGv?36)F9qDr$FiOg z`?@`7QSoNOUa-FD;2BjN@L~W^bGQ@MN1EF6dcl%6%UfVgBhX~*8%O|vhVG!lWT5S^ zvnPPp9)P?G8&g>Tnce`cFu-moUejJ}+VE=g9tH>nJ)bHb7URLu+@?S|8Bt--4;oF;J+}&}uud8PNO< zDvsXvuX#Il1t{-;Dj3)d4>&!6a}}g53*PAjs~ExJ;96_x43I)t!)p)JtuTvWkpoc= z3L#`GV5%VTgfuCFFb>o;YHma`6?3cw(iwq71axiz99&>~K|7WoD4U}Inw4$=H9f zYl>YCtPj*!gG#???!>MSSpqyJgxJxF*!=_Q;J}xyD7;?T@V0vcsO7qB4R~qJ7Ldc= zfJY`@_qQ;B_wYj&ML}~XXkhBqh8EaLHPEJXc$tpqQNyKuKzr6f`y$~YewwJ2Gh70+ ztr)Tq2x1|4V=qPuM#>@J-D=Qs203dWR|pWJpg9a=6{r&mOUaN~5zt~xh*6NOHXv1? zaR5Xp!55K$7Qe%mDZ@-go)1IsSA){*s|^!C;|&vLfSX<5$b{Q~XiS2g0vZQ^v<`I? zP$ss(TX+|&Kw7T_vmK=)3p&xFWdmqZZrK{p>>mDZE~LI@04-Jmb%~L-E;GE^yZ{ub zv$lXX=74t7Sb=LS_+bUm76GJ$0Hp}z2ee>nDyYZ!X73CQ@bLg$OTZ}vBm-$)fyV<-G|Za_ni&CA>T|b%^?^2OLuS@N z9SYb=SEOFc>+Thx{qt{U>_A!@2d^U$WeJij*cG5<1`MF906?_|c%}o?qX3PW^sE3~ z2LKAC{ts1H6n8)Di$sxgyW)aVbEDK0rMeQ1HxK3SCqScKiwj z;%^ZE%?@-gK&m7`qaWb0%-5SB>l4A_$DlSHXeJE2KybxmQIG_3*I3hJWH?E&p}L7oZ1ezyR$V1{%CK$=054xrcrEj`>d1r!Y^^R}R!rf@%l zS4+%V30qqQ9>@ZP5n>hHt4+<|47;cs5&7WF3nc4O(0DVWL%~V`%#flr74o&5$2 zT<~BYXebCg{sdl!1&O;?pj}PiTnI`Y5HXZ~8u(%YcohWl$m_|VlZBwOmtac}@ei^B zzS|Nqn+-|3ubZ}j&Q}4o06|;r=71I$L&q*53pY?U>x0%`LUw5?yqOMKQVlW})a?Te zw=aNpXLZ1>)18ooC&+Cv&>(?}k29KE;pq;Rf)Ke9oZ>)xznj5jI}5JA5>s~`hF1AASXeVnIKlifCsYR zYso=nEO-$)^q3Tdx3lJh7Bj<(8PHJY>!vvhrf;@%fEF6PnF?O^{kFgJ)%FFTd3ErL z^eHW%9j~Be+8r~H19N!;9=-6ve0U-OxgJ^vLbm#W+85gz;Qj-rvp4fv5M!yJLIBcx zB2q7Gf33pXuAOfdOoh%RfagD2AVUGL+z4)_fg%Vb30_VEvJBFW1*MPWJ~6VfsHSqpFaXijEd0VM9>ih$h}l>LP0KExR7N*lkaM8UhnD+q z7H$EZUIscV6Vw<4Z>0mDcL!Dq_9C=&17&YeaG;OnfKooxOvt$#po#g{z3V_He}T8L zfyzlN?&AU_?D=azIRv`*98&#)%3jcL=fW)t2&*8j1zQC^dgAqj8KAYkpaGFhTaa=N zNCnK82Jj+B&@d8YumjW{gSLi1Gew}YTEMHI;6p79D8(FTV-j>fJ7hQ(QWS!0Kn{7N zxSrXd@Otw^&;Swm923aGiMc&oZ{YPW#0UlGLOxKYLhHDL=I=q(?KJQxF=7KNXq)Mr zJ#U+qyk5CM;qA1gZ)Zaf?SU?MfTqj^EpKNufEETpmI;Ge>fl)?@X0x_>S=Bd7pUa} zALRi12YbG!(p z;4p&L)u4%YL<<6;2Hb;(xC6AS2J;+BP^;_h%o*U@96)P-F-{kO2!eM#?pO$#Q~`HN zH#CCgI6%1^v>bBFT-1|6K&O6y)|s>|gpO;&b6E?l2@RcXgJf4wDG%zkFKBtwwvYj| z>}?sS(ZlejX%9G0z&4nJXLx5!Wq7@Q$E&Stp<9+gsRCMK!#XdZ9FFK=g3ru>#J>Xg zS_d!zzS{wkq(LTtnxdeyZ9tt1$m0Q(NM5C>9sFuZ|I`-2;h3XsJgsIy0)#0?#d z*#TO+1Kx_bp&y)=x_7)@w&rd31_kJi77#;0Gs>WLIr3Qz4B$QAu(4L?*d=0pIizld zucd*O1dzFU>>Ft?^}-BSfHXZpi?=`G%XFa1WG}3JVY8)6``M?2#PF7qXKjm z7sykvD=r|1Loz@p(C{aCtR56lZ@{Y?U{yaT;E}eZgDUEkTM(5ksLpz|bI$7xQ$d~D zWotk~)XRH78;qbUk3nk|nj5i>^C-O8GY8bG0xeO;G7i{(+z+I{N8W5&tpGYE zX!#Bl_ka>FsFQ~@xB%V93C(B7R)Cxc$)=#>h$H!Mflhrs#ot ze9P8=IudWTO$BwFK-mM-0|gZs(1U!M*1g&UDh)t$o1m%&wB~Vn2h;>m-xxf`12G-6 z4{6p)P>ezj&Vs}^s2u}p;lo#`fI8pcgavESfX7xr(ojc3_bY?TUsx{*-daJN;RBj( zdbObiKBc||TpNH}Rv3fG&|M6m(hUE~3tq5*A%YlbcBfK^4XZMmT61ZB|8o8jXsdT{r` z+nd$3Xkwtk80sL<>hI>cZ(6{szcEHdUVwY(TecP!@}TKXhTN$ebIMyEodQZb?xhIEetRcbg>2i&TWnd6uU|`4-g}9?p z6ymN4q7ZYYi$dJLQj~!~oq>Vjm?*?O@1XKupz7Jg7#L(37#MiP7#MgN7#Ot07#M^Z z7#J+YAnx`RV_;xsU|`N4fxU*Iq;{P^rh&jFDknouf6<;L|@yAA}`8%Qd_KQQ@djcwdMVx^_fPsPG4piM+ zaY%f9hMM;Ss*hU&qEAEuVvm9Z#2>~IkZ^R8fP`a!1jKzQ5)gOgN-!{pGcYieOE55q zF)%RnL*pIG9R7{Ot&+3=C=v3=DInA?`l{rLRas;^m1n z1A{UH1H%Vti2p@oAnDLf1`;Mi-CbbSB`-piGhKkLXLqU zgn@zKwj2Wk69WT-fjlHUjpZTkw1x6L-=2g;ug zrRT^)+_6xefkBdifnk?CBwnw`L&~F<@(c`i3=9lx3J`N$6(Hf`rvM4ZPz6Z3%v6B5 zvswY-zB;J-E(J(@Ojdw|_iQMCA(X!o%3rSliTCXa5cfQUivLo8ge#LG#Qg$_5c{PS zA>pB<2=S+mA|xDapz@B25dU~6LdxMJsQz|Eh&v`h=^0S<^AsWRv{Vt|-Ytp{f9z3& zlxruT;#UGZB51497=0|TQH1A`s|14EG#1A`I+1H(ckhor@|Y zKYFP$FlaC^Fr-4o`&1$2-xO5_1|0?lhRsm(9zgZKQH7+7k5KhrK=v~-F#Lkr!>kVR zFPAzb{1wz8;ij(+2@flEh`W8D{1A0WxTUE>+*_#*ac`YE#9ysYbNZn4T&VeL)FI)2 zNF5SC*VQ5Q(H|%+p#ibaL<3^Kn+C*Pff@`9S_}*f=^6|SMhpxLlQbaVb`+}qvcLi!f{1K%IF+T}P=R(CRpmc*Kq<-(vgv8%6O^A70G$Hx% zJk-25nvn4N3f0G=1>y5(LDHMF7Q`M^El9a+pap4HI6}oUq3TPtAnC7J3*ydIS`hzi zhMKcW3lc6zpyJoGApW=urJq9ey@lHI4JyvA4GC|4ZHRgmZAg6SXhZV1AynK;8;T~^7(6Rh<{miAn8#+2jX9S z9fp4{K!4pe@*4kUip>p=YRR0k3tuXG^pW7mbW z6O?oz?zYl})EgeU5dX$Q=?Yzl`zGo_+_ym&;+`A25O>_yg}C#%E+pPQ=t9a_Mm-2E zss~Z8smH+J3#u3NAoWI_9s`3v0|UcJJxKdYQXituQXgW!gFXX;9|HqJq&~!+75b2H z+@R0EUEP z#N2BJ5dS}b(%+%te1;HrNfx{tUniL|Foc2n zE2fa}7BGX@CuIgnhl)_z(hTCxKr@IvA!ZPFq?$qW<(WakyTT0Ok2H$_(17k zD~SD3P&&zqf#D_t149;6-o+Y{F1@W87~B{b82qgv;W*72QZH|{hM4!k8seYtP(FtZ z#Gm{&kaQt!15u{{<*VC3!b`^n;%_4xi1}eq@wqk-cQ3Yqln3EhJs& z+d|xDWXr(7$iTpGhJk@$3IhYfK~NtK(uIbT4GfUD1GP&#q5W-;vYf!0-+v2*pns7#N-~FfcfP+G3#oAXI!h0|P@KsGr8b zz|aO2JH`M>1E79HBm)CO4+8_kG6n{QUIqq+wG0dl^BEWzJfUifp>z}j1H*d;NZzq! zU|`t9z`$^Yfq~%-sIAVxz!1v7z+lY)X%kj5Ffg28U|{Hm>IG4tz9NW*;fD+i468x? z9R>!5BMb}-k3eaIfq@~Pfq~&X1Eg$hWME*p4(dyR#wi#W81{ksF;D|QWp)Gu1A{Y^ z52HYBcu;?I5=?-Bf#Cp@?a08uunp8kV}O)ZAmJShkn-CV#DQW^dA}RV291qCnc)2D z0?J#Ua*%<6VF?2R19)E9F>3Xw2qOa#;}1_^=s zp$|ZH3IimMg2X^&IcVIY3&cUf(V)5%)Xzr}12GRVKhXGO^fYgF;HUk60RS<)Lf#ER&1H)n{8`OucVt|yr2~aUmdmY3~W?*2b z2DRN87#Kb>FfdGJU|@I+>MMXGL3KQo22r5CC}{la9s{JD0g26KfRv>>LG?BR1H%ml zNSl8t0|P@iNC5)_!wb;Z00X2fs$*baI13eDz`($8je&u|3CahJNr1)?Kz)^243M(v z9B4d^fq|irfq@~70aD+-0*yI9^|XN6dkhQ=r=ffg1_p*iP~8aSgQz?ZfrLS2@h&JI zG%mLl%D&40X%B(MG@wEZ#taM$7Z@P@-HTAc{h+o#bbJvc^a(T;0%E8+CgqowFsM1E zq$s2(=B6qXB$i}AxOu7N3aNP|MU@O{j-@G?`3jB!o-k%{YDr0EUV1TzUy{$D=9HOS znv|IgV&r7zr6v|Js5u9OGN?IcWacG8sMO+A1~upWyyEZQj3Zi)Lcpvb99sQ zi&7QfvPB9SX^F)pItrzE#U&t}ikcvGxdjkXA-S}uC^fG{A;{4eBvO)`!Jy`v2U4iu zlwT6AkX)2loS{&jUzD9#lwX>c0+$T~c^A&h&jW?KLT-LaDo89nC$ku2P-#(q0Z5=C zvxGs-wW1)kC^I)TuOu;tK@AkX3~Fvgsd*qP-AZ$E6pAbJk`)q56x<_>T#6FQUGno% zH8dI2Kyt;&MX9NI$ZUn;=rg~a6K(xSxVN(MFe z2qQ=|xTof&7A58=AQFyyYEf=t9)p^DQDQ+xX0k#-VsdsdgPMntnF&OTM`}(1gPKQX zdWM2VaZY}@CWD$sesKwMv?*wS!#OuKFC{falR?chub{L9q8q|QSmy}}{+t|0LMSar z0VRt3A_W&8X9hLTlEj=$kXyVG3*Zzuy?Upn7APbZ7o;YaC=`KG6@!|0eo<;7l*(gJ z^T|(40g>tH;IQz?FNZ|9CWD%9YD#8lE{F!F+2Yj1qT~!vTJy~Z#gd0ba!y~PY_0r^EGrRk+$hXkdj6{QwuK$PZW7MC!nAqy3QxViadsjwUy1PYQO zh0=oJ%#>8!l>G8M2DPBl;$pBjgA>bAA)yqUoS3KJ=jN>pYHO5LfT z&;WZYv^Z5E$R#A$nL!Q2ECvS;s2G4}{jki`a)p2ZXN43{$&n5U3%F1TxO^&RPy>r3 zf(ui)GMkkAQjkf>Iho1X3MKgpi76=xi3;%2*N(w4xg@hJmBBG3r3h3QI;N$83dl+Z zNU01@;!gRX+)+@JnO~GyQmLS!YpBV9A!e${;GCF~nFNYl2Is^)^%8}&%)AtZl8jUZ zrJVe{^t{B})cDjqC56m9uwZ&-S!$kwQf_KVqFzOAjuI$HQuQ#jmSmRXq{e5akf*OK zwWv5VpGZsdL0JamUQkj6yCgX^2jrl<{1OGQBuIk6IW@ObA-5D%V1SC_Vo)Lj75DiH zi3J5Ym0-^mr{<(4gAzliC%j71V{pz$%*#v7DYjy8&H+_rU{WD5CkIwo=Tw5qF@;QU zX#)>`=ls01%=FSCNF7?t;GCaVQk0*QlUf8yYgR~n5W6%d1r+!>`H7IghdTvY4k$7> z=R?&LgR(2k8ilm{B86mx(bWdEXevQLgD?bIy?}zmior#kiQxSYXqnR)48X{fkDd1g+ILJFj=1vwuSLz$^53J~pj46eDQ zIiS)A5u{jztQcIutrgdzqWmHTFsb02pOR_?VOue{R+OaXfznyA6@yzMxaKa&SI9{$ z%}dUJCTq9M98i9D4+vram3&|V+$1WtVnD5^JoD1>89+o)E~s9}V}MjR!LWuAw7voP z6x6iP)6-)>5`uV40U}zKn3I`;nZ-QwAQEZ$IVq_{3dJQwrOBW|kioM=p*S@)w^*Sh zBe6sw5mbjMWELwV=73s3l?s`Vnl3d(M8T9RrUye%YH?}_I47s1f(r(P;u26k%})ah zB8n-HN>Bl;ke{XiE|`m;-T_IJ=9Cn}Xa$XGgIZ07pv?3PXgmg$<|gH&G6aVP2e|sV zxVkU|C#sdDBDK{)tr-SL6hbLbrUMm|<(Zke`6a2vpllWlF6tE$5gNgzXkum_NHwVI zQ>@1joRgWH3Tci6mn0@{L+0gdw=J2;53nFx4|;2rexyNX<)O2uUq2QOHCO5>S>+&delx&b$tdI#QrWBD(L{|c`8=T}pia>=FI7@~4 zxwyJ{`nkF=ghKi|CHaWzTaO_$FFP;4JWnANRGcg1gSr%{DR5CpvaT#h)nkCR-k`-A zxG+}8NGw)JN=?lJ`8+u_2b57!mBB?ofd`6x1yG|Jlu*JTZ4rjB{G8I zDk2pUA&sx1)HG1Hu_Pn4C>0a~i3&xT#o3_jk`Ad+6p}!Vk`z4!|Gb<^P+63mnwkPi zRiFwOWId=31$m|4RfY1*k_?cOiWI^#GZjKXw(I8?Dfsz$y67>4 zCl-NH9YbVlF+*g2DJbkfc?8<10S6LjbO03P;Ow1_)~C}0=~6FCg_XsT`K3j$f>NO@ zF{c#lGk9VEw`5T2gOtRQ#1e3vCPLe~NjdrD3b~od4B*x=LrQ9Ka#1F@f5ZUp)1(!p zrdFjgxXP8uGu|je_QicL07HBF~ zD9)@(WherbJ`BaFsRc#(xwuS2mL_OoaYlZ*0<;ee@*zWM0RuQhK*elUYI2FLXNoQ~ zWUKciqleybY1h3^HVbO(ybIMk}^xUB6K0W_>%l0D}{jk)RfE;1tUEJJqxaYoYJDi z99_5kqTFIDg}eeVx7gOuS^>heHL&Ia)zZ2lAfs~6iF&oE=gqtp zg;$&QzFs&-;mx)wuQoK`8<2W4W6A3&Q{K#2qVT3=$(sdJ8Pwj)oAA1SCTJ`QJSe50 z@n&A@>kTV4K_Z}0lEfkfjW;a|-p-h+iDQ`R&E6Sr+NLPHnYQE2v@Hy3Fz)h(H`CW5 z4Mu^6P^d6O1@-aUDJ`!T&UrhvMd9`Gp4XF?yxOsWLG4ZVjyH3+ylvc~@M_0~*BfRi zylrlHGk44D6+0k9Rj`rd-gHg?8Q42T;q}gzw=?IwY1#O8<`f3C*OMFGOxvUIX77|Y z)3zwQM%Fcdje^GOoeN$sYtd17y{+TT)Q&d`Kw;@pig#GcDZiv#J`Fylq_bdftpT-E$P)PTlfq(}p)2TNRMp@up+Oo8^tK`)9t|vEl8^DJW7i zR=l3O=gr(53a?kKcs+RzvYcxk+8EgDrWvm`G(ZD&$=m5O-YjTjP=ka6VjK)*=t}|Y zs7Vb9Z>H>h+qi~7?d_z7H(g6W4qgU|n*}XzHf?!3bIR+LTNK`OEqSx20Tiao8zDnt zuQ#>4?cJmBX4eFeF>e-3eZ8UKb$<)epcqK?h8gg53mXx8wSC9yc{AQl*{krjXC?B8 z*z5k5H?tdFFQ34m_PTA-o4Gv-8n3%1yk53PlR@q6j2$4Sb?v}NC<+>{w)MQ3xAgUd z6{tgGplq>VDcpK!ig`1=^>yzCP@?PFq40Lb&Np)#z_?%Fv6-(cCBMXhgVEquMR zMd8hy1#gx&KnKa7R33xc>xFY(Po9G`TK2kW9mE&l#H7if_GZb%R~uRw)LyTg0*caQ zYZP8>YIxJ!363_92uM44;0)?oP=bbJnm5z8fK|PjvGq;s4AjvxsN&bFR=!?1SK&=_ z$Loc2UTt3RX6FjfxEW$(4PzMX^@5()y;EKK3Wt%aESr6331P!je-VAj*EGvTY{l-^2 z8eqBa&DQfW}7@ z-t3*jpoWt8K&2U?Y;czR%^eDF zr>=dyY9+MV!aa0{FddYw7EHw)sKYUM_iF0|P#yPX@t!x+w!B&1f;?>SX8Ht$Hw&h| znL7iNxKKy)!0C6xJ}}iWL*dQzwl`f{Uw3!B?p~tDK#XaK0X|Uhy`kJFQ0o^w=7-lj zNG1Y1d-@iIH<(V}zT!<=4+C~(b9)#-^?dVOP03a>Uy0;^xp@Mhl9w^J8j1SM!#fdSPDoN5&r zAijncaaIhkH>`X+ZK=ka?zL~`P0&<$+q48c$oRT z4U-t&K-w)R#lox2d%&r@1(eudFP{L;df;TB@OCPw9r0?@0!ZS1wX?^bi|Y+U4x&xr z&60_4rcVGzH8>4`k_NKzuQoI&Kx}NW-l1#LE?4_1O2ADg!@z$oN|3W_Lj6JY0zH%lghM_Y={zKSTwxt-}1V5io&a{Yu+r}zyNA|uHOO5Zm+v{f?68j zxM^APX6FnAP%SZSi^7|E9Uwt)WI&4A*PFM%qeVyI&Ga>I7PP#b*8z3y+o>}^NoeUF zg*US&fE?Ss_H}m$C^f)r1KIzkd%>&ibHI57smTM5f6{E9(eY+Mi^A(oEpHa=0UHO_ z4lZU84#RLr%YwI%f(bdeLEeN02&iHpAqeKJfoK3*i*A`u5CfzyW5w`#dB>aO4X>AX zLMyGeQ+gojS>f%pj@P|2KuMnA&7z(+I~FRu**28{)P9*f2TV;_`DV#P9dP@yd&%ns zOB6u$M$3*jv!);osbXunff{bDI~ZQCTJffJh62dO`D@;EEkPPyg%>;uAm1-*c{3Lj z>!9)!lmwt@ZR!k2wtn5cL=m~Dnt`kd8mzF^#T#6jwkW`=2JA}TU`e>3_8n}%7LwE% zAngu?R~s5$cP)K8bq+W!&RL-FYV+1t8^GRpy{+NR!j16aUytkU)Rs51rohK>Atpc! zfDh%q?&x^4VFjqq0UgVQg9lHE(9DftJNEapc>aNUz$P7Etf^^^P6|P$LZ97K95k zKzauZZ|1bTUIng4Coch2a}Z%rQ9rL0T6OE`L5oO`{M-IDZ>O#RMHjNUuR#R{IQ@Xi z3~<86IV23~Yqf%N&Wt7K1H*4xHon@tl>vEN_)W_kP)~K%7EmwMl*F-NXk`lu#r~Nf zPtDz-04eT4#qaCQ4R0210A-Wu9iYZ-(+q_-Gh0CA;Z%6ahBx&TG{9rTU;;EytnhkU z%bV^cnhKyk3%t+y8Wf5%6yAUICV5PVT&JngJ@*K{-U> z&D56H-7}#Pg4F7Sr$^+BjGq5seo$lpmHZ2qf+}uIjSZl_Cu$Xi&%f~ub9?j{-c0R*@xkTtj3uv^&QN&0Va}W8x#*G*Qxx9LnEIxBt{ww&nGVho;3@%J zw?JAWkoZC>&yaP1)q!<@YryFpNa`RFuK?@HBGp`=rVVHS8&VN5yxzR$_0knk3Ne2D zYSZ3Vn->hn;cJM`7~ah2faY{)V+h-NXtf05(O9H z@Yeq8?wxPC7l0BosLlen`jBf$3dXlVHD_B7xH1Nf?yUh0qk(%9;Qk~j9KrYBfA(X{5x+%0dGfigGle1l~;9G|7g Date: Fri, 8 Sep 2023 02:09:03 +0200 Subject: [PATCH 019/101] Localization improvements and fixes (#956) --- src/Cafe/Account/Account.h | 16 ----- src/Cafe/Filesystem/FST/KeyCache.cpp | 9 ++- src/Cafe/GraphicPack/GraphicPack2Patches.cpp | 13 ++-- src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 5 +- .../Tools/DownloadManager/DownloadManager.cpp | 16 ++--- src/config/CemuConfig.h | 19 +++--- src/gui/CemuApp.cpp | 45 ++++---------- src/gui/ChecksumTool.cpp | 40 +++++++------ src/gui/GameProfileWindow.cpp | 4 +- src/gui/GameUpdateWindow.cpp | 20 +++---- src/gui/GeneralSettings2.cpp | 44 +++++++++----- src/gui/GeneralSettings2.h | 2 + src/gui/GraphicPacksWindow2.cpp | 8 +-- src/gui/LoggingWindow.cpp | 2 +- src/gui/MainWindow.cpp | 59 ++----------------- src/gui/MainWindow.h | 2 - src/gui/MemorySearcherTool.cpp | 24 -------- src/gui/MemorySearcherTool.h | 2 - src/gui/TitleManager.cpp | 25 ++++---- src/gui/canvas/VulkanCanvas.cpp | 5 +- src/gui/components/wxDownloadManagerList.cpp | 27 ++++++--- src/gui/components/wxDownloadManagerList.h | 21 +------ src/gui/components/wxGameList.cpp | 18 +++--- src/gui/components/wxTitleManagerList.cpp | 52 ++++++++++------ src/gui/components/wxTitleManagerList.h | 25 +------- .../CreateAccount/wxCreateAccountDialog.cpp | 3 +- .../dialogs/SaveImport/SaveImportWindow.cpp | 20 +++---- src/gui/dialogs/SaveImport/SaveTransfer.cpp | 8 +-- src/gui/guiWrapper.cpp | 2 +- src/gui/helpers/wxHelpers.h | 19 +----- src/gui/input/InputAPIAddWindow.cpp | 2 +- src/gui/input/InputSettings2.cpp | 15 ++--- .../DebugPPCThreadsWindow.cpp | 6 +- src/gui/wxHelper.h | 7 --- 34 files changed, 229 insertions(+), 356 deletions(-) diff --git a/src/Cafe/Account/Account.h b/src/Cafe/Account/Account.h index 63eb5082..da196e42 100644 --- a/src/Cafe/Account/Account.h +++ b/src/Cafe/Account/Account.h @@ -16,22 +16,6 @@ enum class OnlineAccountError kPasswordCacheEmpty, kNoPrincipalId, }; -template <> -struct fmt::formatter : formatter { - template - auto format(const OnlineAccountError v, FormatContext& ctx) { - switch (v) - { - case OnlineAccountError::kNoAccountId: return formatter::format("AccountId missing (The account is not connected to a NNID)", ctx); - case OnlineAccountError::kNoPasswordCached: return formatter::format("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)", ctx); - case OnlineAccountError::kPasswordCacheEmpty: return formatter::format("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)", ctx); - case OnlineAccountError::kNoPrincipalId: return formatter::format("PrincipalId missing", ctx); - default: break; - } - return formatter::format("no error", ctx); - } -}; - struct OnlineValidator { diff --git a/src/Cafe/Filesystem/FST/KeyCache.cpp b/src/Cafe/Filesystem/FST/KeyCache.cpp index 5d8d51c1..29903e84 100644 --- a/src/Cafe/Filesystem/FST/KeyCache.cpp +++ b/src/Cafe/Filesystem/FST/KeyCache.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "config/ActiveSettings.h" #include "util/crypto/aes128.h" @@ -74,7 +75,7 @@ void KeyCache_Prepare() } else { - wxMessageBox("Unable to create file keys.txt\nThis can happen if Cemu does not have write permission to it's own directory, the disk is full or if anti-virus software is blocking Cemu.", "Error", wxOK | wxCENTRE | wxICON_ERROR); + wxMessageBox(_("Unable to create file keys.txt\nThis can happen if Cemu does not have write permission to its own directory, the disk is full or if anti-virus software is blocking Cemu."), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); } mtxKeyCache.unlock(); return; @@ -107,10 +108,8 @@ void KeyCache_Prepare() continue; if( strishex(line) == false ) { - // show error message - char errorMsg[512]; - sprintf(errorMsg, "Error in keys.txt in line %d\n", lineNumber); - wxMessageBox(errorMsg, "Error", wxOK | wxCENTRE | wxICON_ERROR); + auto errorMsg = formatWxString(_("Error in keys.txt at line {}"), lineNumber); + wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); continue; } if(line.size() == 32 ) diff --git a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp index 578b55db..7fa1e7fe 100644 --- a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp @@ -6,6 +6,7 @@ #include "boost/algorithm/string.hpp" #include "gui/wxgui.h" // for wxMessageBox +#include "gui/helpers/wxHelpers.h" // error handler void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumber, std::string_view errorMsg) @@ -39,13 +40,13 @@ void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumb void PatchErrorHandler::showStageErrorMessageBox() { - std::string errorMsg; + wxString errorMsg; if (m_gp) { if (m_stage == STAGE::PARSER) - errorMsg.assign(fmt::format("Failed to load patches for graphic pack \'{}\'", m_gp->GetName())); + errorMsg.assign(formatWxString(_("Failed to load patches for graphic pack \'{}\'"), m_gp->GetName())); else - errorMsg.assign(fmt::format("Failed to apply patches for graphic pack \'{}\'", m_gp->GetName())); + errorMsg.assign(formatWxString(_("Failed to apply patches for graphic pack \'{}\'"), m_gp->GetName())); } else { @@ -53,7 +54,9 @@ void PatchErrorHandler::showStageErrorMessageBox() } if (cemuLog_isLoggingEnabled(LogType::Patches)) { - errorMsg.append("\n \nDetails:\n"); + errorMsg.append("\n \n") + .append(_("Details:")) + .append("\n"); for (auto& itr : errorMessages) { errorMsg.append(itr); @@ -61,7 +64,7 @@ void PatchErrorHandler::showStageErrorMessageBox() } } - wxMessageBox(errorMsg, "Graphic pack error"); + wxMessageBox(errorMsg, _("Graphic pack error")); } // loads Cemu-style patches (patch_.asm) diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index 55bf4b8a..9576eb2e 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -792,10 +792,9 @@ void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path if (hasOldCacheFiles && !hasNewCacheFiles) { // ask user if they want to delete or keep the old cache file - const auto infoMsg = L"Outdated shader cache\n\nCemu detected that the shader cache for this game is outdated\nOnly shader caches generated with Cemu 1.25.0 or above are supported\n\n" - "We recommend deleting the outdated cache file as it will no longer be used by Cemu"; + auto infoMsg = _("Cemu detected that the shader cache for this game is outdated.\nOnly shader caches generated with Cemu 1.25.0 or above are supported.\n\nWe recommend deleting the outdated cache file as it will no longer be used by Cemu."); - wxMessageDialog dialog(nullptr, _(infoMsg), _("Outdated shader cache"), + wxMessageDialog dialog(nullptr, infoMsg, _("Outdated shader cache"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION); dialog.SetYesNoLabels(_("Delete outdated cache file [recommended]"), _("Keep outdated cache file")); diff --git a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp index bb8eaa92..200d1641 100644 --- a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp +++ b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp @@ -371,7 +371,7 @@ bool DownloadManager::syncAccountTickets() for (auto& tiv : resultTicketIds.tivs) { index++; - std::string msg = _("Downloading account ticket").ToStdString(); + std::string msg = _("Downloading account ticket").utf8_string(); msg.append(fmt::format(" {0}/{1}", index, count)); setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); // skip if already cached @@ -508,7 +508,7 @@ bool DownloadManager::syncUpdateTickets() if (titleIdParser.GetType() != TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE) continue; - std::string msg = _("Downloading ticket").ToStdString(); + std::string msg = _("Downloading ticket").utf8_string(); msg.append(fmt::format(" {0}/{1}", updateIndex, numUpdates)); updateIndex++; setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); @@ -561,7 +561,7 @@ bool DownloadManager::syncTicketCache() for (auto& ticketInfo : m_ticketCache) { index++; - std::string msg = _("Downloading meta data").ToStdString(); + std::string msg = _("Downloading meta data").utf8_string(); msg.append(fmt::format(" {0}/{1}", index, count)); setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); prepareIDBE(ticketInfo.titleId); @@ -1054,7 +1054,7 @@ void DownloadManager::asyncPackageDownloadTMD(Package* package) std::unique_lock _l(m_mutex); if (!tmdResult.isValid) { - setPackageError(package, from_wxString(_("TMD download failed"))); + setPackageError(package, _("TMD download failed").utf8_string()); package->state.isDownloadingTMD = false; return; } @@ -1063,7 +1063,7 @@ void DownloadManager::asyncPackageDownloadTMD(Package* package) NCrypto::TMDParser tmdParser; if (!tmdParser.parse(tmdResult.tmdData.data(), tmdResult.tmdData.size())) { - setPackageError(package, from_wxString(_("Invalid TMD"))); + setPackageError(package, _("Invalid TMD").utf8_string()); package->state.isDownloadingTMD = false; return; } @@ -1172,7 +1172,7 @@ void DownloadManager::asyncPackageDownloadContentFile(Package* package, uint16 i size_t bytesWritten = callbackInfo->receiveBuffer.size(); if (callbackInfo->fileOutput->writeData(callbackInfo->receiveBuffer.data(), callbackInfo->receiveBuffer.size()) != (uint32)callbackInfo->receiveBuffer.size()) { - callbackInfo->downloadMgr->setPackageError(callbackInfo->package, from_wxString(_("Cannot write file. Disk full?"))); + callbackInfo->downloadMgr->setPackageError(callbackInfo->package, _("Cannot write file. Disk full?").utf8_string()); return false; } callbackInfo->receiveBuffer.clear(); @@ -1193,12 +1193,12 @@ void DownloadManager::asyncPackageDownloadContentFile(Package* package, uint16 i callbackInfoData.fileOutput = FileStream::createFile2(packageDownloadPath / fmt::format("{:08x}.app", contentId)); if (!callbackInfoData.fileOutput) { - setPackageError(package, from_wxString(_("Cannot create file"))); + setPackageError(package, _("Cannot create file").utf8_string()); return; } if (!NAPI::CCS_GetContentFile(titleId, contentId, CallbackInfo::writeCallback, &callbackInfoData)) { - setPackageError(package, from_wxString(_("Download failed"))); + setPackageError(package, _("Download failed").utf8_string()); delete callbackInfoData.fileOutput; return; } diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index e90874ba..19d9ca0e 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -6,6 +6,7 @@ #include "Cafe/Account/Account.h" #include +#include struct GameEntry { @@ -258,15 +259,15 @@ struct fmt::formatter : formatter { string_view name; switch (v) { - case CafeConsoleRegion::JPN: name = "Japan"; break; - case CafeConsoleRegion::USA: name = "USA"; break; - case CafeConsoleRegion::EUR: name = "Europe"; break; - case CafeConsoleRegion::AUS_DEPR: name = "Australia"; break; - case CafeConsoleRegion::CHN: name = "China"; break; - case CafeConsoleRegion::KOR: name = "Korea"; break; - case CafeConsoleRegion::TWN: name = "Taiwan"; break; - case CafeConsoleRegion::Auto: name = "Auto"; break; - default: name = "many"; break; + case CafeConsoleRegion::JPN: name = wxTRANSLATE("Japan"); break; + case CafeConsoleRegion::USA: name = wxTRANSLATE("USA"); break; + case CafeConsoleRegion::EUR: name = wxTRANSLATE("Europe"); break; + case CafeConsoleRegion::AUS_DEPR: name = wxTRANSLATE("Australia"); break; + case CafeConsoleRegion::CHN: name = wxTRANSLATE("China"); break; + case CafeConsoleRegion::KOR: name = wxTRANSLATE("Korea"); break; + case CafeConsoleRegion::TWN: name = wxTRANSLATE("Taiwan"); break; + case CafeConsoleRegion::Auto: name = wxTRANSLATE("Auto"); break; + default: name = wxTRANSLATE("many"); break; } return formatter::format(name, ctx); diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 0df90659..03496305 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -38,21 +38,6 @@ void unused_translation_dummy() void(_("Browse")); void(_("Select a file")); void(_("Select a directory")); - - void(_("base")); - void(_("update")); - void(_("dlc")); - void(_("save")); - - void(_("Japan")); - void(_("USA")); - void(_("Europe")); - void(_("Australia")); - void(_("China")); - void(_("Korea")); - void(_("Taiwan")); - void(_("Auto")); - void(_("many")); void(_("Japanese")); void(_("English")); @@ -67,13 +52,6 @@ void unused_translation_dummy() void(_("Russian")); void(_("Taiwanese")); void(_("unknown")); - - - // account.h - void(_("AccountId missing (The account is not connected to a NNID)")); - void(_("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)")); - void(_("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)")); - void(_("PrincipalId missing")); } bool CemuApp::OnInit() @@ -110,7 +88,8 @@ bool CemuApp::OnInit() #endif auto failed_write_access = ActiveSettings::LoadOnce(exePath, user_data_path, config_path, cache_path, data_path); for (auto&& path : failed_write_access) - wxMessageBox(fmt::format("Cemu can't write to {} !", path.generic_string()), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); + wxMessageBox(formatWxString(_("Cemu can't write to {}!"), path.generic_string()), + _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); NetworkConfig::LoadOnce(); g_config.Load(); @@ -288,9 +267,10 @@ void CemuApp::CreateDefaultFiles(bool first_start) // check for mlc01 folder missing if custom path has been set if (!fs::exists(mlc) && !first_start) { - const std::wstring message = fmt::format(fmt::runtime(_(L"Your mlc01 folder seems to be missing.\n\nThis is where Cemu stores save files, game updates and other Wii U files.\n\nThe expected path is:\n{}\n\nDo you want to create the folder at the expected path?").ToStdWstring()), mlc.wstring()); + const wxString message = formatWxString(_("Your mlc01 folder seems to be missing.\n\nThis is where Cemu stores save files, game updates and other Wii U files.\n\nThe expected path is:\n{}\n\nDo you want to create the folder at the expected path?"), + _pathToUtf8(mlc)); - wxMessageDialog dialog(nullptr, message, "Error", wxCENTRE | wxYES_NO | wxCANCEL| wxICON_WARNING); + wxMessageDialog dialog(nullptr, message, _("Error"), wxCENTRE | wxYES_NO | wxCANCEL| wxICON_WARNING); dialog.SetYesNoCancelLabels(_("Yes"), _("No"), _("Select a custom path")); const auto dialogResult = dialog.ShowModal(); if (dialogResult == wxID_NO) @@ -362,16 +342,15 @@ void CemuApp::CreateDefaultFiles(bool first_start) } catch (const std::exception& ex) { - std::stringstream errorMsg; - errorMsg << fmt::format(fmt::runtime(_("Couldn't create a required mlc01 subfolder or file!\n\nError: {0}\nTarget path:\n{1}").ToStdString()), ex.what(), _pathToUtf8(mlc)); + wxString errorMsg = formatWxString(_("Couldn't create a required mlc01 subfolder or file!\n\nError: {0}\nTarget path:\n{1}"), ex.what(), _pathToUtf8(mlc)); #if BOOST_OS_WINDOWS const DWORD lastError = GetLastError(); if (lastError != ERROR_SUCCESS) errorMsg << fmt::format("\n\n{}", GetSystemErrorMessage(lastError)); - - wxMessageBox(errorMsg.str(), "Error", wxOK | wxCENTRE | wxICON_ERROR); #endif + + wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); exit(0); } @@ -388,17 +367,15 @@ void CemuApp::CreateDefaultFiles(bool first_start) } catch (const std::exception& ex) { - std::stringstream errorMsg; - errorMsg << fmt::format(fmt::runtime(_("Couldn't create a required cemu directory or file!\n\nError: {0}").ToStdString()), ex.what()); + wxString errorMsg = formatWxString(_("Couldn't create a required cemu directory or file!\n\nError: {0}"), ex.what()); #if BOOST_OS_WINDOWS const DWORD lastError = GetLastError(); if (lastError != ERROR_SUCCESS) errorMsg << fmt::format("\n\n{}", GetSystemErrorMessage(lastError)); - - - wxMessageBox(errorMsg.str(), "Error", wxOK | wxCENTRE | wxICON_ERROR); #endif + + wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); exit(0); } } diff --git a/src/gui/ChecksumTool.cpp b/src/gui/ChecksumTool.cpp index 7dc61bb3..526ceef9 100644 --- a/src/gui/ChecksumTool.cpp +++ b/src/gui/ChecksumTool.cpp @@ -81,8 +81,8 @@ const char kSchema[] = R"( ChecksumTool::ChecksumTool(wxWindow* parent, wxTitleManagerList::TitleEntry& entry) - : wxDialog(parent, wxID_ANY, - wxStringFormat2(_("Title checksum of {:08x}-{:08x}"), (uint32)(entry.title_id >> 32), (uint32)(entry.title_id & 0xFFFFFFFF)), + : wxDialog(parent, wxID_ANY, + formatWxString(_("Title checksum of {:08x}-{:08x}"), (uint32) (entry.title_id >> 32), (uint32) (entry.title_id & 0xFFFFFFFF)), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxFRAME_TOOL_WINDOW | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), m_entry(entry) { @@ -413,7 +413,7 @@ void ChecksumTool::OnExportChecksums(wxCommandEvent& event) } else { - wxMessageBox(wxStringFormat2(_("Can't write to file: {}"), target_file.string()), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(formatWxString(_("Can't write to file: {}"), target_file.string()), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); } } @@ -461,17 +461,17 @@ void ChecksumTool::VerifyJsonEntry(const rapidjson::Document& doc) if (m_json_entry.title_id != test_entry.title_id) { - wxMessageBox(wxStringFormat2(_("The file you are comparing with is for a different title.")), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(formatWxString(_("The file you are comparing with is for a different title.")), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } if (m_json_entry.version != test_entry.version) { - wxMessageBox(wxStringFormat2(_("Wrong version: {}"), test_entry.version), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(formatWxString(_("Wrong version: {}"), test_entry.version), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } if (m_json_entry.region != test_entry.region) { - wxMessageBox(wxStringFormat2(_("Wrong region: {}"), test_entry.region), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(formatWxString(_("Wrong region: {}"), test_entry.region), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } if (!m_json_entry.wud_hash.empty()) @@ -483,7 +483,7 @@ void ChecksumTool::VerifyJsonEntry(const rapidjson::Document& doc) } if(!boost::iequals(test_entry.wud_hash, m_json_entry.wud_hash)) { - wxMessageBox(wxStringFormat2(_("Your game image is invalid!\n\nYour hash:\n{}\n\nExpected hash:\n{}"), m_json_entry.wud_hash, test_entry.wud_hash), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(formatWxString(_("Your game image is invalid!\n\nYour hash:\n{}\n\nExpected hash:\n{}"), m_json_entry.wud_hash, test_entry.wud_hash), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } } @@ -563,7 +563,9 @@ void ChecksumTool::VerifyJsonEntry(const rapidjson::Document& doc) } else if (missing_files.empty() && !invalid_hashes.empty()) { - const int result = wxMessageBox(wxStringFormat2(_("{} files have an invalid hash!\nDo you want to export a list of them to a file?"), invalid_hashes.size()), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR, this); + const int result = wxMessageBox(formatWxString( + _("{} files have an invalid hash!\nDo you want to export a list of them to a file?"), + invalid_hashes.size()), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR, this); if (result == wxYES) { writeMismatchInfoToLog(); @@ -572,7 +574,9 @@ void ChecksumTool::VerifyJsonEntry(const rapidjson::Document& doc) } else if (!missing_files.empty() && !invalid_hashes.empty()) { - const int result = wxMessageBox(wxStringFormat2(_("Multiple issues with your game files have been found!\nDo you want to export them to a file?"), invalid_hashes.size()), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR, this); + const int result = wxMessageBox(formatWxString( + _("Multiple issues with your game files have been found!\nDo you want to export them to a file?"), + invalid_hashes.size()), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR, this); if (result == wxYES) { writeMismatchInfoToLog(); @@ -584,7 +588,7 @@ void ChecksumTool::VerifyJsonEntry(const rapidjson::Document& doc) } catch (const std::exception& ex) { - wxMessageBox(wxStringFormat2(_("JSON parse error: {}"), ex.what()), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(formatWxString(_("JSON parse error: {}"), ex.what()), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); } } @@ -610,7 +614,7 @@ void ChecksumTool::OnVerifyOnline(wxCommandEvent& event) d.ParseStream(str); if (d.HasParseError()) { - wxMessageBox(_("Can't parse json file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(_("Can't parse JSON file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -638,7 +642,7 @@ void ChecksumTool::OnVerifyLocal(wxCommandEvent& event) d.ParseStream(str); if (d.HasParseError()) { - wxMessageBox(_("Can't parse json file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); + wxMessageBox(_("Can't parse JSON file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -680,7 +684,7 @@ void ChecksumTool::DoWork() case TitleInfo::TitleDataFormat::WUD: { const auto path = m_entry.path.string(); - wxQueueEvent(this, new wxSetGaugeValue(1, m_progress, m_status, wxStringFormat2(_("Reading game image: {}"), path))); + wxQueueEvent(this, new wxSetGaugeValue(1, m_progress, m_status, formatWxString(_("Reading game image: {}"), path))); wud_t* wud = wud_open(m_info.GetPath()); if (!wud) @@ -709,11 +713,11 @@ void ChecksumTool::DoWork() EVP_DigestUpdate(sha256, buffer.data(), read); - wxQueueEvent(this, new wxSetGaugeValue((int)((offset * 90) / wud_size), m_progress, m_status, wxStringFormat2(_("Reading game image: {0}/{1} kB"), offset / 1024, wud_size / 1024))); + wxQueueEvent(this, new wxSetGaugeValue((int)((offset * 90) / wud_size), m_progress, m_status, formatWxString(_("Reading game image: {0}/{1} kB"), offset / 1024, wud_size / 1024))); } while (read != 0 && size > 0); wud_close(wud); - wxQueueEvent(this, new wxSetGaugeValue(90, m_progress, m_status, wxStringFormat2(_("Generating checksum of game image: {}"), path))); + wxQueueEvent(this, new wxSetGaugeValue(90, m_progress, m_status, formatWxString(_("Generating checksum of game image: {}"), path))); if (!m_running.load(std::memory_order_relaxed)) return; @@ -729,7 +733,7 @@ void ChecksumTool::DoWork() m_json_entry.wud_hash = str.str(); - wxQueueEvent(this, new wxSetGaugeValue(100, m_progress, m_status, wxStringFormat2(_("Generated checksum of game image: {}"), path))); + wxQueueEvent(this, new wxSetGaugeValue(100, m_progress, m_status, formatWxString(_("Generated checksum of game image: {}"), path))); break; } default: @@ -765,7 +769,7 @@ void ChecksumTool::DoWork() m_json_entry.file_hashes[filename] = str.str(); ++counter; - wxQueueEvent(this, new wxSetGaugeValue((int)((counter * 100) / file_count), m_progress, m_status, wxStringFormat2(_("Hashing game file: {}/{}"), counter, file_count))); + wxQueueEvent(this, new wxSetGaugeValue((int)((counter * 100) / file_count), m_progress, m_status, formatWxString(_("Hashing game file: {}/{}"), counter, file_count))); if (!m_running.load(std::memory_order_relaxed)) { @@ -775,7 +779,7 @@ void ChecksumTool::DoWork() } m_info.Unmount(temporaryMountPath.c_str()); - wxQueueEvent(this, new wxSetGaugeValue(100, m_progress, m_status, wxStringFormat2(_("Generated checksum of {} game files"), file_count))); + wxQueueEvent(this, new wxSetGaugeValue(100, m_progress, m_status, formatWxString(_("Generated checksum of {} game files"), file_count))); break; } } diff --git a/src/gui/GameProfileWindow.cpp b/src/gui/GameProfileWindow.cpp index 4d56e9cd..17affc84 100644 --- a/src/gui/GameProfileWindow.cpp +++ b/src/gui/GameProfileWindow.cpp @@ -166,7 +166,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id) for (int i = 0; i < 8; ++i) { - profile_sizer->Add(new wxStaticText(panel, wxID_ANY, fmt::format("{} {}", _("Controller").ToStdString(), (i + 1))), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); + profile_sizer->Add(new wxStaticText(panel, wxID_ANY, fmt::format("{} {}", _("Controller").utf8_string(), (i + 1))), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_controller_profile[i] = new wxComboBox(panel, wxID_ANY,"", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_DROPDOWN| wxCB_READONLY); m_controller_profile[i]->SetMinSize(wxSize(250, -1)); @@ -244,7 +244,7 @@ void GameProfileWindow::SetProfileInt(gameProfileIntegerOption_t& option, wxChec void GameProfileWindow::ApplyProfile() { if(m_game_profile.m_gameName) - this->SetTitle(fmt::format("{} - {}", _("Edit game profile").ToStdString(), m_game_profile.m_gameName.value())); + this->SetTitle(fmt::format("{} - {}", _("Edit game profile").utf8_string(), m_game_profile.m_gameName.value())); // general m_load_libs->SetValue(m_game_profile.m_loadSharedLibraries.value()); diff --git a/src/gui/GameUpdateWindow.cpp b/src/gui/GameUpdateWindow.cpp index e90c9dc7..e422cbe6 100644 --- a/src/gui/GameUpdateWindow.cpp +++ b/src/gui/GameUpdateWindow.cpp @@ -16,18 +16,18 @@ std::string _GetTitleIdTypeStr(TitleId titleId) switch (tip.GetType()) { case TitleIdParser::TITLE_TYPE::AOC: - return _("DLC").ToStdString(); + return _("DLC").utf8_string(); case TitleIdParser::TITLE_TYPE::BASE_TITLE: - return _("Base game").ToStdString(); + return _("Base game").utf8_string(); case TitleIdParser::TITLE_TYPE::BASE_TITLE_DEMO: - return _("Demo").ToStdString(); + return _("Demo").utf8_string(); case TitleIdParser::TITLE_TYPE::SYSTEM_TITLE: case TitleIdParser::TITLE_TYPE::SYSTEM_OVERLAY_TITLE: - return _("System title").ToStdString(); + return _("System title").utf8_string(); case TitleIdParser::TITLE_TYPE::SYSTEM_DATA: - return _("System data title").ToStdString(); + return _("System data title").utf8_string(); case TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE: - return _("Update").ToStdString(); + return _("Update").utf8_string(); default: break; } @@ -60,8 +60,8 @@ bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) std::string typeStrToInstall = _GetTitleIdTypeStr(m_title_info.GetAppTitleId()); std::string typeStrCurrentlyInstalled = _GetTitleIdTypeStr(tmp.GetAppTitleId()); - std::string wxMsg = wxHelper::MakeUTF8(_("It seems that there is already a title installed at the target location but it has a different type.\nCurrently installed: \'{}\' Installing: \'{}\'\n\nThis can happen for titles which were installed with very old Cemu versions.\nDo you still want to continue with the installation? It will replace the currently installed title.")); - wxMessageDialog dialog(this, fmt::format(fmt::runtime(wxMsg), typeStrCurrentlyInstalled, typeStrToInstall), _("Warning"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); + auto wxMsg = _("It seems that there is already a title installed at the target location but it has a different type.\nCurrently installed: \'{}\' Installing: \'{}\'\n\nThis can happen for titles which were installed with very old Cemu versions.\nDo you still want to continue with the installation? It will replace the currently installed title."); + wxMessageDialog dialog(this, formatWxString(wxMsg, typeStrCurrentlyInstalled, typeStrToInstall), _("Warning"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); if (dialog.ShowModal() != wxID_YES) return false; } @@ -90,7 +90,7 @@ bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) if (ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to move former title installation:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to move former title installation:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); return false; } @@ -244,7 +244,7 @@ void GameUpdateWindow::ThreadWork() error_msg << GetSystemErrorMessage(ex); if(currentDirEntry != fs::directory_entry{}) - error_msg << fmt::format("\n{}\n{}",_("Current file:").ToStdString(), _pathToUtf8(currentDirEntry.path())); + error_msg << fmt::format("\n{}\n{}",_("Current file:").utf8_string(), _pathToUtf8(currentDirEntry.path())); m_thread_exception = error_msg.str(); m_thread_state = ThreadCanceled; diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 59f0e5ee..0fad827f 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -2001,44 +2001,60 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event) if (validator) // everything valid? shouldn't happen return; - std::wstringstream err; - err << L"The following error(s) have been found:" << std::endl; + wxString err; + err << _("The following error(s) have been found:") << '\n'; if (validator.otp == OnlineValidator::FileState::Missing) - err << L"otp.bin missing in cemu root directory" << std::endl; + err << _("otp.bin missing in Cemu root directory") << '\n'; else if(validator.otp == OnlineValidator::FileState::Corrupted) - err << L"otp.bin is invalid" << std::endl; + err << _("otp.bin is invalid") << '\n'; if (validator.seeprom == OnlineValidator::FileState::Missing) - err << L"seeprom.bin missing in cemu root directory" << std::endl; + err << _("seeprom.bin missing in Cemu root directory") << '\n'; else if(validator.seeprom == OnlineValidator::FileState::Corrupted) - err << L"seeprom.bin is invalid" << std::endl; + err << _("seeprom.bin is invalid") << '\n'; if(!validator.missing_files.empty()) { - err << L"Missing certificate and key files:" << std::endl; + err << _("Missing certificate and key files:") << '\n'; int counter = 0; for (const auto& f : validator.missing_files) { - err << f << std::endl; + err << f << '\n'; ++counter; if(counter > 10) { - err << L"..." << std::endl; + err << "..." << '\n'; break; } } - err << std::endl; + err << '\n'; } if (!validator.valid_account) { - err << L"The currently selected account is not a valid or dumped online account:\n" << boost::nowide::widen(fmt::format("{}", validator.account_error)); + err << _("The currently selected account is not a valid or dumped online account:") << '\n'; + err << GetOnlineAccountErrorMessage(validator.account_error); } - - - wxMessageBox(err.str(), _("Online Status"), wxOK | wxCENTRE | wxICON_INFORMATION); + + wxMessageBox(err, _("Online Status"), wxOK | wxCENTRE | wxICON_INFORMATION); } + +std::string GeneralSettings2::GetOnlineAccountErrorMessage(OnlineAccountError error) +{ + switch (error) { + case OnlineAccountError::kNoAccountId: + return _("AccountId missing (The account is not connected to a NNID)").utf8_string(); + case OnlineAccountError::kNoPasswordCached: + return _("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)").utf8_string(); + case OnlineAccountError::kPasswordCacheEmpty: + return _("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)").utf8_string(); + case OnlineAccountError::kNoPrincipalId: + return _("PrincipalId missing").utf8_string(); + default: + return "no error"; + } +} \ No newline at end of file diff --git a/src/gui/GeneralSettings2.h b/src/gui/GeneralSettings2.h index a6136abf..b667faf0 100644 --- a/src/gui/GeneralSettings2.h +++ b/src/gui/GeneralSettings2.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include class wxColourPickerCtrl; @@ -100,6 +101,7 @@ private: void OnShowOnlineValidator(wxCommandEvent& event); void OnOnlineEnable(wxCommandEvent& event); void OnAccountServiceChanged(wxCommandEvent& event); + std::string GetOnlineAccountErrorMessage(OnlineAccountError error); // updates cemu audio devices void UpdateAudioDevice(); diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index 78fa6569..c03c6fdf 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -570,8 +570,8 @@ void GraphicPacksWindow2::OnActivePresetChanged(wxCommandEvent& event) wxASSERT(obj); const auto string_data = dynamic_cast(obj->GetClientObject()); wxASSERT(string_data); - const auto preset = wxHelper::MakeUTF8(obj->GetStringSelection()); - if(m_shown_graphic_pack->SetActivePreset(wxHelper::MakeUTF8(string_data->GetData()), preset)) + const auto preset = obj->GetStringSelection().utf8_string(); + if(m_shown_graphic_pack->SetActivePreset(string_data->GetData().utf8_string(), preset)) { wxWindowUpdateLocker lock(this); ClearPresets(); @@ -629,7 +629,7 @@ void GraphicPacksWindow2::OnCheckForUpdates(wxCommandEvent& event) const auto packs = str.str(); if(!packs.empty()) { - wxMessageBox(fmt::format("{}\n \n{} \n{}", _("This update removed or renamed the following graphic packs:").ToStdString(), packs, _("You may need to set them up again.").ToStdString()), + wxMessageBox(fmt::format("{}\n \n{} \n{}", _("This update removed or renamed the following graphic packs:").utf8_string(), packs, _("You may need to set them up again.").utf8_string()), _("Warning"), wxOK | wxCENTRE | wxICON_INFORMATION, this); } } @@ -668,7 +668,7 @@ void GraphicPacksWindow2::SashPositionChanged(wxEvent& event) void GraphicPacksWindow2::OnFilterUpdate(wxEvent& event) { - m_filter = wxHelper::MakeUTF8(m_filter_text->GetValue()); + m_filter = m_filter_text->GetValue().utf8_string(); FillGraphicPackList(); event.Skip(); } diff --git a/src/gui/LoggingWindow.cpp b/src/gui/LoggingWindow.cpp index dbc7536d..4026113e 100644 --- a/src/gui/LoggingWindow.cpp +++ b/src/gui/LoggingWindow.cpp @@ -88,7 +88,7 @@ void LoggingWindow::OnLogMessage(wxLogEvent& event) void LoggingWindow::OnFilterChange(wxCommandEvent& event) { - m_log_list->SetActiveFilter(from_wxString(m_filter->GetValue())); + m_log_list->SetActiveFilter(m_filter->GetValue().utf8_string()); event.Skip(); } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 6fa72801..bba64a24 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -638,7 +638,7 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) const auto menuId = event.GetId(); if (menuId == MAINFRAME_MENU_ID_FILE_LOAD) { - const auto wildcard = wxStringFormat2( + const auto wildcard = formatWxString( "{}|*.wud;*.wux;*.wua;*.iso;*.rpx;*.elf" "|{}|*.wud;*.wux;*.iso" "|{}|*.wua" @@ -648,7 +648,7 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) _("Wii U image (*.wud, *.wux, *.iso, *.wad)"), _("Wii U archive (*.wua)"), _("Wii U executable (*.rpx, *.elf)"), - _("All files (*.*)") + _("All files (*.*)") ); wxFileDialog openFileDialog(this, _("Open file to launch"), wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); @@ -706,7 +706,7 @@ void MainWindow::OnInstallUpdate(wxCommandEvent& event) { if (!fs::exists(dirPath.parent_path() / "code") || !fs::exists(dirPath.parent_path() / "content") || !fs::exists(dirPath.parent_path() / "meta")) { - wxMessageBox(wxStringFormat2(_("The (parent) folder of the title you selected is missing at least one of the required subfolders (\"code\", \"content\" and \"meta\")\nMake sure that the files are complete."), dirPath.filename().string())); + wxMessageBox(formatWxString(_("The (parent) folder of the title you selected is missing at least one of the required subfolders (\"code\", \"content\" and \"meta\")\nMake sure that the files are complete."), dirPath.filename().string())); continue; } else @@ -1837,7 +1837,7 @@ public: void AddHeaderInfo(wxWindow* parent, wxSizer* sizer) { - auto versionString = fmt::format(fmt::runtime(_("Cemu\nVersion {0}\nCompiled on {1}\nOriginal authors: {2}").ToStdString()), BUILD_VERSION_STRING, BUILD_DATE, "Exzap, Petergov"); + auto versionString = formatWxString(_("Cemu\nVersion {0}\nCompiled on {1}\nOriginal authors: {2}"), BUILD_VERSION_STRING, BUILD_DATE, "Exzap, Petergov"); sizer->Add(new wxStaticText(parent, wxID_ANY, versionString), wxSizerFlags().Border(wxALL, 3).Border(wxTOP, 10)); sizer->Add(new wxHyperlinkCtrl(parent, -1, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3)); @@ -2287,57 +2287,6 @@ void MainWindow::RecreateMenu() SetMenuVisible(false); } -void MainWindow::OnAfterCallShowErrorDialog() -{ - //wxMessageBox((const wxString&)dialogText, (const wxString&)dialogTitle, wxICON_INFORMATION); - //wxDialog* dialog = new wxDialog(NULL,wxID_ANY,(const wxString&)dialogTitle,wxDefaultPosition,wxSize(310,170)); - //dialog->ShowModal(); - //dialogState = 1; -} - -bool MainWindow::EnableOnlineMode() const -{ - // TODO: not used anymore - // - // if enabling online mode, check if all requirements are met - std::wstring additionalErrorInfo; - const sint32 onlineReqError = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo); - - bool enableOnline = false; - if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_OTP_MISSING) - { - wxMessageBox(_("otp.bin could not be found"), _("Error"), wxICON_ERROR); - } - else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_OTP_CORRUPTED) - { - wxMessageBox(_("otp.bin is corrupted or has invalid size"), _("Error"), wxICON_ERROR); - } - else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_SEEPROM_MISSING) - { - wxMessageBox(_("seeprom.bin could not be found"), _("Error"), wxICON_ERROR); - } - else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_SEEPROM_CORRUPTED) - { - wxMessageBox(_("seeprom.bin is corrupted or has invalid size"), _("Error"), wxICON_ERROR); - } - else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_MISSING_FILE) - { - std::wstring errorMessage = fmt::format(L"Unable to load a necessary file:\n{}", additionalErrorInfo); - wxMessageBox(errorMessage.c_str(), _("Error"), wxICON_ERROR); - } - else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_OK) - { - enableOnline = true; - } - else - { - wxMessageBox(_("Unknown error occured"), _("Error"), wxICON_ERROR); - } - - //config_get()->enableOnlineMode = enableOnline; - return enableOnline; -} - void MainWindow::RestoreSettingsAfterGameExited() { RecreateMenu(); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 7597c2b2..c1762867 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -104,7 +104,6 @@ public: void OnHelpAbout(wxCommandEvent& event); void OnHelpGettingStarted(wxCommandEvent& event); void OnHelpUpdate(wxCommandEvent& event); - void OnAfterCallShowErrorDialog(); void OnDebugSetting(wxCommandEvent& event); void OnDebugLoggingToggleFlagGeneric(wxCommandEvent& event); void OnPPCInfoToggle(wxCommandEvent& event); @@ -149,7 +148,6 @@ private: void RecreateMenu(); static wxString GetInitialWindowTitle(); void ShowGettingStartedDialog(); - bool EnableOnlineMode() const; bool InstallUpdate(const fs::path& metaFilePath); diff --git a/src/gui/MemorySearcherTool.cpp b/src/gui/MemorySearcherTool.cpp index 093f7ffe..5e711dd9 100644 --- a/src/gui/MemorySearcherTool.cpp +++ b/src/gui/MemorySearcherTool.cpp @@ -664,30 +664,6 @@ void MemorySearcherTool::SetSearchDataType() m_searchDataType = SearchDataType_None; } -std::string MemorySearcherTool::GetSearchTypeName() const -{ - switch (m_searchDataType) - { - case SearchDataType_String: - return from_wxString(kDatatypeString); - case SearchDataType_Float: - return from_wxString(kDatatypeFloat); - case SearchDataType_Double: - return from_wxString(kDatatypeDouble); - case SearchDataType_Int8: - return from_wxString(kDatatypeInt8); - case SearchDataType_Int16: - return from_wxString(kDatatypeInt16); - case SearchDataType_Int32: - return from_wxString(kDatatypeInt32); - case SearchDataType_Int64: - return from_wxString(kDatatypeInt64); - default: - return ""; - } - -} - template <> bool MemorySearcherTool::ConvertStringToType(const char* inValue, sint8& outValue) const { diff --git a/src/gui/MemorySearcherTool.h b/src/gui/MemorySearcherTool.h index add9aced..78b5cb77 100644 --- a/src/gui/MemorySearcherTool.h +++ b/src/gui/MemorySearcherTool.h @@ -56,8 +56,6 @@ private: void RefreshResultList(); void RefreshStashList(); void SetSearchDataType(); - std::string GetSearchTypeName() const; - void CreateRightClickPopupMenu(); void Load(); void Save(); diff --git a/src/gui/TitleManager.cpp b/src/gui/TitleManager.cpp index 2440e12c..a36b3f74 100644 --- a/src/gui/TitleManager.cpp +++ b/src/gui/TitleManager.cpp @@ -70,7 +70,7 @@ wxPanel* TitleManager::CreateTitleManagerPage() row->Add(m_refresh_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); auto* help_button = new wxStaticBitmap(panel, wxID_ANY, wxBITMAP_PNG_FROM_DATA(PNG_HELP)); - help_button->SetToolTip(wxStringFormat2(_("The following prefixes are supported:\n{0}\n{1}\n{2}\n{3}\n{4}"), + help_button->SetToolTip(formatWxString(_("The following prefixes are supported:\n{0}\n{1}\n{2}\n{3}\n{4}"), "titleid:", "name:", "type:", "version:", "region:")); row->Add(help_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); @@ -328,7 +328,7 @@ void TitleManager::OnTitleSearchComplete(wxCommandEvent& event) } // update status bar text m_title_list->SortEntries(-1); - m_status_bar->SetStatusText(wxStringFormat2(_("Found {0} games, {1} updates, {2} DLCs and {3} save entries"), + m_status_bar->SetStatusText(formatWxString(_("Found {0} games, {1} updates, {2} DLCs and {3} save entries"), m_title_list->GetCountByType(wxTitleManagerList::EntryType::Base) + m_title_list->GetCountByType(wxTitleManagerList::EntryType::System), m_title_list->GetCountByType(wxTitleManagerList::EntryType::Update), m_title_list->GetCountByType(wxTitleManagerList::EntryType::Dlc), @@ -494,7 +494,7 @@ void TitleManager::OnSaveDelete(wxCommandEvent& event) if (selection.IsEmpty()) return; - const auto msg = wxStringFormat2(_("Are you really sure that you want to delete the save entry for {}"), selection); + const auto msg = formatWxString(_("Are you really sure that you want to delete the save entry for {}"), selection); const auto result = wxMessageBox(msg, _("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this); if (result == wxNO) return; @@ -545,7 +545,7 @@ void TitleManager::OnSaveDelete(wxCommandEvent& event) fs::remove_all(target, ec); if (ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to delete the save directory:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to delete the save directory:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); return; } @@ -622,7 +622,8 @@ void TitleManager::OnSaveExport(wxCommandEvent& event) const auto persistent_id = (uint32)(uintptr_t)m_save_account_list->GetClientData(selection_index); - wxFileDialog path_dialog(this, _("Select a target file to export the save entry"), entry->path.string(), wxEmptyString, "Exported save entry (*.zip)|*.zip", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wxFileDialog path_dialog(this, _("Select a target file to export the save entry"), entry->path.string(), wxEmptyString, + fmt::format("{}|*.zip", _("Exported save entry (*.zip)")), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (path_dialog.ShowModal() != wxID_OK || path_dialog.GetPath().IsEmpty()) return; @@ -633,7 +634,7 @@ void TitleManager::OnSaveExport(wxCommandEvent& event) { zip_error_t ziperror; zip_error_init_with_code(&ziperror, ze); - const auto error_msg = wxStringFormat2(_("Error when creating the zip for the save entry:\n{}"), zip_error_strerror(&ziperror)); + const auto error_msg = formatWxString(_("Error when creating the zip for the save entry:\n{}"), zip_error_strerror(&ziperror)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -651,7 +652,7 @@ void TitleManager::OnSaveExport(wxCommandEvent& event) { if(zip_dir_add(zip, (const char*)entryname.substr(savedir_str.size() + 1).c_str(), ZIP_FL_ENC_UTF_8) < 0 ) { - const auto error_msg = wxStringFormat2(_("Error when trying to add a directory to the zip:\n{}"), zip_strerror(zip)); + const auto error_msg = formatWxString(_("Error when trying to add a directory to the zip:\n{}"), zip_strerror(zip)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); } } @@ -660,13 +661,13 @@ void TitleManager::OnSaveExport(wxCommandEvent& event) auto* source = zip_source_file(zip, (const char*)entryname.c_str(), 0, 0); if(!source) { - const auto error_msg = wxStringFormat2(_("Error when trying to add a file to the zip:\n{}"), zip_strerror(zip)); + const auto error_msg = formatWxString(_("Error when trying to add a file to the zip:\n{}"), zip_strerror(zip)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); } if (zip_file_add(zip, (const char*)entryname.substr(savedir_str.size() + 1).c_str(), source, ZIP_FL_ENC_UTF_8) < 0) { - const auto error_msg = wxStringFormat2(_("Error when trying to add a file to the zip:\n{}"), zip_strerror(zip)); + const auto error_msg = formatWxString(_("Error when trying to add a file to the zip:\n{}"), zip_strerror(zip)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); zip_source_free(source); @@ -679,7 +680,7 @@ void TitleManager::OnSaveExport(wxCommandEvent& event) auto* metabuff = zip_source_buffer(zip, metacontent.data(), metacontent.size(), 0); if(zip_file_add(zip, "cemu_meta", metabuff, ZIP_FL_ENC_UTF_8) < 0) { - const auto error_msg = wxStringFormat2(_("Error when trying to add cemu_meta file to the zip:\n{}"), zip_strerror(zip)); + const auto error_msg = formatWxString(_("Error when trying to add cemu_meta file to the zip:\n{}"), zip_strerror(zip)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); zip_source_free(metabuff); @@ -730,11 +731,11 @@ void TitleManager::InitiateConnect() if (!NCrypto::SEEPROM_IsPresent()) { - SetDownloadStatusText("Dumped online files not found"); + SetDownloadStatusText(_("Dumped online files not found")); return; } - SetDownloadStatusText("Connecting..."); + SetDownloadStatusText(_("Connecting...")); // begin async connect dlMgr->setUserData(this); dlMgr->registerCallbacks( diff --git a/src/gui/canvas/VulkanCanvas.cpp b/src/gui/canvas/VulkanCanvas.cpp index 5463a494..eb56b3c4 100644 --- a/src/gui/canvas/VulkanCanvas.cpp +++ b/src/gui/canvas/VulkanCanvas.cpp @@ -7,6 +7,7 @@ #endif #include +#include VulkanCanvas::VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_window) : IRenderCanvas(is_main_window), wxWindow(parent, wxID_ANY, wxDefaultPosition, size, wxNO_FULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) @@ -36,8 +37,8 @@ VulkanCanvas::VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_wi } catch(const std::exception& ex) { - const auto msg = fmt::format(fmt::runtime(_("Error when initializing Vulkan renderer:\n{}").ToStdString()), ex.what()); - cemuLog_log(LogType::Force, msg); + cemuLog_log(LogType::Force, "Error when initializing Vulkan renderer: {}", ex.what()); + auto msg = formatWxString(_("Error when initializing Vulkan renderer:\n{}"), ex.what()); wxMessageDialog dialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); dialog.ShowModal(); exit(0); diff --git a/src/gui/components/wxDownloadManagerList.cpp b/src/gui/components/wxDownloadManagerList.cpp index ebff9e95..ca2d7a71 100644 --- a/src/gui/components/wxDownloadManagerList.cpp +++ b/src/gui/components/wxDownloadManagerList.cpp @@ -432,13 +432,11 @@ wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemC switch (column) { case ColumnTitleId: - return wxStringFormat2("{:08x}-{:08x}", (uint32)(entry.titleId >> 32), (uint32)(entry.titleId & 0xFFFFFFFF)); + return formatWxString("{:08x}-{:08x}", (uint32) (entry.titleId >> 32), (uint32) (entry.titleId & 0xFFFFFFFF)); case ColumnName: - { return entry.name; - } case ColumnType: - return wxStringFormat2("{}", entry.type); + return GetTranslatedTitleEntryType(entry.type); case ColumnVersion: { // dont show version for base game unless it is not v0 @@ -446,7 +444,7 @@ wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemC return ""; if (entry.type == EntryType::DLC && entry.version == 0) return ""; - return wxStringFormat2("v{}", entry.version); + return formatWxString("v{}", entry.version); } case ColumnProgress: { @@ -454,11 +452,11 @@ wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemC { if (entry.progress >= 1000) return "100%"; - return wxStringFormat2("{:.1f}%", (float)entry.progress / 10.0f); // one decimal + return formatWxString("{:.1f}%", (float) entry.progress / 10.0f); // one decimal } else if (entry.status == TitleDownloadStatus::Installing || entry.status == TitleDownloadStatus::Checking || entry.status == TitleDownloadStatus::Verifying) { - return wxStringFormat2("{0}/{1}", entry.progress, entry.progressMax); // number of processed files/content files + return formatWxString("{0}/{1}", entry.progress, entry.progressMax); // number of processed files/content files } return ""; } @@ -503,6 +501,21 @@ wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemC return wxEmptyString; } +std::string wxDownloadManagerList::GetTranslatedTitleEntryType(EntryType type) +{ + switch (type) + { + case EntryType::Base: + return _("base").utf8_string(); + case EntryType::Update: + return _("update").utf8_string(); + case EntryType::DLC: + return _("DLC").utf8_string(); + default: + return std::to_string(static_cast>(type)); + } +} + void wxDownloadManagerList::AddOrUpdateTitle(TitleEntryData_t* obj) { const auto& data = obj->GetData(); diff --git a/src/gui/components/wxDownloadManagerList.h b/src/gui/components/wxDownloadManagerList.h index 0af5b082..b0051076 100644 --- a/src/gui/components/wxDownloadManagerList.h +++ b/src/gui/components/wxDownloadManagerList.h @@ -150,25 +150,6 @@ private: bool SortFunc(std::span sortColumnOrder, const Type_t& v1, const Type_t& v2); static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column); + static std::string GetTranslatedTitleEntryType(EntryType entryType); std::future m_context_worker; }; - -template <> -struct fmt::formatter : formatter -{ - using base = fmt::formatter; - template - auto format(const wxDownloadManagerList::EntryType& type, FormatContext& ctx) - { - switch (type) - { - case wxDownloadManagerList::EntryType::Base: - return base::format("base", ctx); - case wxDownloadManagerList::EntryType::Update: - return base::format("update", ctx); - case wxDownloadManagerList::EntryType::DLC: - return base::format("DLC", ctx); - } - return base::format(std::to_string(static_cast>(type)), ctx); - } -}; \ No newline at end of file diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 69f74870..ebbab044 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -633,7 +633,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event) if(dialog.ShowModal() == wxID_OK) { const auto custom_name = dialog.GetValue(); - GetConfig().SetGameListCustomName(title_id, wxHelper::MakeUTF8(custom_name)); + GetConfig().SetGameListCustomName(title_id, custom_name.utf8_string()); m_name_cache.clear(); g_config.Save(); // update list entry @@ -1036,8 +1036,8 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) const auto region_text = fmt::format("{}", gameInfo.GetRegion()); - SetItem(index, ColumnRegion, _(region_text)); - SetItem(index, ColumnTitleID, _(fmt::format("{:016x}", titleId))); + SetItem(index, ColumnRegion, wxGetTranslation(region_text)); + SetItem(index, ColumnTitleID, fmt::format("{:016x}", titleId)); } else if (m_style == Style::kIcons) { @@ -1124,7 +1124,7 @@ void wxGameList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt) void wxGameList::RemoveCache(const std::list& cachePaths, const std::string& titleName) { - wxMessageDialog dialog(this, fmt::format(fmt::runtime(_("Remove the shader caches for {}?").ToStdString()), titleName), _("Remove shader caches"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); + wxMessageDialog dialog(this, formatWxString(_("Remove the shader caches for {}?"), titleName), _("Remove shader caches"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); dialog.SetYesNoLabels(_("Yes"), _("No")); const auto dialogResult = dialog.ShowModal(); @@ -1139,7 +1139,7 @@ void wxGameList::RemoveCache(const std::list& cachePaths, const std::s if (errs.empty()) wxMessageDialog(this, _("The shader caches were removed!"), _("Shader caches removed"), wxCENTRE | wxOK | wxICON_INFORMATION).ShowModal(); else - wxMessageDialog(this, fmt::format(fmt::runtime(_("Failed to remove the shader caches:\n{}").ToStdString()), fmt::join(errs, "\n")), _("Error"), wxCENTRE | wxOK | wxICON_ERROR).ShowModal(); + wxMessageDialog(this, formatWxString(_("Failed to remove the shader caches:\n{}"), fmt::join(errs, "\n")), _("Error"), wxCENTRE | wxOK | wxICON_ERROR).ShowModal(); } void wxGameList::AsyncWorkerThread() @@ -1265,13 +1265,13 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { // In most cases it should find it if (!result_index){ - wxMessageBox("Icon is yet to load, so will not be used by the shortcut", "Warning", wxOK | wxCENTRE | wxICON_WARNING); + wxMessageBox(_("Icon is yet to load, so will not be used by the shortcut"), _("Warning"), wxOK | wxCENTRE | wxICON_WARNING); } else { const fs::path out_icon_dir = ActiveSettings::GetUserDataPath("icons"); if (!fs::exists(out_icon_dir) && !fs::create_directories(out_icon_dir)){ - wxMessageBox("Cannot access the icon directory, the shortcut will have no icon", "Warning", wxOK | wxCENTRE | wxICON_WARNING); + wxMessageBox(_("Cannot access the icon directory, the shortcut will have no icon"), _("Warning"), wxOK | wxCENTRE | wxICON_WARNING); } else { icon_path = out_icon_dir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); @@ -1282,7 +1282,7 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { wxPNGHandler pngHandler; if (!pngHandler.SaveFile(&image, png_file, false)) { icon_path = std::nullopt; - wxMessageBox("The icon was unable to be saved, the shortcut will have no icon", "Warning", wxOK | wxCENTRE | wxICON_WARNING); + wxMessageBox(_("The icon was unable to be saved, the shortcut will have no icon"), _("Warning"), wxOK | wxCENTRE | wxICON_WARNING); } } } @@ -1306,7 +1306,7 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { std::ofstream output_stream(output_path); if (!output_stream.good()) { - const wxString errorMsg = fmt::format("Failed to save desktop entry to {}", output_path.utf8_string()); + auto errorMsg = formatWxString(_("Failed to save desktop entry to {}"), output_path.utf8_string()); wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return; } diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index 6572a702..bae986ca 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -303,29 +303,29 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 righ } } - std::string msg = wxHelper::MakeUTF8(_("The following content will be converted to a compressed Wii U archive file (.wua):")); + wxString msg = _("The following content will be converted to a compressed Wii U archive file (.wua):"); msg.append("\n \n"); if (titleInfo_base.IsValid()) - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game:\n{}"))), titleInfo_base.GetPrintPath())); + msg.append(formatWxString(_("Base game:\n{}"), titleInfo_base.GetPrintPath())); else - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game:\nNot installed"))))); + msg.append(_("Base game:\nNot installed")); msg.append("\n\n"); if (titleInfo_update.IsValid()) - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update:\n{}"))), titleInfo_update.GetPrintPath())); + msg.append(formatWxString(_("Update:\n{}"), titleInfo_update.GetPrintPath())); else - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update:\nNot installed"))))); + msg.append(_("Update:\nNot installed")); msg.append("\n\n"); if (titleInfo_aoc.IsValid()) - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC:\n{}"))), titleInfo_aoc.GetPrintPath())); + msg.append(formatWxString(_("DLC:\n{}"), titleInfo_aoc.GetPrintPath())); else - msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC:\nNot installed"))))); + msg.append(_("DLC:\nNot installed")); - const int answer = wxMessageBox(wxString::FromUTF8(msg), _("Confirmation"), wxOK | wxCANCEL | wxCENTRE | wxICON_QUESTION, this); + const int answer = wxMessageBox(msg, _("Confirmation"), wxOK | wxCANCEL | wxCENTRE | wxICON_QUESTION, this); if (answer != wxOK) return; std::vector titlesToConvert; @@ -732,7 +732,7 @@ void wxTitleManagerList::OnItemSelected(wxListEvent& event) // return;; //} - //m_tooltip_text->SetLabel(wxStringFormat2("{}\n{}", msg, _("You can use the context menu to fix it."))); + //m_tooltip_text->SetLabel(formatWxString("{}\n{}", msg, _("You can use the context menu to fix it."))); //m_tooltip_window->Fit(); //m_tooltip_timer->StartOnce(250); } @@ -792,9 +792,9 @@ bool wxTitleManagerList::DeleteEntry(long index, const TitleEntry& entry) wxString msg; const bool is_directory = fs::is_directory(entry.path); if(is_directory) - msg = wxStringFormat2(_("Are you really sure that you want to delete the following folder:\n{}"), wxHelper::FromUtf8(_pathToUtf8(entry.path))); + msg = formatWxString(_("Are you really sure that you want to delete the following folder:\n{}"), _pathToUtf8(entry.path)); else - msg = wxStringFormat2(_("Are you really sure that you want to delete the following file:\n{}"), wxHelper::FromUtf8(_pathToUtf8(entry.path))); + msg = formatWxString(_("Are you really sure that you want to delete the following file:\n{}"), _pathToUtf8(entry.path)); const auto result = wxMessageBox(msg, _("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this); if (result == wxNO) @@ -835,7 +835,7 @@ bool wxTitleManagerList::DeleteEntry(long index, const TitleEntry& entry) if(ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to delete the entry:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to delete the entry:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK|wxCENTRE, this); return false; } @@ -922,15 +922,15 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu switch (column) { case ColumnTitleId: - return wxStringFormat2("{:08x}-{:08x}", (uint32)(entry.title_id >> 32), (uint32)(entry.title_id & 0xFFFFFFFF)); + return formatWxString("{:08x}-{:08x}", (uint32) (entry.title_id >> 32), (uint32) (entry.title_id & 0xFFFFFFFF)); case ColumnName: return entry.name; case ColumnType: - return wxStringFormat2("{}", entry.type); + return GetTranslatedTitleEntryType(entry.type); case ColumnVersion: - return wxStringFormat2("{}", entry.version); + return formatWxString("{}", entry.version); case ColumnRegion: - return wxStringFormat2("{}", entry.region); // TODO its a flag so formatter is currently not correct + return wxGetTranslation(fmt::format("{}", entry.region)); case ColumnFormat: { if (entry.type == EntryType::Save) @@ -945,7 +945,6 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu return _("WUA"); } return ""; - //return wxStringFormat2("{}", entry.format); } case ColumnLocation: { @@ -964,6 +963,25 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu return wxEmptyString; } +std::string wxTitleManagerList::GetTranslatedTitleEntryType(EntryType type) +{ + switch (type) + { + case EntryType::Base: + return _("base").utf8_string(); + case EntryType::Update: + return _("update").utf8_string(); + case EntryType::Dlc: + return _("DLC").utf8_string(); + case EntryType::Save: + return _("save").utf8_string(); + case EntryType::System: + return _("system").utf8_string(); + default: + return std::to_string(static_cast>(type)); + } +} + void wxTitleManagerList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt) { if (evt->eventType != CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED && diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/components/wxTitleManagerList.h index 547310c2..043c78f6 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/components/wxTitleManagerList.h @@ -132,32 +132,9 @@ private: bool SortFunc(int column, const Type_t& v1, const Type_t& v2); static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column); + static std::string GetTranslatedTitleEntryType(EntryType entryType); std::future m_context_worker; uint64 m_callbackIdTitleList; uint64 m_callbackIdSaveList; }; - -template <> -struct fmt::formatter : formatter -{ - using base = fmt::formatter; - template - auto format(const wxTitleManagerList::EntryType& type, FormatContext& ctx) - { - switch (type) - { - case wxTitleManagerList::EntryType::Base: - return base::format("base", ctx); - case wxTitleManagerList::EntryType::Update: - return base::format("update", ctx); - case wxTitleManagerList::EntryType::Dlc: - return base::format("DLC", ctx); - case wxTitleManagerList::EntryType::Save: - return base::format("save", ctx); - case wxTitleManagerList::EntryType::System: - return base::format("system", ctx); - } - return base::format(std::to_string(static_cast>(type)), ctx); - } -}; \ No newline at end of file diff --git a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp b/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp index 71b56637..1da92c34 100644 --- a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp +++ b/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "util/helpers/helpers.h" wxCreateAccountDialog::wxCreateAccountDialog(wxWindow* parent) @@ -71,7 +72,7 @@ void wxCreateAccountDialog::OnOK(wxCommandEvent& event) const auto id = GetPersistentId(); if(id < Account::kMinPersistendId) { - wxMessageBox(fmt::format(fmt::runtime(_("The persistent id must be greater than {:x}!").ToStdString()), Account::kMinPersistendId), + wxMessageBox(formatWxString(_("The persistent id must be greater than {:x}!"), Account::kMinPersistendId), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } diff --git a/src/gui/dialogs/SaveImport/SaveImportWindow.cpp b/src/gui/dialogs/SaveImport/SaveImportWindow.cpp index 2a570bb0..b31f24b2 100644 --- a/src/gui/dialogs/SaveImport/SaveImportWindow.cpp +++ b/src/gui/dialogs/SaveImport/SaveImportWindow.cpp @@ -30,8 +30,8 @@ SaveImportWindow::SaveImportWindow(wxWindow* parent, uint64 title_id) row1->Add(new wxStaticText(this, wxID_ANY, _("Source")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_source_selection = new wxFilePickerCtrl(this, wxID_ANY, wxEmptyString, - _("Select a zipped save file"), - wxStringFormat2("{}|*.zip", _("Save entry (*.zip)"))); + _("Select a zipped save file"), + formatWxString("{}|*.zip", _("Save entry (*.zip)"))); m_source_selection->SetMinSize({ 270, -1 }); row1->Add(m_source_selection, 1, wxALL | wxEXPAND, 5); @@ -118,7 +118,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) const uint64_t titleId = ConvertString(str.substr(sizeof("titleId = ") + 1), 16); if(titleId != 0 && titleId != m_title_id) { - const auto msg = wxStringFormat2(_("You are trying to import a savegame for a different title than your currently selected one: {:016x} vs {:016x}\nAre you sure that you want to continue?"), titleId, m_title_id); + const auto msg = formatWxString(_("You are trying to import a savegame for a different title than your currently selected one: {:016x} vs {:016x}\nAre you sure that you want to continue?"), titleId, m_title_id); const auto res = wxMessageBox(msg, _("Error"), wxYES_NO | wxCENTRE | wxICON_WARNING, this); if(res == wxNO) { @@ -143,7 +143,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) //auto tmp_source = fs::temp_directory_path(ec); //if(ec) //{ - // const auto error_msg = wxStringFormat2(_("Error when getting the temp directory path:\n{}"), GetSystemErrorMessage(ec)); + // const auto error_msg = formatWxString(_("Error when getting the temp directory path:\n{}"), GetSystemErrorMessage(ec)); // wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); // return; //} @@ -158,7 +158,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) target_id = ConvertString(m_target_selection->GetValue().ToStdString(), 16); if (target_id < Account::kMinPersistendId) { - const auto msg = wxStringFormat2(_("The given account id is not valid!\nIt must be a hex number bigger or equal than {:08x}"), Account::kMinPersistendId); + const auto msg = formatWxString(_("The given account id is not valid!\nIt must be a hex number bigger or equal than {:08x}"), Account::kMinPersistendId); wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -170,7 +170,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) { if (!fs::is_directory(target_path)) { - const auto msg = wxStringFormat2(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path)); + const auto msg = formatWxString(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path)); wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); m_return_code = wxCANCEL; Close(); @@ -193,7 +193,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) if (ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to delete the former save game:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to delete the former save game:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); return; } @@ -213,7 +213,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) fs::create_directories(tmp_source, ec); if (ec) { - const auto error_msg = wxStringFormat2(_("Error when creating the extraction path:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when creating the extraction path:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -221,7 +221,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) zip = zip_open(zipfile.c_str(), ZIP_RDONLY, &ziperr); if (!zip) { - const auto error_msg = wxStringFormat2(_("Error when opening the import zip file:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when opening the import zip file:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -319,7 +319,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event) fs::rename(tmp_source, target_path, ec); if (ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to move the extracted save game:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to move the extracted save game:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); return; }*/ diff --git a/src/gui/dialogs/SaveImport/SaveTransfer.cpp b/src/gui/dialogs/SaveImport/SaveTransfer.cpp index 14e473a1..c763c419 100644 --- a/src/gui/dialogs/SaveImport/SaveTransfer.cpp +++ b/src/gui/dialogs/SaveImport/SaveTransfer.cpp @@ -92,7 +92,7 @@ void SaveTransfer::OnTransfer(wxCommandEvent& event) target_id = ConvertString(m_target_selection->GetValue().ToStdString(), 16); if(target_id < Account::kMinPersistendId) { - const auto msg = wxStringFormat2(_("The given account id is not valid!\nIt must be a hex number bigger or equal than {:08x}"), Account::kMinPersistendId); + const auto msg = formatWxString(_("The given account id is not valid!\nIt must be a hex number bigger or equal than {:08x}"), Account::kMinPersistendId); wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); return; } @@ -108,7 +108,7 @@ void SaveTransfer::OnTransfer(wxCommandEvent& event) { if(!fs::is_directory(target_path)) { - const auto msg = wxStringFormat2(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path)); + const auto msg = formatWxString(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path)); wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); m_return_code = wxCANCEL; Close(); @@ -131,7 +131,7 @@ void SaveTransfer::OnTransfer(wxCommandEvent& event) if (ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to delete the former save game:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to delete the former save game:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); return; } @@ -187,7 +187,7 @@ void SaveTransfer::OnTransfer(wxCommandEvent& event) fs::rename(source_path, target_path, ec); if (ec) { - const auto error_msg = wxStringFormat2(_("Error when trying to move the save game:\n{}"), GetSystemErrorMessage(ec)); + const auto error_msg = formatWxString(_("Error when trying to move the save game:\n{}"), GetSystemErrorMessage(ec)); wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); return; } diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 1a5e999b..68f97590 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -136,7 +136,7 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps) g_mainFrame->AsyncSetTitle(windowText); auto* pad = g_mainFrame->GetPadView(); if (pad) - pad->AsyncSetTitle(fmt::format("GamePad View - FPS: {:.02f}", fps)); + pad->AsyncSetTitle(fmt::format("{} - FPS: {:.02f}", _("GamePad View").utf8_string(), fps)); } } diff --git a/src/gui/helpers/wxHelpers.h b/src/gui/helpers/wxHelpers.h index 8fd0f8a9..fa135cf4 100644 --- a/src/gui/helpers/wxHelpers.h +++ b/src/gui/helpers/wxHelpers.h @@ -45,16 +45,9 @@ public: }; template -wxString wxStringFormat2(const wxString& format, TArgs&&...args) +wxString formatWxString(const wxString& format, TArgs&&...args) { - // ignores locale? - return fmt::format(fmt::runtime(format.ToStdString()), std::forward(args)...); -} - -template -wxString wxStringFormat2W(const wxString& format, TArgs&&...args) -{ - return fmt::format(fmt::runtime(format.ToStdWstring()), std::forward(args)...); + return wxString::FromUTF8(fmt::format(fmt::runtime(format.utf8_string()), std::forward(args)...)); } // executes a function when destroying the obj @@ -86,14 +79,6 @@ inline wxString to_wxString(std::string_view str) return wxString::FromUTF8(str.data(), str.size()); } -// creates utf8 std::string from wxString -inline std::string from_wxString(const wxString& str) -{ - const auto tmp = str.ToUTF8(); - return std::string{ tmp.data(), tmp.length() }; -} - - template T get_next_sibling(const T element) { diff --git a/src/gui/input/InputAPIAddWindow.cpp b/src/gui/input/InputAPIAddWindow.cpp index f32a85b6..8fa85fa3 100644 --- a/src/gui/input/InputAPIAddWindow.cpp +++ b/src/gui/input/InputAPIAddWindow.cpp @@ -23,7 +23,7 @@ using wxControllerData = wxCustomData; InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position, const std::vector& controllers) - : wxDialog(parent, wxID_ANY, _("Add input API"), position, wxDefaultSize, 0), m_controllers(controllers) + : wxDialog(parent, wxID_ANY, "Add input API", position, wxDefaultSize, 0), m_controllers(controllers) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp index e34c9241..7a52f865 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/input/InputSettings2.cpp @@ -79,7 +79,7 @@ InputSettings2::InputSettings2(wxWindow* parent) { auto* page = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); page->SetClientObject(nullptr); // force internal type to client object - m_notebook->AddPage(page, wxStringFormat2(_("Controller {}"), i + 1)); + m_notebook->AddPage(page, formatWxString(_("Controller {}"), i + 1)); } m_notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &InputSettings2::on_controller_page_changed, this); @@ -585,9 +585,7 @@ void InputSettings2::on_profile_text_changed(wxCommandEvent& event) // load_bttn, save_bttn, delete_bttn, profile_status const auto text = event.GetString(); - const auto text_str = from_wxString(text); - - const bool valid_name = InputManager::is_valid_profilename(text_str); + const bool valid_name = InputManager::is_valid_profilename(text.utf8_string()); const bool name_exists = profile_names->FindString(text) != wxNOT_FOUND; page_data.m_profile_load->Enable(name_exists); @@ -603,7 +601,7 @@ void InputSettings2::on_profile_load(wxCommandEvent& event) auto* profile_names = page_data.m_profiles; auto* text = page_data.m_profile_status; - const auto selection = from_wxString(profile_names->GetValue()); + const auto selection = profile_names->GetValue().utf8_string(); text->Show(); if (selection.empty() || !InputManager::is_valid_profilename(selection)) { @@ -639,7 +637,7 @@ void InputSettings2::on_profile_save(wxCommandEvent& event) auto* profile_names = page_data.m_profiles; auto* text = page_data.m_profile_status; - const auto selection = from_wxString(profile_names->GetValue()); + const auto selection = profile_names->GetValue().utf8_string(); text->Show(); if (selection.empty() || !InputManager::is_valid_profilename(selection)) { @@ -670,7 +668,7 @@ void InputSettings2::on_profile_delete(wxCommandEvent& event) auto* profile_names = page_data.m_profiles; auto* text = page_data.m_profile_status; - const auto selection = from_wxString(profile_names->GetStringSelection()); + const auto selection = profile_names->GetStringSelection().utf8_string(); text->Show(); if (selection.empty() || !InputManager::is_valid_profilename(selection)) @@ -725,10 +723,9 @@ void InputSettings2::on_emulated_controller_selected(wxCommandEvent& event) } else { - const auto type_str = from_wxString(event.GetString()); try { - const auto type = EmulatedController::type_from_string(type_str); + const auto type = EmulatedController::type_from_string(event.GetString().utf8_string()); // same has already been selected if (page_data.m_controller && page_data.m_controller->type() == type) return; diff --git a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp index b93cf94e..bd71942f 100644 --- a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp +++ b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp @@ -8,6 +8,7 @@ #include "gui/components/wxProgressDialogManager.h" #include +#include enum { @@ -333,7 +334,7 @@ void DebugPPCThreadsWindow::PresentProfileResults(OSThread_t* thread, const std: void DebugPPCThreadsWindow::ProfileThreadWorker(OSThread_t* thread) { wxProgressDialogManager progressDialog(this); - progressDialog.Create("Profiling thread", + progressDialog.Create(_("Profiling thread"), _("Capturing samples..."), 1000, // range wxPD_CAN_SKIP); @@ -364,8 +365,7 @@ void DebugPPCThreadsWindow::ProfileThreadWorker(OSThread_t* thread) totalSampleCount++; if ((totalSampleCount % 50) == 0) { - wxString msg = fmt::format("Capturing samples... ({:})\nResults will be written to log.txt\n", - totalSampleCount); + wxString msg = formatWxString(_("Capturing samples... ({:})\nResults will be written to log.txt\n"), totalSampleCount); if (totalSampleCount < 30000) msg.Append(_("Click Skip button for early results with lower accuracy")); else diff --git a/src/gui/wxHelper.h b/src/gui/wxHelper.h index ac959755..468651ac 100644 --- a/src/gui/wxHelper.h +++ b/src/gui/wxHelper.h @@ -3,13 +3,6 @@ namespace wxHelper { - // wxString to utf8 std::string - inline std::string MakeUTF8(const wxString& str) - { - auto tmpUtf8 = str.ToUTF8(); - return std::string(tmpUtf8.data(), tmpUtf8.length()); - } - inline fs::path MakeFSPath(const wxString& str) { auto tmpUtf8 = str.ToUTF8(); From c66ab0c51ac19eedb5655b231e3731310ccdaaf3 Mon Sep 17 00:00:00 2001 From: Francesco Saltori Date: Fri, 8 Sep 2023 02:09:28 +0200 Subject: [PATCH 020/101] Use native language names in language selector (#964) --- src/gui/GeneralSettings2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 0fad827f..bbe1c474 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -123,13 +123,13 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook) first_row->Add(new wxStaticText(box, wxID_ANY, _("Language"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - wxString language_choices[] = { _("Default"), _("English") }; + wxString language_choices[] = { _("Default"), "English" }; m_language = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(language_choices), language_choices); m_language->SetSelection(0); m_language->SetToolTip(_("Changes the interface language of Cemu\nAvailable languages are stored in the translation directory\nA restart will be required after changing the language")); for (const auto& language : wxGetApp().GetLanguages()) { - m_language->Append(language->Description); + m_language->Append(language->DescriptionNative); } first_row->Add(m_language, 0, wxALL | wxEXPAND, 5); @@ -935,7 +935,7 @@ void GeneralSettings2::StoreConfig() const auto language = m_language->GetStringSelection(); for (const auto& lang : app->GetLanguages()) { - if (lang->Description == language) + if (lang->DescriptionNative == language) { GetConfig().language = lang->Language; break; @@ -1538,7 +1538,7 @@ void GeneralSettings2::ApplyConfig() { if (config.language == language->Language) { - m_language->SetStringSelection(language->Description); + m_language->SetStringSelection(language->DescriptionNative); break; } } From 96800c6f9785d0fc9822da24421a7e6ac9014dd9 Mon Sep 17 00:00:00 2001 From: Francesco Saltori Date: Thu, 14 Sep 2023 12:47:59 +0200 Subject: [PATCH 021/101] Additional localization fixes (#966) --- .github/workflows/generate_pot.yml | 4 +- .../Tools/DownloadManager/DownloadManager.cpp | 20 +++--- src/gui/CemuApp.cpp | 2 +- src/gui/GameProfileWindow.cpp | 4 +- src/gui/GameUpdateWindow.cpp | 22 +++---- src/gui/GeneralSettings2.cpp | 10 +-- src/gui/GeneralSettings2.h | 2 +- src/gui/GraphicPacksWindow2.cpp | 15 ++--- src/gui/MemorySearcherTool.cpp | 4 +- src/gui/TitleManager.cpp | 2 +- src/gui/components/wxDownloadManagerList.cpp | 8 +-- src/gui/components/wxDownloadManagerList.h | 2 +- src/gui/components/wxGameList.cpp | 19 +++--- src/gui/components/wxTitleManagerList.cpp | 12 ++-- src/gui/components/wxTitleManagerList.h | 2 +- src/gui/wxgui.h | 63 +------------------ 16 files changed, 68 insertions(+), 123 deletions(-) diff --git a/.github/workflows/generate_pot.yml b/.github/workflows/generate_pot.yml index f2675574..7dfa86f8 100644 --- a/.github/workflows/generate_pot.yml +++ b/.github/workflows/generate_pot.yml @@ -29,8 +29,8 @@ jobs: - name: "Generate POT file using xgettext" run: > find src -name *.cpp -o -name *.hpp -o -name *.h | - xargs xgettext --from-code=utf-8 - -k_ -kwxTRANSLATE -w 100 + xargs xgettext --from-code=utf-8 -w 100 + --keyword="_" --keyword="wxTRANSLATE" --keyword="wxPLURAL:1,2" --check=space-ellipsis --omit-header -o cemu.pot diff --git a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp index 200d1641..ec39b928 100644 --- a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp +++ b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp @@ -424,7 +424,7 @@ bool DownloadManager::syncAccountTickets() bool DownloadManager::syncSystemTitleTickets() { - setStatusMessage(std::string(_("Downloading system tickets...")), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage(_("Downloading system tickets...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); // todo - add GetAuth() function NAPI::AuthInfo authInfo; authInfo.accountId = m_authInfo.nnidAccountName; @@ -486,7 +486,7 @@ bool DownloadManager::syncSystemTitleTickets() // build list of updates for which either an installed game exists or the base title ticket is cached bool DownloadManager::syncUpdateTickets() { - setStatusMessage(std::string(_("Retrieving update information...")), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage(_("Retrieving update information...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); // download update version list downloadTitleVersionList(); if (!m_hasTitleVersionList) @@ -566,7 +566,7 @@ bool DownloadManager::syncTicketCache() setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); prepareIDBE(ticketInfo.titleId); } - setStatusMessage(std::string(_("Connected. Right click entries in the list to start downloading")), DLMGR_STATUS_CODE::CONNECTED); + setStatusMessage(_("Connected. Right click entries in the list to start downloading").utf8_string(), DLMGR_STATUS_CODE::CONNECTED); return true; } @@ -652,7 +652,7 @@ void DownloadManager::_handle_connect() // reset login state m_iasToken.serviceAccountId.clear(); m_iasToken.deviceToken.clear(); - setStatusMessage(std::string(_("Logging in..")), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage(_("Logging in...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); // retrieve ECS AccountId + DeviceToken from cache if (s_nupFileCache) { @@ -675,7 +675,7 @@ void DownloadManager::_handle_connect() cemuLog_log(LogType::Force, "Failed to request IAS token"); cemu_assert_debug(false); m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(std::string(_("Login failed. Outdated or incomplete online files?")), DLMGR_STATUS_CODE::FAILED); + setStatusMessage(_("Login failed. Outdated or incomplete online files?").utf8_string(), DLMGR_STATUS_CODE::FAILED); return; } } @@ -683,16 +683,16 @@ void DownloadManager::_handle_connect() if (!_connect_queryAccountStatusAndServiceURLs()) { m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(std::string(_("Failed to query account status. Invalid account information?")), DLMGR_STATUS_CODE::FAILED); + setStatusMessage(_("Failed to query account status. Invalid account information?").utf8_string(), DLMGR_STATUS_CODE::FAILED); return; } // load ticket cache and sync - setStatusMessage(std::string(_("Updating ticket cache")), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage(_("Updating ticket cache").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); loadTicketCache(); if (!syncTicketCache()) { m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(std::string(_("Failed to request tickets (invalid NNID?)")), DLMGR_STATUS_CODE::FAILED); + setStatusMessage(_("Failed to request tickets (invalid NNID?)").utf8_string(), DLMGR_STATUS_CODE::FAILED); return; } searchForIncompleteDownloads(); @@ -716,7 +716,7 @@ void DownloadManager::connect( if (nnidAccountName.empty()) { m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(std::string(_("This account is not linked with an NNID")), DLMGR_STATUS_CODE::FAILED); + setStatusMessage(_("This account is not linked with an NNID").utf8_string(), DLMGR_STATUS_CODE::FAILED); return; } runManager(); @@ -726,7 +726,7 @@ void DownloadManager::connect( { cemuLog_log(LogType::Force, "DLMgr: Invalid password hash"); m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(std::string(_("Failed. Account does not have password set")), DLMGR_STATUS_CODE::FAILED); + setStatusMessage(_("Failed. Account does not have password set").utf8_string(), DLMGR_STATUS_CODE::FAILED); return; } m_authInfo.region = region; diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 03496305..74ef6848 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -169,7 +169,7 @@ bool CemuApp::OnInit() "Thank you for testing the in-development build of Cemu for macOS.\n \n" "The macOS port is currently purely experimental and should not be considered stable or ready for issue-free gameplay. " "There are also known issues with degraded performance due to the use of MoltenVk and Rosetta for ARM Macs. We appreciate your patience while we improve Cemu for macOS."); - wxMessageDialog dialog(nullptr, message, "Preview version", wxCENTRE | wxOK | wxICON_WARNING); + wxMessageDialog dialog(nullptr, message, _("Preview version"), wxCENTRE | wxOK | wxICON_WARNING); dialog.SetOKLabel(_("I understand")); dialog.ShowModal(); GetConfig().did_show_macos_disclaimer = true; diff --git a/src/gui/GameProfileWindow.cpp b/src/gui/GameProfileWindow.cpp index 17affc84..f15395e4 100644 --- a/src/gui/GameProfileWindow.cpp +++ b/src/gui/GameProfileWindow.cpp @@ -166,7 +166,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id) for (int i = 0; i < 8; ++i) { - profile_sizer->Add(new wxStaticText(panel, wxID_ANY, fmt::format("{} {}", _("Controller").utf8_string(), (i + 1))), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); + profile_sizer->Add(new wxStaticText(panel, wxID_ANY, formatWxString(_("Controller {}"), i + 1)), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_controller_profile[i] = new wxComboBox(panel, wxID_ANY,"", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_DROPDOWN| wxCB_READONLY); m_controller_profile[i]->SetMinSize(wxSize(250, -1)); @@ -244,7 +244,7 @@ void GameProfileWindow::SetProfileInt(gameProfileIntegerOption_t& option, wxChec void GameProfileWindow::ApplyProfile() { if(m_game_profile.m_gameName) - this->SetTitle(fmt::format("{} - {}", _("Edit game profile").utf8_string(), m_game_profile.m_gameName.value())); + this->SetTitle(_("Edit game profile") + " - " + m_game_profile.m_gameName.value()); // general m_load_libs->SetValue(m_game_profile.m_loadSharedLibraries.value()); diff --git a/src/gui/GameUpdateWindow.cpp b/src/gui/GameUpdateWindow.cpp index e422cbe6..184d5fde 100644 --- a/src/gui/GameUpdateWindow.cpp +++ b/src/gui/GameUpdateWindow.cpp @@ -10,24 +10,24 @@ #include "gui/helpers/wxHelpers.h" #include "wxHelper.h" -std::string _GetTitleIdTypeStr(TitleId titleId) +wxString _GetTitleIdTypeStr(TitleId titleId) { TitleIdParser tip(titleId); switch (tip.GetType()) { case TitleIdParser::TITLE_TYPE::AOC: - return _("DLC").utf8_string(); + return _("DLC"); case TitleIdParser::TITLE_TYPE::BASE_TITLE: - return _("Base game").utf8_string(); + return _("Base game"); case TitleIdParser::TITLE_TYPE::BASE_TITLE_DEMO: - return _("Demo").utf8_string(); + return _("Demo"); case TitleIdParser::TITLE_TYPE::SYSTEM_TITLE: case TitleIdParser::TITLE_TYPE::SYSTEM_OVERLAY_TITLE: - return _("System title").utf8_string(); + return _("System title"); case TitleIdParser::TITLE_TYPE::SYSTEM_DATA: - return _("System data title").utf8_string(); + return _("System data title"); case TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE: - return _("Update").utf8_string(); + return _("Update"); default: break; } @@ -57,8 +57,8 @@ bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) if (tip.GetType() != tipOther.GetType()) { - std::string typeStrToInstall = _GetTitleIdTypeStr(m_title_info.GetAppTitleId()); - std::string typeStrCurrentlyInstalled = _GetTitleIdTypeStr(tmp.GetAppTitleId()); + auto typeStrToInstall = _GetTitleIdTypeStr(m_title_info.GetAppTitleId()); + auto typeStrCurrentlyInstalled = _GetTitleIdTypeStr(tmp.GetAppTitleId()); auto wxMsg = _("It seems that there is already a title installed at the target location but it has a different type.\nCurrently installed: \'{}\' Installing: \'{}\'\n\nThis can happen for titles which were installed with very old Cemu versions.\nDo you still want to continue with the installation? It will replace the currently installed title."); wxMessageDialog dialog(this, formatWxString(wxMsg, typeStrCurrentlyInstalled, typeStrToInstall), _("Warning"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); @@ -131,8 +131,8 @@ bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) const fs::space_info targetSpace = fs::space(ActiveSettings::GetMlcPath()); if (targetSpace.free <= m_required_size) { - auto string = wxStringFormat(_("Not enough space available.\nRequired: {0} MB\nAvailable: {1} MB"), L"%lld %lld", (m_required_size / 1024 / 1024), (targetSpace.free / 1024 / 1024)); - throw std::runtime_error(string); + auto string = formatWxString(_("Not enough space available.\nRequired: {0} MB\nAvailable: {1} MB"), (m_required_size / 1024 / 1024), (targetSpace.free / 1024 / 1024)); + throw std::runtime_error(string.utf8_string()); } return true; diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index bbe1c474..e069c10a 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -2043,17 +2043,17 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event) wxMessageBox(err, _("Online Status"), wxOK | wxCENTRE | wxICON_INFORMATION); } -std::string GeneralSettings2::GetOnlineAccountErrorMessage(OnlineAccountError error) +wxString GeneralSettings2::GetOnlineAccountErrorMessage(OnlineAccountError error) { switch (error) { case OnlineAccountError::kNoAccountId: - return _("AccountId missing (The account is not connected to a NNID)").utf8_string(); + return _("AccountId missing (The account is not connected to a NNID)"); case OnlineAccountError::kNoPasswordCached: - return _("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)").utf8_string(); + return _("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)"); case OnlineAccountError::kPasswordCacheEmpty: - return _("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)").utf8_string(); + return _("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)"); case OnlineAccountError::kNoPrincipalId: - return _("PrincipalId missing").utf8_string(); + return _("PrincipalId missing"); default: return "no error"; } diff --git a/src/gui/GeneralSettings2.h b/src/gui/GeneralSettings2.h index b667faf0..2846af38 100644 --- a/src/gui/GeneralSettings2.h +++ b/src/gui/GeneralSettings2.h @@ -101,7 +101,7 @@ private: void OnShowOnlineValidator(wxCommandEvent& event); void OnOnlineEnable(wxCommandEvent& event); void OnAccountServiceChanged(wxCommandEvent& event); - std::string GetOnlineAccountErrorMessage(OnlineAccountError error); + static wxString GetOnlineAccountErrorMessage(OnlineAccountError error); // updates cemu audio devices void UpdateAudioDevice(); diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index c03c6fdf..2b618e86 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -445,7 +445,7 @@ void GraphicPacksWindow2::OnTreeSelectionChanged(wxTreeEvent& event) m_graphic_pack_name->SetLabel(wxHelper::FromUtf8(m_gp_name)); if (gp->GetDescription().empty()) - m_gp_description = _("This graphic pack has no description"); + m_gp_description = _("This graphic pack has no description").utf8_string(); else m_gp_description = gp->GetDescription(); @@ -609,7 +609,7 @@ void GraphicPacksWindow2::OnCheckForUpdates(wxCommandEvent& event) // check if enabled graphic packs are lost: const auto& new_packs = GraphicPack2::GetGraphicPacks(); - std::stringstream str; + std::stringstream lost_packs; for(const auto& p : old_packs) { if (!p->IsEnabled()) @@ -622,15 +622,16 @@ void GraphicPacksWindow2::OnCheckForUpdates(wxCommandEvent& event) if(it == new_packs.cend()) { - str << p->GetPath() << std::endl; + lost_packs << p->GetPath() << "\n"; } } - const auto packs = str.str(); - if(!packs.empty()) + const auto lost_packs_str = lost_packs.str(); + if (!lost_packs_str.empty()) { - wxMessageBox(fmt::format("{}\n \n{} \n{}", _("This update removed or renamed the following graphic packs:").utf8_string(), packs, _("You may need to set them up again.").utf8_string()), - _("Warning"), wxOK | wxCENTRE | wxICON_INFORMATION, this); + wxString message = _("This update removed or renamed the following graphic packs:"); + message << "\n \n" << lost_packs_str << " \n" << _("You may need to set them up again."); + wxMessageBox(message, _("Warning"), wxOK | wxCENTRE | wxICON_INFORMATION, this); } } } diff --git a/src/gui/MemorySearcherTool.cpp b/src/gui/MemorySearcherTool.cpp index 5e711dd9..fadebc44 100644 --- a/src/gui/MemorySearcherTool.cpp +++ b/src/gui/MemorySearcherTool.cpp @@ -472,9 +472,7 @@ bool MemorySearcherTool::VerifySearchValue() const void MemorySearcherTool::FillResultList() { - //char text[128]; - //sprintf(text, "Results (%u)", (uint32)m_searchBuffer.size()); - auto text = wxStringFormat(_("Results ({0})"), L"%llu", m_searchBuffer.size()); + auto text = formatWxString(_("Results ({0})"), m_searchBuffer.size()); m_textEntryTable->SetLabelText(text); m_listResults->DeleteAllItems(); diff --git a/src/gui/TitleManager.cpp b/src/gui/TitleManager.cpp index a36b3f74..669a1aaf 100644 --- a/src/gui/TitleManager.cpp +++ b/src/gui/TitleManager.cpp @@ -799,7 +799,7 @@ void TitleManager::SetConnected(bool state) void TitleManager::Callback_ConnectStatusUpdate(std::string statusText, DLMGR_STATUS_CODE statusCode) { TitleManager* titleManager = static_cast(DownloadManager::GetInstance()->getUserData()); - titleManager->SetDownloadStatusText(statusText); + titleManager->SetDownloadStatusText(wxString::FromUTF8(statusText)); if (statusCode == DLMGR_STATUS_CODE::FAILED) { auto* evt = new wxCommandEvent(wxEVT_DL_DISCONNECT_COMPLETE); diff --git a/src/gui/components/wxDownloadManagerList.cpp b/src/gui/components/wxDownloadManagerList.cpp index ca2d7a71..14bf5cbe 100644 --- a/src/gui/components/wxDownloadManagerList.cpp +++ b/src/gui/components/wxDownloadManagerList.cpp @@ -501,16 +501,16 @@ wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemC return wxEmptyString; } -std::string wxDownloadManagerList::GetTranslatedTitleEntryType(EntryType type) +wxString wxDownloadManagerList::GetTranslatedTitleEntryType(EntryType type) { switch (type) { case EntryType::Base: - return _("base").utf8_string(); + return _("base"); case EntryType::Update: - return _("update").utf8_string(); + return _("update"); case EntryType::DLC: - return _("DLC").utf8_string(); + return _("DLC"); default: return std::to_string(static_cast>(type)); } diff --git a/src/gui/components/wxDownloadManagerList.h b/src/gui/components/wxDownloadManagerList.h index b0051076..3a6b853a 100644 --- a/src/gui/components/wxDownloadManagerList.h +++ b/src/gui/components/wxDownloadManagerList.h @@ -150,6 +150,6 @@ private: bool SortFunc(std::span sortColumnOrder, const Type_t& v1, const Type_t& v2); static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column); - static std::string GetTranslatedTitleEntryType(EntryType entryType); + static wxString GetTranslatedTitleEntryType(EntryType entryType); std::future m_context_worker; }; diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index ebbab044..a64b49bf 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1009,15 +1009,20 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat)) { // time played - uint32 timePlayed = playTimeStat.numMinutesPlayed * 60; - if (timePlayed == 0) + uint32 minutesPlayed = playTimeStat.numMinutesPlayed; + if (minutesPlayed == 0) SetItem(index, ColumnGameTime, wxEmptyString); - else if (timePlayed < 60) - SetItem(index, ColumnGameTime, fmt::format("{} seconds", timePlayed)); - else if (timePlayed < 60 * 60) - SetItem(index, ColumnGameTime, fmt::format("{} minutes", timePlayed / 60)); + else if (minutesPlayed < 60) + SetItem(index, ColumnGameTime, formatWxString(wxPLURAL("{} minute", "{} minutes", minutesPlayed), minutesPlayed)); else - SetItem(index, ColumnGameTime, fmt::format("{} hours {} minutes", timePlayed / 3600, (timePlayed / 60) % 60)); + { + uint32 hours = minutesPlayed / 60; + uint32 minutes = minutesPlayed % 60; + wxString hoursText = formatWxString(wxPLURAL("{} hour", "{} hours", hours), hours); + wxString minutesText = formatWxString(wxPLURAL("{} minute", "{} minutes", minutes), minutes); + SetItem(index, ColumnGameTime, hoursText + " " + minutesText); + } + // last played if (playTimeStat.last_played.year != 0) { diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index bae986ca..aad46c52 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -963,20 +963,20 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu return wxEmptyString; } -std::string wxTitleManagerList::GetTranslatedTitleEntryType(EntryType type) +wxString wxTitleManagerList::GetTranslatedTitleEntryType(EntryType type) { switch (type) { case EntryType::Base: - return _("base").utf8_string(); + return _("base"); case EntryType::Update: - return _("update").utf8_string(); + return _("update"); case EntryType::Dlc: - return _("DLC").utf8_string(); + return _("DLC"); case EntryType::Save: - return _("save").utf8_string(); + return _("save"); case EntryType::System: - return _("system").utf8_string(); + return _("system"); default: return std::to_string(static_cast>(type)); } diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/components/wxTitleManagerList.h index 043c78f6..07556068 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/components/wxTitleManagerList.h @@ -132,7 +132,7 @@ private: bool SortFunc(int column, const Type_t& v1, const Type_t& v2); static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column); - static std::string GetTranslatedTitleEntryType(EntryType entryType); + static wxString GetTranslatedTitleEntryType(EntryType entryType); std::future m_context_worker; uint64 m_callbackIdTitleList; diff --git a/src/gui/wxgui.h b/src/gui/wxgui.h index 098449d6..bb4352d1 100644 --- a/src/gui/wxgui.h +++ b/src/gui/wxgui.h @@ -1,5 +1,7 @@ #pragma once +#define wxNO_UNSAFE_WXSTRING_CONV 1 + #include #ifndef WX_PRECOMP #include @@ -36,67 +38,6 @@ extern bool g_inputConfigWindowHasFocus; -// wx helper functions -#include -struct wxStringFormatParameters -{ - sint32 parameter_index; - sint32 parameter_count; - - wchar_t* token_buffer; - wchar_t* substitude_parameter; -}; - -template -wxString wxStringFormat(std::wstring& format, wxStringFormatParameters& parameters) -{ - return format; -} - -template -wxString wxStringFormat(std::wstring& format, wxStringFormatParameters& parameters, T arg, Args... args) -{ - wchar_t tmp[64]; - swprintf(tmp, 64, LR"(\{[%d]+\})", parameters.parameter_index); - const std::wregex placeholder_regex(tmp); - - auto result = format; - while (std::regex_search(result, placeholder_regex)) - { - result = std::regex_replace(result, placeholder_regex, parameters.substitude_parameter, std::regex_constants::format_first_only); - result = wxString::Format(wxString(result), arg); - } - - parameters.parameter_index++; - if (parameters.parameter_index == parameters.parameter_count) - return result; - - parameters.substitude_parameter = std::wcstok(nullptr, LR"( )", ¶meters.token_buffer); - return wxStringFormat(result, parameters, args...); -} - -template -wxString wxStringFormat(const wxString& format, const wchar_t* parameters, T... args) -{ - const auto parameter_count = std::count(parameters, parameters + wcslen(parameters), '%'); - if (parameter_count == 0) - return format; - - const auto copy = wcsdup(parameters); - - wxStringFormatParameters para; - para.substitude_parameter = std::wcstok(copy, LR"( )", ¶.token_buffer); - para.parameter_count = parameter_count; - para.parameter_index = 0; - - auto tmp_string = format.ToStdWstring(); - auto result = wxStringFormat(tmp_string, para, args...); - - free(copy); - - return result; -} - inline bool SendSliderEvent(wxSlider* slider, int new_value) { wxCommandEvent cevent(wxEVT_SLIDER, slider->GetId()); From 524188bb7aa08692a688ea7911f757a298913108 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 8 Sep 2023 02:28:51 +0200 Subject: [PATCH 022/101] Refactor more GX2 code to use LatteReg.h --- src/Cafe/CafeSystem.cpp | 2 +- src/Cafe/HW/Latte/Core/FetchShader.cpp | 4 +- src/Cafe/HW/Latte/ISA/LatteReg.h | 305 ++++++++++++++++++++- src/Cafe/HW/Latte/ISA/RegDefines.h | 2 - src/Cafe/OS/libs/gx2/GX2.cpp | 7 +- src/Cafe/OS/libs/gx2/GX2.h | 6 - src/Cafe/OS/libs/gx2/GX2_Command.cpp | 4 +- src/Cafe/OS/libs/gx2/GX2_Shader.cpp | 196 ++++++++++++- src/Cafe/OS/libs/gx2/GX2_Shader.h | 58 ++-- src/Cafe/OS/libs/gx2/GX2_shader_legacy.cpp | 214 +-------------- 10 files changed, 536 insertions(+), 262 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 8c2344ce..93ced948 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -487,7 +487,7 @@ namespace CafeSystem #if BOOST_OS_WINDOWS std::string GetWindowsNamedVersion(uint32& buildNumber) { - static char productName[256]; + char productName[256]; HKEY hKey; DWORD dwType = REG_SZ; DWORD dwSize = sizeof(productName); diff --git a/src/Cafe/HW/Latte/Core/FetchShader.cpp b/src/Cafe/HW/Latte/Core/FetchShader.cpp index b4beba4e..6c9893f9 100644 --- a/src/Cafe/HW/Latte/Core/FetchShader.cpp +++ b/src/Cafe/HW/Latte/Core/FetchShader.cpp @@ -228,13 +228,13 @@ void _fetchShaderDecompiler_parseInstruction_VTX_SEMANTIC(LatteFetchShader* pars else if (srcSelX == LatteClauseInstruction_VTX::SRC_SEL::SEL_Y) { // use alu divisor 1 - attribGroup->attrib[groupAttribIndex].aluDivisor = (sint32)contextRegister[mmVGT_INSTANCE_STEP_RATE_0 + 0]; + attribGroup->attrib[groupAttribIndex].aluDivisor = (sint32)contextRegister[Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0]; cemu_assert_debug(attribGroup->attrib[groupAttribIndex].aluDivisor > 0); } else if (srcSelX == LatteClauseInstruction_VTX::SRC_SEL::SEL_Z) { // use alu divisor 2 - attribGroup->attrib[groupAttribIndex].aluDivisor = (sint32)contextRegister[mmVGT_INSTANCE_STEP_RATE_0 + 1]; + attribGroup->attrib[groupAttribIndex].aluDivisor = (sint32)contextRegister[Latte::REGADDR::VGT_INSTANCE_STEP_RATE_1]; cemu_assert_debug(attribGroup->attrib[groupAttribIndex].aluDivisor > 0); } } diff --git a/src/Cafe/HW/Latte/ISA/LatteReg.h b/src/Cafe/HW/Latte/ISA/LatteReg.h index e539902f..7f0cf7c9 100644 --- a/src/Cafe/HW/Latte/ISA/LatteReg.h +++ b/src/Cafe/HW/Latte/ISA/LatteReg.h @@ -381,6 +381,9 @@ namespace Latte PA_SC_GENERIC_SCISSOR_TL = 0xA090, PA_SC_GENERIC_SCISSOR_BR = 0xA091, + SQ_VTX_SEMANTIC_0 = 0xA0E0, + SQ_VTX_SEMANTIC_31 = 0xA0FF, + VGT_MULTI_PRIM_IB_RESET_INDX = 0xA103, SX_ALPHA_TEST_CONTROL = 0xA104, CB_BLEND_RED = 0xA105, @@ -398,6 +401,10 @@ namespace Latte PA_CL_VPORT_ZSCALE = 0xA113, PA_CL_VPORT_ZOFFSET = 0xA114, + SPI_VS_OUT_ID_0 = 0xA185, + + SPI_VS_OUT_CONFIG = 0xA1B1, + CB_BLEND0_CONTROL = 0xA1E0, // first CB_BLEND7_CONTROL = 0xA1E7, // last @@ -408,7 +415,23 @@ namespace Latte PA_CL_CLIP_CNTL = 0xA204, PA_SU_SC_MODE_CNTL = 0xA205, PA_CL_VTE_CNTL = 0xA206, - + PA_CL_VS_OUT_CNTL = 0xA207, + + // shader program descriptors: + SQ_PGM_START_PS = 0xA210, + SQ_PGM_RESOURCES_PS = 0xA214, + SQ_PGM_EXPORTS_PS = 0xA215, + SQ_PGM_START_VS = 0xA216, + SQ_PGM_RESOURCES_VS = 0xA21A, + SQ_PGM_START_GS = 0xA21B, + SQ_PGM_RESOURCES_GS = 0xA21F, + SQ_PGM_START_ES = 0xA220, + SQ_PGM_RESOURCES_ES = 0xA224, + SQ_PGM_START_FS = 0xA225, + SQ_PGM_RESOURCES_FS = 0xA229, + + SQ_VTX_SEMANTIC_CLEAR = 0xA238, + PA_SU_POINT_SIZE = 0xA280, PA_SU_POINT_MINMAX = 0xA281, @@ -416,6 +439,35 @@ namespace Latte VGT_DMA_INDEX_TYPE = 0xA29F, // todo - verify offset + VGT_PRIMITIVEID_EN = 0xA2A1, + + VGT_MULTI_PRIM_IB_RESET_EN = 0xA2A5, + + VGT_INSTANCE_STEP_RATE_0 = 0xA2A8, + VGT_INSTANCE_STEP_RATE_1 = 0xA2A9, + + VGT_STRMOUT_BUFFER_SIZE_0 = 0xA2B4, + VGT_STRMOUT_VTX_STRIDE_0 = 0xA2B5, + VGT_STRMOUT_BUFFER_BASE_0 = 0xA2B6, + VGT_STRMOUT_BUFFER_OFFSET_0 = 0xA2B7, + VGT_STRMOUT_BUFFER_SIZE_1 = 0xA2B8, + VGT_STRMOUT_VTX_STRIDE_1 = 0xA2B9, + VGT_STRMOUT_BUFFER_BASE_1 = 0xA2BA, + VGT_STRMOUT_BUFFER_OFFSET_1 = 0xA2BB, + VGT_STRMOUT_BUFFER_SIZE_2 = 0xA2BC, + VGT_STRMOUT_VTX_STRIDE_2 = 0xA2BD, + VGT_STRMOUT_BUFFER_BASE_2 = 0xA2BE, + VGT_STRMOUT_BUFFER_OFFSET_2 = 0xA2BF, + VGT_STRMOUT_BUFFER_SIZE_3 = 0xA2C0, + VGT_STRMOUT_VTX_STRIDE_3 = 0xA2C1, + VGT_STRMOUT_BUFFER_BASE_3 = 0xA2C2, + VGT_STRMOUT_BUFFER_OFFSET_3 = 0xA2C3, + VGT_STRMOUT_BASE_OFFSET_0 = 0xA2C4, + VGT_STRMOUT_BASE_OFFSET_1 = 0xA2C5, + VGT_STRMOUT_BASE_OFFSET_2 = 0xA2C6, + VGT_STRMOUT_BASE_OFFSET_3 = 0xA2C7, + VGT_STRMOUT_BUFFER_EN = 0xA2C8, + // HiZ early stencil test? DB_SRESULTS_COMPARE_STATE0 = 0xA34A, DB_SRESULTS_COMPARE_STATE1 = 0xA34B, @@ -842,6 +894,12 @@ float get_##__regname() const \ LATTE_BITFIELD_BOOL(VTX_W0_FMT, 10); }; + struct LATTE_PA_CL_VS_OUT_CNTL : LATTEREG // 0xA207 + { + LATTE_BITFIELD(CLIP_DIST_ENA_MASK, 0, 8); + LATTE_BITFIELD(CULL_DIST_ENA_MASK, 8, 8); + }; + struct LATTE_PA_SU_POINT_SIZE : LATTEREG // 0xA280 { LATTE_BITFIELD(HEIGHT, 0, 16); @@ -909,6 +967,54 @@ float get_##__regname() const \ LATTE_BITFIELD_FULL_TYPED(INDEX_TYPE, E_INDEX_TYPE); }; + struct LATTE_VGT_PRIMITIVEID_EN : LATTEREG // 0xA2A1 + { + LATTE_BITFIELD_BOOL(PRIMITIVEID_EN, 0); + }; + + struct LATTE_VGT_MULTI_PRIM_IB_RESET_EN : LATTEREG // 0xA2A5 + { + LATTE_BITFIELD_BOOL(RESET_EN, 0); + }; + + struct LATTE_VGT_INSTANCE_STEP_RATE_X : LATTEREG // 0xA2A8-0xA2A9 + { + LATTE_BITFIELD_FULL_TYPED(STEP_RATE, uint32); + }; + + struct LATTE_VGT_STRMOUT_BUFFER_SIZE_X : LATTEREG // 0xA2B4 + index * 4 + { + LATTE_BITFIELD_FULL_TYPED(SIZE, uint32); + }; + + struct LATTE_VGT_STRMOUT_STRIDE_X : LATTEREG // 0xA2B5 + index * 4 + { + LATTE_BITFIELD_FULL_TYPED(STRIDE, uint32); + }; + + struct LATTE_VGT_STRMOUT_BUFFER_BASE_X : LATTEREG // 0xA2B6 + index * 4 + { + LATTE_BITFIELD_FULL_TYPED(BASE, uint32); + }; + + struct LATTE_VGT_STRMOUT_BUFFER_OFFSET_X : LATTEREG // 0xA2B7 + index * 4 + { + LATTE_BITFIELD_FULL_TYPED(BUFFER_OFFSET, uint32); + }; + + struct LATTE_VGT_STRMOUT_BASE_OFFSET_X : LATTEREG // 0xA2C4-0xA2C7 + { + LATTE_BITFIELD_FULL_TYPED(BASE_OFFSET, uint32); + }; + + struct LATTE_VGT_STRMOUT_BUFFER_EN : LATTEREG // 0xA2C8 + { + LATTE_BITFIELD_BOOL(BUFFER_ENABLE_0, 0); + LATTE_BITFIELD_BOOL(BUFFER_ENABLE_1, 1); + LATTE_BITFIELD_BOOL(BUFFER_ENABLE_2, 2); + LATTE_BITFIELD_BOOL(BUFFER_ENABLE_3, 3); + }; + struct LATTE_PA_SU_POLY_OFFSET_CLAMP : LATTEREG // 0xA37F { LATTE_BITFIELD_FLOAT(CLAMP); @@ -934,6 +1040,16 @@ float get_##__regname() const \ LATTE_BITFIELD_FLOAT(OFFSET); }; + struct LATTE_SQ_VTX_SEMANTIC_CLEAR : LATTEREG // 0xA238 + { + LATTE_BITFIELD_FULL_TYPED(CLEAR_MASK, uint32); // probably a bitmask + }; + + struct LATTE_SQ_VTX_SEMANTIC_X : LATTEREG // 0xA0E0 - 0xA0FF + { + LATTE_BITFIELD(SEMANTIC_ID, 0, 8); + }; + struct LATTE_SQ_TEX_RESOURCE_WORD0_N : LATTEREG // 0xE000 + index * 7 { LATTE_BITFIELD_TYPED(DIM, 0, 3, E_DIM); @@ -1154,6 +1270,65 @@ float get_##__regname() const \ LATTE_BITFIELD_TYPED(TYPE, 31, 1, E_SAMPLER_TYPE); }; + struct LATTE_SQ_PGM_START_X : LATTEREG // 0xA210 / 0xA216 / 0xA21B / 0xA220 / 0xA225 + { + LATTE_BITFIELD_FULL_TYPED(PGM_START, uint32); + }; + + struct LATTE_SQ_PGM_RESOURCES_PS : LATTEREG // 0xA214 + { + LATTE_BITFIELD(NUM_GPRS, 0, 8); + LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8); + LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN + LATTE_BITFIELD(FETCH_CACHE_LINES, 24, 3); + LATTE_BITFIELD_BOOL(UNCACHED_FIRST_INST, 28); + LATTE_BITFIELD_BOOL(CLAMP_CONSTS, 31); + }; + + struct LATTE_SQ_PGM_RESOURCES_VS : LATTEREG // 0xA21A + { + LATTE_BITFIELD(NUM_GPRS, 0, 8); + LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8); + LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN + LATTE_BITFIELD(FETCH_CACHE_LINES, 24, 3); + LATTE_BITFIELD_BOOL(UNCACHED_FIRST_INST, 28); + }; + + struct LATTE_SQ_PGM_RESOURCES_GS : LATTEREG // 0xA21F + { + LATTE_BITFIELD(NUM_GPRS, 0, 8); + LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8); + LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN + }; + + struct LATTE_SQ_PGM_RESOURCES_ES : LATTEREG // 0xA224 + { + LATTE_BITFIELD(NUM_GPRS, 0, 8); + LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8); + LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN + }; + + struct LATTE_SQ_PGM_RESOURCES_FS : LATTEREG // 0xA229 + { + LATTE_BITFIELD(NUM_GPRS, 0, 8); + LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8); + LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN + }; + + struct LATTE_SQ_XX_ITEMSIZE : LATTEREG // 0xA227 - 0xA2XX + { + // used by: + // SQ_ESGS_RING_ITEMSIZE + // SQ_GSVS_RING_ITEMSIZE + // SQ_ESTMP_RING_ITEMSIZE + // SQ_GSTMP_RING_ITEMSIZE + // SQ_VSTMP_RING_ITEMSIZE + // SQ_PSTMP_RING_ITEMSIZE + // SQ_FBUF_RING_ITEMSIZE + // SQ_REDUC_RING_ITEMSIZE + LATTE_BITFIELD(ITEMSIZE, 0, 15); + }; + struct LATTE_PA_SU_SC_MODE_CNTL : LATTEREG // 0xA205 { enum class E_FRONTFACE @@ -1185,7 +1360,32 @@ float get_##__regname() const \ LATTE_BITFIELD_BOOL(OFFSET_PARA_ENABLED, 13); // offset enable for lines and points? // additional fields? }; -} + + struct LATTE_SPI_VS_OUT_CONFIG : LATTEREG // 0xA1B1 + { + LATTE_BITFIELD_BOOL(VS_PER_COMPONENT, 0); + LATTE_BITFIELD(VS_EXPORT_COUNT, 1, 5); + LATTE_BITFIELD_BOOL(EXPORTS_FOG, 8); + LATTE_BITFIELD(VS_OUT_FOG_VEC_ADDR, 9, 5); + }; + + struct LATTE_SPI_VS_OUT_ID_N : LATTEREG // 0xA185 - 0xA18E(?) - 0xA1B2 - 0xA1B3 + { + uint8 get_SEMANTIC(sint32 index) + { + cemu_assert_debug(index < 4); + return (uint8)((v >> (index * 8)) & 0xFF); + } + + void set_SEMANTIC(sint32 index, uint8 value) + { + cemu_assert_debug(index < 4); + v &= ~(0xFF << (index * 8)); + v |= (value & 0xFF) << (index * 8); + } + }; + +}; struct _LatteRegisterSetTextureUnit { @@ -1219,6 +1419,16 @@ struct _LatteRegisterSetSamplerBorderColor static_assert(sizeof(_LatteRegisterSetSamplerBorderColor) == 16); +struct _LatteRegisterSetStreamoutBuffer +{ + Latte::LATTE_VGT_STRMOUT_BUFFER_SIZE_X SIZE; + Latte::LATTE_VGT_STRMOUT_STRIDE_X STRIDE; + Latte::LATTE_VGT_STRMOUT_BUFFER_BASE_X BASE; + Latte::LATTE_VGT_STRMOUT_BUFFER_OFFSET_X BUFFER_OFFSET; +}; + +static_assert(sizeof(_LatteRegisterSetStreamoutBuffer) == 16); + struct LatteContextRegister { uint8 padding0[0x08958]; @@ -1235,7 +1445,9 @@ struct LatteContextRegister uint8 padding_2823C[4]; /* +0x28240 */ Latte::LATTE_PA_SC_GENERIC_SCISSOR_TL PA_SC_GENERIC_SCISSOR_TL; /* +0x28244 */ Latte::LATTE_PA_SC_GENERIC_SCISSOR_BR PA_SC_GENERIC_SCISSOR_BR; - uint8 padding_28248[0x2840C - 0x28248]; + uint8 padding_28248[0x28380 - 0x28248]; + /* +0x28380 */ Latte::LATTE_SQ_VTX_SEMANTIC_X SQ_VTX_SEMANTIC_X[32]; + /* +0x28400 */ uint8 padding_28400[0x2840C - 0x28400]; /* +0x2840C */ Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_INDX VGT_MULTI_PRIM_IB_RESET_INDX; /* +0x28410 */ Latte::LATTE_SX_ALPHA_TEST_CONTROL SX_ALPHA_TEST_CONTROL; /* +0x28414 */ Latte::LATTE_CB_BLEND_RED CB_BLEND_RED; @@ -1253,7 +1465,15 @@ struct LatteContextRegister /* +0x2844C */ Latte::LATTE_PA_CL_VPORT_ZSCALE PA_CL_VPORT_ZSCALE; /* +0x28450 */ Latte::LATTE_PA_CL_VPORT_ZOFFSET PA_CL_VPORT_ZOFFSET; - uint8 padding_28450[0x28780 - 0x28454]; + uint8 padding_28450[0x28614 - 0x28454]; + + /* +0x28614 */ Latte::LATTE_SPI_VS_OUT_ID_N LATTE_SPI_VS_OUT_ID_N[10]; + + uint8 padding_2863C[0x286C4 - 0x2863C]; + + /* +0x286C4 */ Latte::LATTE_SPI_VS_OUT_CONFIG SPI_VS_OUT_CONFIG; + + uint8 padding_286C8[0x28780 - 0x286C8]; /* +0x28780 */ Latte::LATTE_CB_BLENDN_CONTROL CB_BLENDN_CONTROL[8]; @@ -1266,9 +1486,44 @@ struct LatteContextRegister /* +0x28810 */ Latte::LATTE_PA_CL_CLIP_CNTL PA_CL_CLIP_CNTL; /* +0x28814 */ Latte::LATTE_PA_SU_SC_MODE_CNTL PA_SU_SC_MODE_CNTL; /* +0x28818 */ Latte::LATTE_PA_CL_VTE_CNTL PA_CL_VTE_CNTL; + /* +0x2881C */ Latte::LATTE_PA_CL_VS_OUT_CNTL PA_CL_VS_OUT_CNTL; - uint8 padding_2881C[0x28A00 - 0x2881C]; + uint8 padding_2881C[0x28840 - 0x28820]; + /* +0x28840 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_PS; + /* +0x28844 */ uint32 ukn28844; // PS size + /* +0x28848 */ uint32 ukn28848; + /* +0x2884C */ uint32 ukn2884C; + /* +0x28850 */ Latte::LATTE_SQ_PGM_RESOURCES_PS SQ_PGM_RESOURCES_PS; + /* +0x28854 */ uint32 ukn28854; // SQ_PGM_EXPORTS_PS + /* +0x28858 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_VS; + /* +0x2885C */ uint32 ukn2885C; // VS size + /* +0x28860 */ uint32 ukn28860; + /* +0x28864 */ uint32 ukn28864; + /* +0x28868 */ Latte::LATTE_SQ_PGM_RESOURCES_VS SQ_PGM_RESOURCES_VS; + /* +0x2886C */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_GS; + /* +0x28870 */ uint32 ukn28870; // GS size + /* +0x28874 */ uint32 ukn28874; + /* +0x28878 */ uint32 ukn28878; + /* +0x2887C */ Latte::LATTE_SQ_PGM_RESOURCES_GS SQ_PGM_RESOURCES_GS; + /* +0x28880 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_ES; + /* +0x28884 */ uint32 ukn28884; // ES size + /* +0x28888 */ uint32 ukn28888; + /* +0x2888C */ uint32 ukn2888C; + /* +0x28890 */ Latte::LATTE_SQ_PGM_RESOURCES_ES SQ_PGM_RESOURCES_ES; + /* +0x28894 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_FS; + /* +0x28898 */ uint32 ukn28898; // FS size + /* +0x2889C */ uint32 ukn2889C; + /* +0x288A0 */ uint32 ukn288A0; + /* +0x288A4 */ Latte::LATTE_SQ_PGM_RESOURCES_FS SQ_PGM_RESOURCES_FS; + /* +0x288A8 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_ESGS_RING_ITEMSIZE; + /* +0x288AC */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_GSVS_RING_ITEMSIZE; + /* +0x288B0 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_ESTMP_RING_ITEMSIZE; + /* +0x288B4 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_GSTMP_RING_ITEMSIZE; + /* +0x288B8 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_VSTMP_RING_ITEMSIZE; + uint8 padding_288BC[0x288E0 - 0x288BC]; + /* +0x288E0 */ Latte::LATTE_SQ_VTX_SEMANTIC_CLEAR SQ_VTX_SEMANTIC_CLEAR; + uint8 padding_288E4[0x28A00 - 0x288E4]; /* +0x28A00 */ Latte::LATTE_PA_SU_POINT_SIZE PA_SU_POINT_SIZE; /* +0x28A04 */ Latte::LATTE_PA_SU_POINT_MINMAX PA_SU_POINT_MINMAX; @@ -1279,8 +1534,24 @@ struct LatteContextRegister uint8 padding_28A44[0x28A7C - 0x28A44]; /* +0x28A7C */ Latte::LATTE_VGT_DMA_INDEX_TYPE VGT_DMA_INDEX_TYPE; + /* +0x28A80 */ uint32 ukn28A80; + /* +0x28A84 */ Latte::LATTE_VGT_PRIMITIVEID_EN VGT_PRIMITIVEID_EN; + /* +0x28A88 */ uint32 ukn28A88; + /* +0x28A8C */ uint32 ukn28A8C; + /* +0x28A90 */ uint32 ukn28A90; + /* +0x28A94 */ Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_EN VGT_MULTI_PRIM_IB_RESET_EN; + /* +0x28A98 */ uint32 ukn28A98; + /* +0x28A9C */ uint32 ukn28A9C; + /* +0x28AA0 */ Latte::LATTE_VGT_INSTANCE_STEP_RATE_X VGT_INSTANCE_STEP_RATE_0; + /* +0x28AA4 */ Latte::LATTE_VGT_INSTANCE_STEP_RATE_X VGT_INSTANCE_STEP_RATE_1; - uint8 padding_28A80[0x28DFC - 0x28A80]; + uint8 padding_28AA8[0x28AD0 - 0x28AA8]; + + /* +0x28AD0 */ _LatteRegisterSetStreamoutBuffer VGT_STRMOUT_BUFFER_X[4]; + /* +0x28B10 */ Latte::LATTE_VGT_STRMOUT_BASE_OFFSET_X VGT_STRMOUT_BASE_OFFSET_X[4]; + /* +0x28B20 */ Latte::LATTE_VGT_STRMOUT_BUFFER_EN VGT_STRMOUT_BUFFER_EN; + + uint8 padding_28B24[0x28DFC - 0x28B24]; /* +0x28DFC */ Latte::LATTE_PA_SU_POLY_OFFSET_CLAMP PA_SU_POLY_OFFSET_CLAMP; /* +0x28E00 */ Latte::LATTE_PA_SU_POLY_OFFSET_FRONT_SCALE PA_SU_POLY_OFFSET_FRONT_SCALE; @@ -1334,6 +1605,13 @@ static_assert(offsetof(LatteContextRegister, CB_TARGET_MASK) == Latte::REGADDR:: static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_TL) == Latte::REGADDR::PA_SC_GENERIC_SCISSOR_TL * 4); static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_BR) == Latte::REGADDR::PA_SC_GENERIC_SCISSOR_BR * 4); static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_INDX) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_INDX * 4); +static_assert(offsetof(LatteContextRegister, VGT_PRIMITIVEID_EN) == Latte::REGADDR::VGT_PRIMITIVEID_EN * 4); +static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_EN) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_EN * 4); +static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_0) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0 * 4); +static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_1) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_1 * 4); +static_assert(offsetof(LatteContextRegister, VGT_STRMOUT_BUFFER_X) == Latte::REGADDR::VGT_STRMOUT_BUFFER_SIZE_0 * 4); +static_assert(offsetof(LatteContextRegister, VGT_STRMOUT_BASE_OFFSET_X) == Latte::REGADDR::VGT_STRMOUT_BASE_OFFSET_0 * 4); +static_assert(offsetof(LatteContextRegister, VGT_STRMOUT_BUFFER_EN) == Latte::REGADDR::VGT_STRMOUT_BUFFER_EN * 4); static_assert(offsetof(LatteContextRegister, SX_ALPHA_TEST_CONTROL) == Latte::REGADDR::SX_ALPHA_TEST_CONTROL * 4); static_assert(offsetof(LatteContextRegister, DB_STENCILREFMASK) == Latte::REGADDR::DB_STENCILREFMASK * 4); static_assert(offsetof(LatteContextRegister, DB_STENCILREFMASK_BF) == Latte::REGADDR::DB_STENCILREFMASK_BF * 4); @@ -1351,6 +1629,7 @@ static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_ZOFFSET) == Latte::REGA static_assert(offsetof(LatteContextRegister, PA_CL_CLIP_CNTL) == Latte::REGADDR::PA_CL_CLIP_CNTL * 4); static_assert(offsetof(LatteContextRegister, PA_SU_SC_MODE_CNTL) == Latte::REGADDR::PA_SU_SC_MODE_CNTL * 4); static_assert(offsetof(LatteContextRegister, PA_CL_VTE_CNTL) == Latte::REGADDR::PA_CL_VTE_CNTL * 4); +static_assert(offsetof(LatteContextRegister, PA_CL_VS_OUT_CNTL) == Latte::REGADDR::PA_CL_VS_OUT_CNTL * 4); static_assert(offsetof(LatteContextRegister, PA_SU_POINT_SIZE) == Latte::REGADDR::PA_SU_POINT_SIZE * 4); static_assert(offsetof(LatteContextRegister, PA_SU_POINT_MINMAX) == Latte::REGADDR::PA_SU_POINT_MINMAX * 4); static_assert(offsetof(LatteContextRegister, CB_BLENDN_CONTROL) == Latte::REGADDR::CB_BLEND0_CONTROL * 4); @@ -1363,7 +1642,21 @@ static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_FRONT_SCALE) == L static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_FRONT_OFFSET) == Latte::REGADDR::PA_SU_POLY_OFFSET_FRONT_OFFSET * 4); static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_BACK_SCALE) == Latte::REGADDR::PA_SU_POLY_OFFSET_BACK_SCALE * 4); static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_BACK_OFFSET) == Latte::REGADDR::PA_SU_POLY_OFFSET_BACK_OFFSET * 4); +static_assert(offsetof(LatteContextRegister, SQ_VTX_SEMANTIC_X) == Latte::REGADDR::SQ_VTX_SEMANTIC_0 * 4); +static_assert(offsetof(LatteContextRegister, SQ_VTX_SEMANTIC_CLEAR) == Latte::REGADDR::SQ_VTX_SEMANTIC_CLEAR * 4); static_assert(offsetof(LatteContextRegister, SQ_TEX_START_PS) == Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS * 4); static_assert(offsetof(LatteContextRegister, SQ_TEX_START_VS) == Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS * 4); static_assert(offsetof(LatteContextRegister, SQ_TEX_START_GS) == Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS * 4); static_assert(offsetof(LatteContextRegister, SQ_TEX_SAMPLER) == Latte::REGADDR::SQ_TEX_SAMPLER_WORD0_0 * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_START_PS) == Latte::REGADDR::SQ_PGM_START_PS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_PS) == Latte::REGADDR::SQ_PGM_RESOURCES_PS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_START_VS) == Latte::REGADDR::SQ_PGM_START_VS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_VS) == Latte::REGADDR::SQ_PGM_RESOURCES_VS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_START_FS) == Latte::REGADDR::SQ_PGM_START_FS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_FS) == Latte::REGADDR::SQ_PGM_RESOURCES_FS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_START_ES) == Latte::REGADDR::SQ_PGM_START_ES * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_ES) == Latte::REGADDR::SQ_PGM_RESOURCES_ES * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_START_GS) == Latte::REGADDR::SQ_PGM_START_GS * 4); +static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_GS) == Latte::REGADDR::SQ_PGM_RESOURCES_GS * 4); +static_assert(offsetof(LatteContextRegister, SPI_VS_OUT_CONFIG) == Latte::REGADDR::SPI_VS_OUT_CONFIG * 4); +static_assert(offsetof(LatteContextRegister, LATTE_SPI_VS_OUT_ID_N) == Latte::REGADDR::SPI_VS_OUT_ID_0 * 4); \ No newline at end of file diff --git a/src/Cafe/HW/Latte/ISA/RegDefines.h b/src/Cafe/HW/Latte/ISA/RegDefines.h index d28739c0..b99c2126 100644 --- a/src/Cafe/HW/Latte/ISA/RegDefines.h +++ b/src/Cafe/HW/Latte/ISA/RegDefines.h @@ -50,8 +50,6 @@ #define mmVGT_PRIMITIVEID_EN 0xA2A1 #define mmVGT_VTX_CNT_EN 0xA2AE #define mmVGT_REUSE_OFF 0xA2AD -#define mmVGT_INSTANCE_STEP_RATE_0 0xA2A8 -#define mmVGT_INSTANCE_STEP_RATE_1 0xA2A9 #define mmVGT_MAX_VTX_INDX 0xA100 #define mmVGT_MIN_VTX_INDX 0xA101 #define mmVGT_INDX_OFFSET 0xA102 diff --git a/src/Cafe/OS/libs/gx2/GX2.cpp b/src/Cafe/OS/libs/gx2/GX2.cpp index 5a1f5520..8c3fbc64 100644 --- a/src/Cafe/OS/libs/gx2/GX2.cpp +++ b/src/Cafe/OS/libs/gx2/GX2.cpp @@ -396,12 +396,7 @@ void gx2_load() osLib_addFunction("gx2", "GX2GetCurrentScanBuffer", gx2Export_GX2GetCurrentScanBuffer); // shader stuff - osLib_addFunction("gx2", "GX2GetVertexShaderGPRs", gx2Export_GX2GetVertexShaderGPRs); - osLib_addFunction("gx2", "GX2GetVertexShaderStackEntries", gx2Export_GX2GetVertexShaderStackEntries); - osLib_addFunction("gx2", "GX2GetPixelShaderGPRs", gx2Export_GX2GetPixelShaderGPRs); - osLib_addFunction("gx2", "GX2GetPixelShaderStackEntries", gx2Export_GX2GetPixelShaderStackEntries); - osLib_addFunction("gx2", "GX2SetFetchShader", gx2Export_GX2SetFetchShader); - osLib_addFunction("gx2", "GX2SetVertexShader", gx2Export_GX2SetVertexShader); + //osLib_addFunction("gx2", "GX2SetVertexShader", gx2Export_GX2SetVertexShader); osLib_addFunction("gx2", "GX2SetPixelShader", gx2Export_GX2SetPixelShader); osLib_addFunction("gx2", "GX2SetGeometryShader", gx2Export_GX2SetGeometryShader); osLib_addFunction("gx2", "GX2SetComputeShader", gx2Export_GX2SetComputeShader); diff --git a/src/Cafe/OS/libs/gx2/GX2.h b/src/Cafe/OS/libs/gx2/GX2.h index c9607ee4..b8a3f919 100644 --- a/src/Cafe/OS/libs/gx2/GX2.h +++ b/src/Cafe/OS/libs/gx2/GX2.h @@ -20,12 +20,6 @@ void gx2_load(); // shader -void gx2Export_GX2SetFetchShader(PPCInterpreter_t* hCPU); -void gx2Export_GX2GetVertexShaderGPRs(PPCInterpreter_t* hCPU); -void gx2Export_GX2GetVertexShaderStackEntries(PPCInterpreter_t* hCPU); -void gx2Export_GX2GetPixelShaderGPRs(PPCInterpreter_t* hCPU); -void gx2Export_GX2GetPixelShaderStackEntries(PPCInterpreter_t* hCPU); -void gx2Export_GX2SetVertexShader(PPCInterpreter_t* hCPU); void gx2Export_GX2SetPixelShader(PPCInterpreter_t* hCPU); void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU); void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU); diff --git a/src/Cafe/OS/libs/gx2/GX2_Command.cpp b/src/Cafe/OS/libs/gx2/GX2_Command.cpp index 8d584190..6da19741 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Command.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Command.cpp @@ -263,7 +263,7 @@ namespace GX2 if (patchType == GX2_PATCH_TYPE::VERTEX_SHADER) { - GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)obj; + GX2VertexShader* vertexShader = (GX2VertexShader*)obj; displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(vertexShader->GetProgramAddr()) >> 8; } else if (patchType == GX2_PATCH_TYPE::PIXEL_SHADER) @@ -273,7 +273,7 @@ namespace GX2 } else if (patchType == GX2_PATCH_TYPE::FETCH_SHADER) { - GX2FetchShader_t* fetchShader = (GX2FetchShader_t*)obj; + GX2FetchShader* fetchShader = (GX2FetchShader*)obj; displayData[patchOffset / 4 + 2] = memory_virtualToPhysical(fetchShader->GetProgramAddr()) >> 8; } else if (patchType == GX2_PATCH_TYPE::GEOMETRY_COPY_SHADER) diff --git a/src/Cafe/OS/libs/gx2/GX2_Shader.cpp b/src/Cafe/OS/libs/gx2/GX2_Shader.cpp index c63688eb..ad17dc49 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Shader.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Shader.cpp @@ -3,6 +3,7 @@ #include "GX2_Shader.h" #include "Cafe/HW/Latte/Core/LatteConst.h" #include "Cafe/HW/Latte/Core/LattePM4.h" +#include "Cafe/HW/Latte/ISA/LatteReg.h" #include "Cafe/HW/Latte/ISA/LatteInstructions.h" uint32 memory_getVirtualOffsetFromPointer(void* ptr); // remove once we updated everything to MEMPTR @@ -70,9 +71,9 @@ namespace GX2 static_assert(sizeof(betype) == 0x4); // calculate size of CF program subpart, includes alignment padding for clause instructions - size_t _calcFetchShaderCFCodeSize(uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode) + size_t _calcFetchShaderCFCodeSize(uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode) { - cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION); + cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION); cemu_assert_debug(tessellationMode == 0); uint32 numCFInstructions = ((attributeCount + 15) / 16) + 1; // one VTX clause can have up to 16 instructions + final CF instruction is RETURN size_t cfSize = numCFInstructions * 8; @@ -80,16 +81,16 @@ namespace GX2 return cfSize; } - size_t _calcFetchShaderClauseCodeSize(uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode) + size_t _calcFetchShaderClauseCodeSize(uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode) { - cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION); + cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION); cemu_assert_debug(tessellationMode == 0); uint32 numClauseInstructions = attributeCount; size_t clauseSize = numClauseInstructions * 16; return clauseSize; } - void _writeFetchShaderCFCode(void* programBufferOut, uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode) + void _writeFetchShaderCFCode(void* programBufferOut, uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode) { LatteCFInstruction* cfInstructionWriter = (LatteCFInstruction*)programBufferOut; uint32 attributeIndex = 0; @@ -111,7 +112,7 @@ namespace GX2 memcpy(cfInstructionWriter, &returnInstr, sizeof(LatteCFInstruction)); } - void _writeFetchShaderVTXCode(GX2FetchShader_t* fetchShader, void* programOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode) + void _writeFetchShaderVTXCode(GX2FetchShader* fetchShader, void* programOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode) { uint8* writePtr = (uint8*)programOut; // one instruction per attribute (hardcoded into _writeFetchShaderCFCode) @@ -151,7 +152,7 @@ namespace GX2 bool divisorFound = false; for (uint32 i = 0; i < numDivisors; i++) { - if (_swapEndianU32(fetchShader->divisors[i]) == attrAluDivisor) + if (fetchShader->divisors[i] == attrAluDivisor) { srcSelX = i != 0 ? 2 : 1; divisorFound = true; @@ -168,7 +169,7 @@ namespace GX2 else { srcSelX = numDivisors != 0 ? 2 : 1; - fetchShader->divisors[numDivisors] = _swapEndianU32(attrAluDivisor); + fetchShader->divisors[numDivisors] = attrAluDivisor; numDivisors++; fetchShader->divisorCount = _swapEndianU32(numDivisors); } @@ -213,9 +214,9 @@ namespace GX2 } } - uint32 GX2CalcFetchShaderSizeEx(uint32 attributeCount, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode) + uint32 GX2CalcFetchShaderSizeEx(uint32 attributeCount, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode) { - cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION); // other types are todo + cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION); // other types are todo cemu_assert_debug(tessellationMode == 0); // other modes are todo uint32 finalSize = @@ -225,9 +226,9 @@ namespace GX2 return finalSize; } - void GX2InitFetchShaderEx(GX2FetchShader_t* fetchShader, void* programBufferOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader_t::FetchShaderType fetchShaderType, uint32 tessellationMode) + void GX2InitFetchShaderEx(GX2FetchShader* fetchShader, void* programBufferOut, uint32 attributeCount, GX2AttribDescription* attributeDescription, GX2FetchShader::FetchShaderType fetchShaderType, uint32 tessellationMode) { - cemu_assert_debug(fetchShaderType == GX2FetchShader_t::FetchShaderType::NO_TESSELATION); + cemu_assert_debug(fetchShaderType == GX2FetchShader::FetchShaderType::NO_TESSELATION); cemu_assert_debug(tessellationMode == 0); /* @@ -238,7 +239,7 @@ namespace GX2 [CLAUSES] */ - memset(fetchShader, 0x00, sizeof(GX2FetchShader_t)); + memset(fetchShader, 0x00, sizeof(GX2FetchShader)); fetchShader->attribCount = _swapEndianU32(attributeCount); fetchShader->shaderPtr = (MPTR)_swapEndianU32(memory_getVirtualOffsetFromPointer(programBufferOut)); @@ -251,14 +252,181 @@ namespace GX2 shaderOutput += _calcFetchShaderClauseCodeSize(attributeCount, fetchShaderType, tessellationMode); uint32 shaderSize = (uint32)(shaderOutput - shaderStart); - cemu_assert_debug(shaderSize == GX2CalcFetchShaderSizeEx(attributeCount, GX2FetchShader_t::FetchShaderType::NO_TESSELATION, tessellationMode)); + cemu_assert_debug(shaderSize == GX2CalcFetchShaderSizeEx(attributeCount, GX2FetchShader::FetchShaderType::NO_TESSELATION, tessellationMode)); fetchShader->shaderSize = _swapEndianU32((uint32)(shaderOutput - shaderStart)); + + fetchShader->reg_SQ_PGM_RESOURCES_FS = Latte::LATTE_SQ_PGM_RESOURCES_FS().set_NUM_GPRS(2); // todo - affected by tesselation params? + } + + uint32 GX2GetVertexShaderGPRs(GX2VertexShader* vertexShader) + { + return vertexShader->regs.SQ_PGM_RESOURCES_VS.value().get_NUM_GPRS(); + } + + uint32 GX2GetVertexShaderStackEntries(GX2VertexShader* vertexShader) + { + return vertexShader->regs.SQ_PGM_RESOURCES_VS.value().get_NUM_STACK_ENTRIES(); + } + + uint32 GX2GetPixelShaderGPRs(GX2PixelShader_t* pixelShader) + { + return _swapEndianU32(pixelShader->regs[0])&0xFF; + } + + uint32 GX2GetPixelShaderStackEntries(GX2PixelShader_t* pixelShader) + { + return (_swapEndianU32(pixelShader->regs[0]>>8))&0xFF; + } + + void GX2SetFetchShader(GX2FetchShader* fetchShaderPtr) + { + GX2ReserveCmdSpace(11); + cemu_assert_debug((_swapEndianU32(fetchShaderPtr->shaderPtr) & 0xFF) == 0); + + gx2WriteGather_submit( + // setup fetch shader + pm4HeaderType3(IT_SET_CONTEXT_REG, 1+5), + Latte::REGADDR::SQ_PGM_START_FS-0xA000, + _swapEndianU32(fetchShaderPtr->shaderPtr)>>8, + _swapEndianU32(fetchShaderPtr->shaderSize)>>3, + 0x10000, // ukn (ring buffer size?) + 0x10000, // ukn (ring buffer size?) + fetchShaderPtr->reg_SQ_PGM_RESOURCES_FS, + + // write instance step + pm4HeaderType3(IT_SET_CONTEXT_REG, 1+2), + Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0-0xA000, + fetchShaderPtr->divisors[0], + fetchShaderPtr->divisors[1]); + } + + void GX2SetVertexShader(GX2VertexShader* vertexShader) + { + GX2ReserveCmdSpace(100); + + MPTR shaderProgramAddr; + uint32 shaderProgramSize; + if (vertexShader->shaderPtr) + { + // without R API + shaderProgramAddr = vertexShader->shaderPtr.GetMPTR(); + shaderProgramSize = vertexShader->shaderSize; + } + else + { + shaderProgramAddr = vertexShader->rBuffer.GetVirtualAddr(); + shaderProgramSize = vertexShader->rBuffer.GetSize(); + } + + cemu_assert_debug(shaderProgramAddr != 0); + cemu_assert_debug(shaderProgramSize != 0); + + if (vertexShader->shaderMode == GX2_SHADER_MODE::GEOMETRY_SHADER) + { + // in geometry shader mode the vertex shader is written to _ES register and almost all vs control registers are set by GX2SetGeometryShader + gx2WriteGather_submit( + pm4HeaderType3(IT_SET_CONTEXT_REG, 6), + Latte::REGADDR::SQ_PGM_START_ES-0xA000, + memory_virtualToPhysical(shaderProgramAddr)>>8, + shaderProgramSize>>3, + 0x100000, + 0x100000, + vertexShader->regs.SQ_PGM_RESOURCES_VS); // SQ_PGM_RESOURCES_VS/SQ_PGM_RESOURCES_ES + } + else + { + gx2WriteGather_submit( + /* vertex shader program */ + pm4HeaderType3(IT_SET_CONTEXT_REG, 6), + Latte::REGADDR::SQ_PGM_START_VS-0xA000, + memory_virtualToPhysical(shaderProgramAddr)>>8, // physical address + shaderProgramSize>>3, + 0x100000, + 0x100000, + vertexShader->regs.SQ_PGM_RESOURCES_VS, // SQ_PGM_RESOURCES_VS/ES + /* primitive id enable */ + pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::VGT_PRIMITIVEID_EN-0xA000, + vertexShader->regs.VGT_PRIMITIVEID_EN, + /* output config */ + pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::SPI_VS_OUT_CONFIG-0xA000, + vertexShader->regs.SPI_VS_OUT_CONFIG, + /* PA_CL_VS_OUT_CNTL */ + pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::PA_CL_VS_OUT_CNTL-0xA000, + vertexShader->regs.PA_CL_VS_OUT_CNTL + ); + + cemu_assert_debug(vertexShader->regs.SPI_VS_OUT_CONFIG.value().get_VS_PER_COMPONENT() == false); // not handled on the GPU side + + uint32 numOutputIds = vertexShader->regs.vsOutIdTableSize; + numOutputIds = std::min(numOutputIds, 0xA); + gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+numOutputIds)); + gx2WriteGather_submitU32AsBE(Latte::REGADDR::SPI_VS_OUT_ID_0-0xA000); + for(uint32 i=0; iregs.LATTE_SPI_VS_OUT_ID_N[i].value().getRawValue()); + + // todo: SQ_PGM_CF_OFFSET_VS + // todo: VGT_STRMOUT_BUFFER_EN + // stream out + if (vertexShader->usesStreamOut != 0) + { + // stride 0 + gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_0-0xA000, + vertexShader->streamOutVertexStride[0]>>2, + // stride 1 + pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_1-0xA000, + vertexShader->streamOutVertexStride[1]>>2, + // stride 2 + pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_2-0xA000, + vertexShader->streamOutVertexStride[2]>>2, + // stride 3 + pm4HeaderType3(IT_SET_CONTEXT_REG, 2), + Latte::REGADDR::VGT_STRMOUT_VTX_STRIDE_3-0xA000, + vertexShader->streamOutVertexStride[3]>>2); + } + } + // update semantic table + uint32 vsSemanticTableSize = vertexShader->regs.semanticTableSize; + if (vsSemanticTableSize > 0) + { + gx2WriteGather_submit( + pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1), + Latte::REGADDR::SQ_VTX_SEMANTIC_CLEAR-0xA000, + 0xFFFFFFFF); + if (vsSemanticTableSize == 0) + { + gx2WriteGather_submit( + pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1), + Latte::REGADDR::SQ_VTX_SEMANTIC_0-0xA000, + 0xFFFFFFFF); + } + else + { + uint32* vsSemanticTable = (uint32*)vertexShader->regs.SQ_VTX_SEMANTIC_N; + vsSemanticTableSize = std::min(vsSemanticTableSize, 32); + gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+vsSemanticTableSize)); + gx2WriteGather_submitU32AsBE(Latte::REGADDR::SQ_VTX_SEMANTIC_0-0xA000); + gx2WriteGather_submitU32AsLEArray(vsSemanticTable, vsSemanticTableSize); + } + } } void GX2ShaderInit() { cafeExportRegister("gx2", GX2CalcFetchShaderSizeEx, LogType::GX2); cafeExportRegister("gx2", GX2InitFetchShaderEx, LogType::GX2); + + cafeExportRegister("gx2", GX2GetVertexShaderGPRs, LogType::GX2); + cafeExportRegister("gx2", GX2GetVertexShaderStackEntries, LogType::GX2); + cafeExportRegister("gx2", GX2GetPixelShaderGPRs, LogType::GX2); + cafeExportRegister("gx2", GX2GetPixelShaderStackEntries, LogType::GX2); + cafeExportRegister("gx2", GX2SetFetchShader, LogType::GX2); + cafeExportRegister("gx2", GX2SetVertexShader, LogType::GX2); } } \ No newline at end of file diff --git a/src/Cafe/OS/libs/gx2/GX2_Shader.h b/src/Cafe/OS/libs/gx2/GX2_Shader.h index 1d1c79cc..960bdf95 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Shader.h +++ b/src/Cafe/OS/libs/gx2/GX2_Shader.h @@ -2,7 +2,7 @@ #include "Cafe/HW/Latte/ISA/LatteReg.h" #include "GX2_Streamout.h" -struct GX2FetchShader_t +struct GX2FetchShader { enum class FetchShaderType : uint32 { @@ -10,12 +10,12 @@ struct GX2FetchShader_t }; /* +0x00 */ betype fetchShaderType; - /* +0x04 */ uint32 _regs[1]; + /* +0x04 */ betype reg_SQ_PGM_RESOURCES_FS; /* +0x08 */ uint32 shaderSize; /* +0x0C */ MPTR shaderPtr; /* +0x10 */ uint32 attribCount; /* +0x14 */ uint32 divisorCount; - /* +0x18 */ uint32 divisors[2]; + /* +0x18 */ uint32be divisors[2]; MPTR GetProgramAddr() const { @@ -23,8 +23,8 @@ struct GX2FetchShader_t } }; -static_assert(sizeof(GX2FetchShader_t) == 0x20); -static_assert(sizeof(betype) == 4); +static_assert(sizeof(GX2FetchShader) == 0x20); +static_assert(sizeof(betype) == 4); namespace GX2 { @@ -32,19 +32,43 @@ namespace GX2 void GX2ShaderInit(); } -// code below still needs to be modernized (use betype, enum classes) +// code below still needs to be modernized (use betype, enum classes, move to namespace) +// deprecated, use GX2_SHADER_MODE enum class instead #define GX2_SHADER_MODE_UNIFORM_REGISTER 0 #define GX2_SHADER_MODE_UNIFORM_BLOCK 1 #define GX2_SHADER_MODE_GEOMETRY_SHADER 2 #define GX2_SHADER_MODE_COMPUTE_SHADER 3 -struct GX2VertexShader_t +enum class GX2_SHADER_MODE : uint32 { - /* +0x000 */ uint32 regs[52]; - /* +0x0D0 */ uint32 shaderSize; - /* +0x0D4 */ MPTR shaderPtr; - /* +0x0D8 */ uint32 shaderMode; // GX2_SHADER_MODE_* + UNIFORM_REGISTER = 0, + UNIFORM_BLOCK = 1, + GEOMETRY_SHADER = 2, + COMPUTE_SHADER = 3, +}; + +struct GX2VertexShader +{ + /* +0x000 */ + struct + { + /* +0x00 */ betype SQ_PGM_RESOURCES_VS; // compatible with SQ_PGM_RESOURCES_ES + /* +0x04 */ betype VGT_PRIMITIVEID_EN; + /* +0x08 */ betype SPI_VS_OUT_CONFIG; + /* +0x0C */ uint32be vsOutIdTableSize; + /* +0x10 */ betype LATTE_SPI_VS_OUT_ID_N[10]; + /* +0x38 */ betype PA_CL_VS_OUT_CNTL; + /* +0x3C */ uint32be uknReg15; // ? + /* +0x40 */ uint32be semanticTableSize; + /* +0x44 */ betype SQ_VTX_SEMANTIC_N[32]; + /* +0xC4 */ uint32be uknReg49; // ? + /* +0xC8 */ uint32be uknReg50; // vgt_vertex_reuse_block_cntl + /* +0xCC */ uint32be uknReg51; // vgt_hos_reuse_depth + }regs; + /* +0x0D0 */ uint32be shaderSize; + /* +0x0D4 */ MEMPTR shaderPtr; + /* +0x0D8 */ betype shaderMode; /* +0x0DC */ uint32 uniformBlockCount; /* +0x0E0 */ MPTR uniformBlockInfo; /* +0x0E4 */ uint32 uniformVarCount; @@ -57,20 +81,20 @@ struct GX2VertexShader_t /* +0x100 */ MPTR samplerInfo; /* +0x104 */ uint32 attribCount; /* +0x108 */ MPTR attribInfo; - /* +0x10C */ uint32 ringItemsize; // for GS - /* +0x110 */ uint32 usesStreamOut; - /* +0x114 */ uint32 streamOutVertexStride[GX2_MAX_STREAMOUT_BUFFERS]; + /* +0x10C */ uint32be ringItemsize; // for GS + /* +0x110 */ uint32be usesStreamOut; + /* +0x114 */ uint32be streamOutVertexStride[GX2_MAX_STREAMOUT_BUFFERS]; /* +0x124 */ GX2RBuffer rBuffer; MPTR GetProgramAddr() const { - if (_swapEndianU32(this->shaderPtr) != MPTR_NULL) - return _swapEndianU32(this->shaderPtr); + if (this->shaderPtr) + return this->shaderPtr.GetMPTR(); return this->rBuffer.GetVirtualAddr(); } }; -static_assert(sizeof(GX2VertexShader_t) == 0x134); +static_assert(sizeof(GX2VertexShader) == 0x134); typedef struct _GX2PixelShader { diff --git a/src/Cafe/OS/libs/gx2/GX2_shader_legacy.cpp b/src/Cafe/OS/libs/gx2/GX2_shader_legacy.cpp index 845292fe..1cb61a7e 100644 --- a/src/Cafe/OS/libs/gx2/GX2_shader_legacy.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_shader_legacy.cpp @@ -8,204 +8,6 @@ #include "GX2.h" #include "GX2_Shader.h" -void gx2Export_GX2SetFetchShader(PPCInterpreter_t* hCPU) -{ - cemuLog_log(LogType::GX2, "GX2SetFetchShader(0x{:08x})", hCPU->gpr[3]); - GX2ReserveCmdSpace(11); - GX2FetchShader_t* fetchShaderPtr = (GX2FetchShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); - cemu_assert_debug((_swapEndianU32(fetchShaderPtr->shaderPtr) & 0xFF) == 0); - - gx2WriteGather_submit( - // setup fetch shader - pm4HeaderType3(IT_SET_CONTEXT_REG, 1+5), - mmSQ_PGM_START_FS-0xA000, - _swapEndianU32(fetchShaderPtr->shaderPtr)>>8, // pointer divided by 256 - _swapEndianU32(fetchShaderPtr->shaderSize)>>3, // size divided by 8 - 0x10000, // ukn (ring buffer size?) - 0x10000, // ukn (ring buffer size?) - *(uint32be*)&(fetchShaderPtr->_regs[0]), - - // write instance step - pm4HeaderType3(IT_SET_CONTEXT_REG, 1+2), - mmVGT_INSTANCE_STEP_RATE_0-0xA000, - *(uint32be*)&(fetchShaderPtr->divisors[0]), - *(uint32be*)&(fetchShaderPtr->divisors[1])); - - osLib_returnFromFunction(hCPU, 0); -} - -void gx2Export_GX2GetVertexShaderGPRs(PPCInterpreter_t* hCPU) -{ - cemuLog_log(LogType::GX2, "GX2GetVertexShaderGPRs(0x{:08x})", hCPU->gpr[3]); - GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); - uint8 numGPRs = _swapEndianU32(vertexShader->regs[0])&0xFF; - osLib_returnFromFunction(hCPU, numGPRs); -} - -void gx2Export_GX2GetVertexShaderStackEntries(PPCInterpreter_t* hCPU) -{ - cemuLog_log(LogType::GX2, "GX2GetVertexShaderStackEntries(0x{:08x})", hCPU->gpr[3]); - GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); - uint8 stackEntries = (_swapEndianU32(vertexShader->regs[0])>>8)&0xFF; - osLib_returnFromFunction(hCPU, stackEntries); -} - -void gx2Export_GX2GetPixelShaderGPRs(PPCInterpreter_t* hCPU) -{ - cemuLog_log(LogType::GX2, "GX2GetPixelShaderGPRs(0x{:08x})", hCPU->gpr[3]); - GX2PixelShader_t* pixelShader = (GX2PixelShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); - uint8 stackEntries = (_swapEndianU32(pixelShader->regs[0]))&0xFF; - osLib_returnFromFunction(hCPU, stackEntries); -} - -void gx2Export_GX2GetPixelShaderStackEntries(PPCInterpreter_t* hCPU) -{ - cemuLog_log(LogType::GX2, "GX2GetPixelShaderStackEntries(0x{:08x})", hCPU->gpr[3]); - GX2PixelShader_t* pixelShader = (GX2PixelShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); - uint8 numGPRs = (_swapEndianU32(pixelShader->regs[0]>>8))&0xFF; - osLib_returnFromFunction(hCPU, numGPRs); -} - -void gx2Export_GX2SetVertexShader(PPCInterpreter_t* hCPU) -{ - cemuLog_log(LogType::GX2, "GX2SetVertexShader(0x{:08x})", hCPU->gpr[3]); - GX2ReserveCmdSpace(100); - - GX2VertexShader_t* vertexShader = (GX2VertexShader_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); - - MPTR shaderProgramAddr; - uint32 shaderProgramSize; - - if( _swapEndianU32(vertexShader->shaderPtr) != MPTR_NULL ) - { - // without R API - shaderProgramAddr = _swapEndianU32(vertexShader->shaderPtr); - shaderProgramSize = _swapEndianU32(vertexShader->shaderSize); - } - else - { - shaderProgramAddr = vertexShader->rBuffer.GetVirtualAddr(); - shaderProgramSize = vertexShader->rBuffer.GetSize(); - } - - cemu_assert_debug(shaderProgramAddr != 0); - cemu_assert_debug(shaderProgramSize != 0); - - if( _swapEndianU32(vertexShader->shaderMode) == GX2_SHADER_MODE_GEOMETRY_SHADER ) - { - // in geometry shader mode the vertex shader is written to _ES register and almost all vs control registers are set by GX2SetGeometryShader - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 6)); - gx2WriteGather_submitU32AsBE(mmSQ_PGM_START_ES-0xA000); - gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(shaderProgramAddr)>>8); - gx2WriteGather_submitU32AsBE(shaderProgramSize>>3); - gx2WriteGather_submitU32AsBE(0x100000); - gx2WriteGather_submitU32AsBE(0x100000); - gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->regs[0])); // unknown - } - else - { - gx2WriteGather_submit( - /* vertex shader program */ - pm4HeaderType3(IT_SET_CONTEXT_REG, 6), - mmSQ_PGM_START_VS-0xA000, - memory_virtualToPhysical(shaderProgramAddr)>>8, // physical address - shaderProgramSize>>3, // size - 0x100000, - 0x100000, - _swapEndianU32(vertexShader->regs[0]), // unknown - /* primitive id enable */ - pm4HeaderType3(IT_SET_CONTEXT_REG, 2), - mmVGT_PRIMITIVEID_EN-0xA000, - _swapEndianU32(vertexShader->regs[1]), - /* output config */ - pm4HeaderType3(IT_SET_CONTEXT_REG, 2), - mmSPI_VS_OUT_CONFIG-0xA000, - _swapEndianU32(vertexShader->regs[2])); - - if( (_swapEndianU32(vertexShader->regs[2]) & 1) != 0 ) - debugBreakpoint(); // per-component flag? - - // ukn - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2)); - gx2WriteGather_submitU32AsBE(mmPA_CL_VS_OUT_CNTL-0xA000); - gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->regs[14])); - - uint32 numOutputIds = _swapEndianU32(vertexShader->regs[3]); - numOutputIds = std::min(numOutputIds, 0xA); - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+numOutputIds)); - gx2WriteGather_submitU32AsBE(mmSPI_VS_OUT_ID_0-0xA000); - for(uint32 i=0; iregs[4+i])); - } - - /* - VS _regs[]: - 0 ? - 1 mmVGT_PRIMITIVEID_EN (?) - 2 mmSPI_VS_OUT_CONFIG - 3 Number of used SPI_VS_OUT_ID_* entries - 4 - 13 SPI_VS_OUT_ID_0 - SPI_VS_OUT_ID_9 - 14 pa_cl_vs_out_cntl - ... - 17 - ?? semantic table entry (input) - - ... - 50 vgt_vertex_reuse_block_cntl - 51 vgt_hos_reuse_depth - */ - - // todo: mmSQ_PGM_CF_OFFSET_VS - // todo: mmVGT_STRMOUT_BUFFER_EN - // stream out - if( _swapEndianU32(vertexShader->usesStreamOut) != 0 ) - { - // stride 0 - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2)); - gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_0-0xA000); - gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[0])>>2); - // stride 1 - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2)); - gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_1-0xA000); - gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[1])>>2); - // stride 2 - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2)); - gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_2-0xA000); - gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[2])>>2); - // stride 3 - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2)); - gx2WriteGather_submitU32AsBE(mmVGT_STRMOUT_VTX_STRIDE_3-0xA000); - gx2WriteGather_submitU32AsBE(_swapEndianU32(vertexShader->streamOutVertexStride[3])>>2); - } - } - // update semantic table - uint32 vsSemanticTableSize = _swapEndianU32(vertexShader->regs[0x40/4]); - if( vsSemanticTableSize > 0 ) - { - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1)); - gx2WriteGather_submitU32AsBE(mmSQ_VTX_SEMANTIC_CLEAR-0xA000); - gx2WriteGather_submitU32AsBE(0xFFFFFFFF); - if( vsSemanticTableSize == 0 ) - { - // todo: Figure out how this is done on real SW/HW (some vertex shaders don't have a semantic table) - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+1)); - gx2WriteGather_submitU32AsBE(mmSQ_VTX_SEMANTIC_0-0xA000); - gx2WriteGather_submitU32AsBE(0xFFFFFFFF); - } - else - { - uint32* vsSemanticTable = vertexShader->regs+(0x44/4); - vsSemanticTableSize = std::min(vsSemanticTableSize, 0x20); - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 1+vsSemanticTableSize)); - gx2WriteGather_submitU32AsBE(mmSQ_VTX_SEMANTIC_0-0xA000); - for(uint32 i=0; igpr[3]); @@ -415,14 +217,14 @@ void gx2Export_GX2SetGeometryShader(PPCInterpreter_t* hCPU) osLib_returnFromFunction(hCPU, 0); } -struct GX2ComputeShader_t +struct GX2ComputeShader { /* +0x00 */ uint32be regs[12]; /* +0x30 */ uint32be programSize; /* +0x34 */ uint32be programPtr; - /* +0x38 */ uint32 ukn38; - /* +0x3C */ uint32 ukn3C; - /* +0x40 */ uint32 ukn40[8]; + /* +0x38 */ uint32be ukn38; + /* +0x3C */ uint32be ukn3C; + /* +0x40 */ uint32be ukn40[8]; /* +0x60 */ uint32be workgroupSizeX; /* +0x64 */ uint32be workgroupSizeY; /* +0x68 */ uint32be workgroupSizeZ; @@ -431,13 +233,13 @@ struct GX2ComputeShader_t /* +0x74 */ GX2RBuffer rBuffer; }; -static_assert(offsetof(GX2ComputeShader_t, programSize) == 0x30); -static_assert(offsetof(GX2ComputeShader_t, workgroupSizeX) == 0x60); -static_assert(offsetof(GX2ComputeShader_t, rBuffer) == 0x74); +static_assert(offsetof(GX2ComputeShader, programSize) == 0x30); +static_assert(offsetof(GX2ComputeShader, workgroupSizeX) == 0x60); +static_assert(offsetof(GX2ComputeShader, rBuffer) == 0x74); void gx2Export_GX2SetComputeShader(PPCInterpreter_t* hCPU) { - ppcDefineParamTypePtr(computeShader, GX2ComputeShader_t, 0); + ppcDefineParamTypePtr(computeShader, GX2ComputeShader, 0); cemuLog_log(LogType::GX2, "GX2SetComputeShader(0x{:08x})", hCPU->gpr[3]); MPTR shaderPtr; From 62889adfde94710f280868c1b7dc4be4cc8cc229 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 8 Sep 2023 07:04:28 +0200 Subject: [PATCH 023/101] Use memory barriers in Linux fiber implementation Prevent compilers from caching TLS variables across swapcontext calls --- src/util/Fiber/FiberUnix.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/Fiber/FiberUnix.cpp b/src/util/Fiber/FiberUnix.cpp index 7d3bf05a..0d527069 100644 --- a/src/util/Fiber/FiberUnix.cpp +++ b/src/util/Fiber/FiberUnix.cpp @@ -1,5 +1,6 @@ #include "Fiber.h" #include +#include thread_local Fiber* sCurrentFiber{}; @@ -44,7 +45,9 @@ void Fiber::Switch(Fiber& targetFiber) { Fiber* leavingFiber = sCurrentFiber; sCurrentFiber = &targetFiber; + std::atomic_thread_fence(std::memory_order_seq_cst); swapcontext((ucontext_t*)(leavingFiber->m_implData), (ucontext_t*)(targetFiber.m_implData)); + std::atomic_thread_fence(std::memory_order_seq_cst); } void* Fiber::GetFiberPrivateData() From c168cf536a3ee1d6aabcddf41062136843f84e1b Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 9 Sep 2023 14:09:40 +0200 Subject: [PATCH 024/101] Vulkan: Dont immediately crash on bad pipeline cache --- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 937e3266..7987b20e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2119,18 +2119,14 @@ void VulkanRenderer::CreatePipelineCache() if (fs::exists(dir)) { const auto filename = dir / fmt::format("{:016x}.bin", CafeSystem::GetForegroundTitleId()); - auto file = std::ifstream(filename, std::ios::in | std::ios::binary | std::ios::ate); if (file.is_open()) { const size_t fileSize = file.tellg(); file.seekg(0, std::ifstream::beg); - cacheData.resize(fileSize); file.read((char*)cacheData.data(), cacheData.size()); file.close(); - - cemuLog_logDebug(LogType::Force, "pipeline cache loaded"); } } @@ -2140,7 +2136,16 @@ void VulkanRenderer::CreatePipelineCache() createInfo.pInitialData = cacheData.data(); VkResult result = vkCreatePipelineCache(m_logicalDevice, &createInfo, nullptr, &m_pipeline_cache); if (result != VK_SUCCESS) - UnrecoverableError(fmt::format("Failed to create pipeline cache: {}", result).c_str()); + { + cemuLog_log(LogType::Force, "Failed to open Vulkan pipeline cache: {}", result); + // unable to load the existing cache, start with an empty cache instead + createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + createInfo.initialDataSize = 0; + createInfo.pInitialData = nullptr; + result = vkCreatePipelineCache(m_logicalDevice, &createInfo, nullptr, &m_pipeline_cache); + if (result != VK_SUCCESS) + UnrecoverableError(fmt::format("Failed to create new Vulkan pipeline cache: {}", result).c_str()); + } size_t cache_size = 0; vkGetPipelineCacheData(m_logicalDevice, m_pipeline_cache, &cache_size, nullptr); From f04c7575d7aa206489668a8493cac993e41c51ae Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 9 Sep 2023 14:33:31 +0200 Subject: [PATCH 025/101] coreinit: Handle non-existing modules in OSDynLoad_Acquire Fixes Togabito crashing on boot coreinit: Handle non-existing modules in OSDynLoad_Acquire --- src/Cafe/CafeSystem.cpp | 2 +- .../HW/Espresso/Recompiler/PPCRecompiler.cpp | 2 + src/Cafe/OS/RPL/rpl.cpp | 152 ++++++++++-------- src/Cafe/OS/RPL/rpl.h | 5 +- src/Cafe/OS/RPL/rpl_structs.h | 8 +- .../OS/libs/coreinit/coreinit_DynLoad.cpp | 6 +- src/Cafe/OS/libs/swkbd/swkbd.cpp | 2 - 7 files changed, 102 insertions(+), 75 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 93ced948..dd761f6e 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -165,7 +165,7 @@ void LoadMainExecutable() { // RPX RPLLoader_AddDependency(_pathToExecutable.c_str()); - applicationRPX = rpl_loadFromMem(rpxData, rpxSize, (char*)_pathToExecutable.c_str()); + applicationRPX = RPLLoader_LoadFromMemory(rpxData, rpxSize, (char*)_pathToExecutable.c_str()); if (!applicationRPX) { wxMessageBox(_("Failed to run this title because the executable is damaged")); diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp index 6b830563..f4d063fa 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp @@ -325,6 +325,8 @@ void PPCRecompiler_thread() PPCRecompilerState.recompilerSpinlock.unlock(); PPCRecompiler_recompileAtAddress(enterAddress); + if(s_recompilerThreadStopSignal) + return; } } } diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index c9683683..48c7acc4 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -39,20 +39,22 @@ VHeap rplLoaderHeap_workarea(nullptr, MEMORY_RPLLOADER_AREA_SIZE); PPCCodeHeap rplLoaderHeap_lowerAreaCodeMem2(nullptr, MEMORY_CODE_TRAMPOLINE_AREA_SIZE); PPCCodeHeap rplLoaderHeap_codeArea2(nullptr, MEMORY_CODEAREA_SIZE); -bool rplLoader_applicationHasMemoryControl = false; -uint32 rplLoader_maxCodeAddress = 0; // highest used code address - ChunkedFlatAllocator<64 * 1024> g_heapTrampolineArea; -std::vector rplDependencyList = std::vector(); +std::vector rplDependencyList; RPLModule* rplModuleList[256]; sint32 rplModuleCount = 0; -uint32 _currentTLSModuleIndex = 1; // value 0 is reserved - +bool rplLoader_applicationHasMemoryControl = false; +uint32 rplLoader_maxCodeAddress = 0; // highest used code address +uint32 rplLoader_currentTLSModuleIndex = 1; // value 0 is reserved +uint32 rplLoader_currentHandleCounter = 0x00001000; +sint16 rplLoader_currentTlsModuleIndex = 0x0001; +RPLModule* rplLoader_mainModule = nullptr; uint32 rplLoader_sdataAddr = MPTR_NULL; // r13 uint32 rplLoader_sdata2Addr = MPTR_NULL; // r2 +uint32 rplLoader_currentDataAllocatorAddr = 0x10000000; std::map g_map_callableExports; @@ -110,8 +112,6 @@ MPTR RPLLoader_AllocateCodeSpace(uint32 size, uint32 alignment) return codeAddr; } -uint32 rpl3_currentDataAllocatorAddr = 0x10000000; - uint32 RPLLoader_AllocateDataSpace(RPLModule* rpl, uint32 size, uint32 alignment) { if (rplLoader_applicationHasMemoryControl) @@ -121,9 +121,9 @@ uint32 RPLLoader_AllocateDataSpace(RPLModule* rpl, uint32 size, uint32 alignment PPCCoreCallback(rpl->funcAlloc.value(), size, alignment, memPtr.GetPointer()); return (uint32)*(memPtr.GetPointer()); } - rpl3_currentDataAllocatorAddr = (rpl3_currentDataAllocatorAddr + alignment - 1)&~(alignment-1); - uint32 mem = rpl3_currentDataAllocatorAddr; - rpl3_currentDataAllocatorAddr += size; + rplLoader_currentDataAllocatorAddr = (rplLoader_currentDataAllocatorAddr + alignment - 1) & ~(alignment - 1); + uint32 mem = rplLoader_currentDataAllocatorAddr; + rplLoader_currentDataAllocatorAddr += size; return mem; } @@ -134,7 +134,7 @@ void RPLLoader_FreeData(RPLModule* rpl, void* ptr) uint32 RPLLoader_GetDataAllocatorAddr() { - return (rpl3_currentDataAllocatorAddr + 0xFFF)&(~0xFFF); + return (rplLoader_currentDataAllocatorAddr + 0xFFF) & (~0xFFF); } uint32 RPLLoader_GetMaxCodeOffset() @@ -1385,12 +1385,11 @@ bool RPLLoader_HandleRelocs(RPLModule* rplLoaderContext, std::span 0); - sint32 startIndex = inputLen - 1; + cemu_assert(!input.empty()); + size_t startIndex = input.size() - 1; while (startIndex > 0) { if (input[startIndex] == '/') @@ -1401,23 +1400,20 @@ void _RPLLoader_ExtractModuleNameFromPath(char* output, const char* input) startIndex--; } // cut off after '.' - sint32 endIndex = startIndex; - while (endIndex <= inputLen) + size_t endIndex = startIndex; + while (endIndex < input.size()) { if (input[endIndex] == '.') break; endIndex++; } - sint32 nameLen = endIndex - startIndex; + size_t nameLen = endIndex - startIndex; cemu_assert(nameLen != 0); - nameLen = std::min(nameLen, RPL_MODULE_NAME_LENGTH-1); - memcpy(output, input + startIndex, nameLen); + nameLen = std::min(nameLen, RPL_MODULE_NAME_LENGTH-1); + memcpy(output, input.data() + startIndex, nameLen); output[nameLen] = '\0'; // convert to lower case - for (sint32 i = 0; i < nameLen; i++) - { - output[i] = _ansiToLower(output[i]); - } + std::for_each(output, output + nameLen, [](char& c) {c = _ansiToLower(c);}); } void RPLLoader_InitState() @@ -1432,27 +1428,6 @@ void RPLLoader_InitState() RPLLoader_ResetState(); } -void RPLLoader_ResetState() -{ - // unload all RPL modules - while (rplModuleCount > 0) - RPLLoader_UnloadModule(rplModuleList[0]); - rplDependencyList.clear(); - // unload all remaining symbols - rplSymbolStorage_unloadAll(); - // free all code imports - g_heapTrampolineArea.releaseAll(); - list_mappedFunctionImports.clear(); - g_map_callableExports.clear(); - - rplLoader_applicationHasMemoryControl = false; - rplLoader_maxCodeAddress = 0; - rpl3_currentDataAllocatorAddr = 0x10000000; - _currentTLSModuleIndex = 1; - rplLoader_sdataAddr = MPTR_NULL; - rplLoader_sdata2Addr = MPTR_NULL; -} - void RPLLoader_BeginCemuhookCRC(RPLModule* rpl) { // calculate some values required for CRC @@ -1610,7 +1585,7 @@ void RPLLoader_InitModuleAllocator(RPLModule* rpl) } // map rpl into memory, but do not resolve relocs and imports yet -RPLModule* rpl_loadFromMem(uint8* rplData, sint32 size, char* name) +RPLModule* RPLLoader_LoadFromMemory(uint8* rplData, sint32 size, char* name) { char moduleName[RPL_MODULE_NAME_LENGTH]; _RPLLoader_ExtractModuleNameFromPath(moduleName, name); @@ -1699,7 +1674,7 @@ RPLModule* rpl_loadFromMem(uint8* rplData, sint32 size, char* name) return rpl; } -void RPLLoader_flushMemory(RPLModule* rpl) +void RPLLoader_FlushMemory(RPLModule* rpl) { // invalidate recompiler cache PPCRecompiler_invalidateRange(rpl->regionMappingBase_text.GetMPTR(), rpl->regionMappingBase_text.GetMPTR() + rpl->regionSize_text); @@ -1755,7 +1730,7 @@ void RPLLoader_LinkSingleModule(RPLModule* rplLoaderContext, bool resolveOnlyExp else RPLLoader_HandleRelocs(rplLoaderContext, sharedImportTracking, 0); - RPLLoader_flushMemory(rplLoaderContext); + RPLLoader_FlushMemory(rplLoaderContext); } void RPLLoader_LoadSectionDebugSymbols(RPLModule* rplLoaderContext, rplSectionEntryNew_t* section, int symtabSectionIndex) @@ -1919,8 +1894,24 @@ uint32 RPLLoader_GetModuleEntrypoint(RPLModule* rplLoaderContext) return rplLoaderContext->entrypoint; } -uint32 rplLoader_currentHandleCounter = 0x00001000; -sint16 rplLoader_currentTlsModuleIndex = 0x0001; +// takes a module name without extension, returns true if the RPL module is a known Cafe OS module +bool RPLLoader_IsKnownCafeOSModule(std::string_view name) +{ + static std::unordered_set s_systemModules556 = { + "avm","camera","coreinit","dc","dmae","drmapp","erreula", + "gx2","h264","lzma920","mic","nfc","nio_prof","nlibcurl", + "nlibnss","nlibnss2","nn_ac","nn_acp","nn_act","nn_aoc","nn_boss", + "nn_ccr","nn_cmpt","nn_dlp","nn_ec","nn_fp","nn_hai","nn_hpad", + "nn_idbe","nn_ndm","nn_nets2","nn_nfp","nn_nim","nn_olv","nn_pdm", + "nn_save","nn_sl","nn_spm","nn_temp","nn_uds","nn_vctl","nsysccr", + "nsyshid","nsyskbd","nsysnet","nsysuhs","nsysuvd","ntag","padscore", + "proc_ui","sndcore2","snduser2","snd_core","snd_user","swkbd","sysapp", + "tcl","tve","uac","uac_rpl","usb_mic","uvc","uvd","vpad","vpadbase", + "zlib125"}; + std::string nameLower{name}; + std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), _ansiToLower); + return s_systemModules556.contains(nameLower); +} // increment reference counter for module void RPLLoader_AddDependency(const char* name) @@ -1946,18 +1937,18 @@ void RPLLoader_AddDependency(const char* name) { if (strcmp(moduleName, dep->modulename) == 0) { - // entry already exists, increment reference counter dep->referenceCount++; return; } } // add new entry - rplDependency_t* newDependency = new rplDependency_t(); + RPLDependency* newDependency = new RPLDependency(); strcpy(newDependency->modulename, moduleName); newDependency->referenceCount = 1; newDependency->coreinitHandle = rplLoader_currentHandleCounter; newDependency->tlsModuleIndex = rplLoader_currentTlsModuleIndex; - rplLoader_currentTlsModuleIndex++; + newDependency->isCafeOSModule = RPLLoader_IsKnownCafeOSModule(moduleName); + rplLoader_currentTlsModuleIndex++; // todo - delay handle and tls allocation until the module is actually loaded. It may not exist rplLoader_currentHandleCounter++; if (rplLoader_currentTlsModuleIndex == 0x7FFF) cemuLog_log(LogType::Force, "RPLLoader: Exhausted TLS module indices pool"); @@ -2005,6 +1996,18 @@ void RPLLoader_RemoveDependency(const char* name) } } +bool RPLLoader_HasDependency(std::string_view name) +{ + char moduleName[RPL_MODULE_NAME_LENGTH]; + _RPLLoader_ExtractModuleNameFromPath(moduleName, name); + for (const auto& dep : rplDependencyList) + { + if (strcmp(moduleName, dep->modulename) == 0) + return true; + } + return false; +} + // decrement reference counter for dependency by module handle void RPLLoader_RemoveDependency(uint32 handle) { @@ -2030,6 +2033,9 @@ uint32 RPLLoader_GetHandleByModuleName(const char* name) { if (strcmp(moduleName, dep->modulename) == 0) { + cemu_assert_debug(dep->loadAttempted); + if (!dep->isCafeOSModule && !dep->rplLoaderContext) + return RPL_INVALID_HANDLE; // module not found return dep->coreinitHandle; } } @@ -2062,21 +2068,21 @@ bool RPLLoader_GetTLSDataByTLSIndex(sint16 tlsModuleIndex, uint8** tlsData, sint return true; } -bool RPLLoader_LoadFromVirtualPath(rplDependency_t* dependency, char* filePath) +bool RPLLoader_LoadFromVirtualPath(RPLDependency* dependency, char* filePath) { uint32 rplSize = 0; uint8* rplData = fsc_extractFile(filePath, &rplSize); if (rplData) { cemuLog_logDebug(LogType::Force, "Loading: {}", filePath); - dependency->rplLoaderContext = rpl_loadFromMem(rplData, rplSize, filePath); + dependency->rplLoaderContext = RPLLoader_LoadFromMemory(rplData, rplSize, filePath); free(rplData); return true; } return false; } -void RPLLoader_LoadDependency(rplDependency_t* dependency) +void RPLLoader_LoadDependency(RPLDependency* dependency) { dependency->loadAttempted = true; // check if module is already loaded @@ -2084,11 +2090,9 @@ void RPLLoader_LoadDependency(rplDependency_t* dependency) { if(!boost::iequals(rplModuleList[i]->moduleName2, dependency->modulename)) continue; - // already loaded dependency->rplLoaderContext = rplModuleList[i]; return; } - // attempt to load rpl from various locations char filePath[RPL_MODULE_PATH_LENGTH]; // check if path is absolute if (dependency->filepath[0] == '/') @@ -2097,7 +2101,7 @@ void RPLLoader_LoadDependency(rplDependency_t* dependency) RPLLoader_LoadFromVirtualPath(dependency, filePath); return; } - // attempt to load rpl from internal folder + // attempt to load rpl from code directory of current title strcpy_s(filePath, "/internal/current_title/code/"); strcat_s(filePath, dependency->filepath); // except if it is blacklisted @@ -2119,7 +2123,8 @@ void RPLLoader_LoadDependency(rplDependency_t* dependency) if (fileData) { cemuLog_log(LogType::Force, "Loading RPL: /cafeLibs/{}", dependency->filepath); - dependency->rplLoaderContext = rpl_loadFromMem(fileData->data(), fileData->size(), dependency->filepath); + dependency->rplLoaderContext = RPLLoader_LoadFromMemory(fileData->data(), fileData->size(), + dependency->filepath); return; } } @@ -2168,8 +2173,6 @@ void RPLLoader_UpdateDependencies() RPLLoader_Link(); } -RPLModule* rplLoader_mainModule = nullptr; - void RPLLoader_SetMainModule(RPLModule* rplLoaderContext) { rplLoaderContext->entrypointCalled = true; @@ -2250,7 +2253,7 @@ uint32 RPLLoader_FindModuleOrHLEExport(uint32 moduleHandle, bool isData, const c { // find dependency from handle RPLModule* rplLoaderContext = nullptr; - rplDependency_t* dependency = nullptr; + RPLDependency* dependency = nullptr; for (auto& dep : rplDependencyList) { if (dep->coreinitHandle == moduleHandle) @@ -2379,3 +2382,24 @@ void RPLLoader_ReleaseCodeCaveMem(MEMPTR addr) { heapCodeCaveArea.free(addr.GetMPTR()); } + +void RPLLoader_ResetState() +{ + // unload all RPL modules + while (rplModuleCount > 0) + RPLLoader_UnloadModule(rplModuleList[0]); + rplDependencyList.clear(); + // unload all remaining symbols + rplSymbolStorage_unloadAll(); + // free all code imports + g_heapTrampolineArea.releaseAll(); + list_mappedFunctionImports.clear(); + g_map_callableExports.clear(); + rplLoader_applicationHasMemoryControl = false; + rplLoader_maxCodeAddress = 0; + rplLoader_currentDataAllocatorAddr = 0x10000000; + rplLoader_currentTLSModuleIndex = 1; + rplLoader_sdataAddr = MPTR_NULL; + rplLoader_sdata2Addr = MPTR_NULL; + rplLoader_mainModule = nullptr; +} diff --git a/src/Cafe/OS/RPL/rpl.h b/src/Cafe/OS/RPL/rpl.h index ced7e83c..1075ee58 100644 --- a/src/Cafe/OS/RPL/rpl.h +++ b/src/Cafe/OS/RPL/rpl.h @@ -2,7 +2,7 @@ struct RPLModule; -#define RPL_INVALID_HANDLE (0xFFFFFFFF) +#define RPL_INVALID_HANDLE 0xFFFFFFFF void RPLLoader_InitState(); void RPLLoader_ResetState(); @@ -14,7 +14,7 @@ MPTR RPLLoader_AllocateCodeSpace(uint32 size, uint32 alignment); uint32 RPLLoader_GetMaxCodeOffset(); uint32 RPLLoader_GetDataAllocatorAddr(); -RPLModule* rpl_loadFromMem(uint8* rplData, sint32 size, char* name); +RPLModule* RPLLoader_LoadFromMemory(uint8* rplData, sint32 size, char* name); uint32 rpl_mapHLEImport(RPLModule* rplLoaderContext, const char* rplName, const char* funcName, bool functionMustExist); void RPLLoader_Link(); @@ -29,6 +29,7 @@ void RPLLoader_NotifyControlPassedToApplication(); void RPLLoader_AddDependency(const char* name); void RPLLoader_RemoveDependency(uint32 handle); +bool RPLLoader_HasDependency(std::string_view name); void RPLLoader_UpdateDependencies(); uint32 RPLLoader_GetHandleByModuleName(const char* name); diff --git a/src/Cafe/OS/RPL/rpl_structs.h b/src/Cafe/OS/RPL/rpl_structs.h index 71be960c..998ec8d7 100644 --- a/src/Cafe/OS/RPL/rpl_structs.h +++ b/src/Cafe/OS/RPL/rpl_structs.h @@ -225,17 +225,17 @@ struct RPLModule }; -typedef struct +struct RPLDependency { char modulename[RPL_MODULE_NAME_LENGTH]; char filepath[RPL_MODULE_PATH_LENGTH]; bool loadAttempted; - //bool isHLEModule; // determined to be a HLE module - RPLModule* rplLoaderContext; // context of loaded module + bool isCafeOSModule; // name is a known Cafe OS RPL + RPLModule* rplLoaderContext; // context of loaded module, can be nullptr for HLE COS modules sint32 referenceCount; uint32 coreinitHandle; // fake handle for coreinit sint16 tlsModuleIndex; // tls module index assigned to this dependency -}rplDependency_t; +}; RPLModule* RPLLoader_FindModuleByCodeAddr(uint32 addr); RPLModule* RPLLoader_FindModuleByDataAddr(uint32 addr); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp index c8b05124..546501b6 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp @@ -86,8 +86,7 @@ namespace coreinit } // search for loaded modules with matching name uint32 rplHandle = RPLLoader_GetHandleByModuleName(libName); - - if (rplHandle == RPL_INVALID_HANDLE) + if (rplHandle == RPL_INVALID_HANDLE && !RPLLoader_HasDependency(libName)) { RPLLoader_AddDependency(libName); RPLLoader_UpdateDependencies(); @@ -100,7 +99,10 @@ namespace coreinit else *moduleHandleOut = rplHandle; if (rplHandle == RPL_INVALID_HANDLE) + { + cemuLog_logDebug(LogType::Force, "OSDynLoad_Acquire() failed to load module '{}'", libName); return 0xFFFCFFE9; // module not found + } return 0; } diff --git a/src/Cafe/OS/libs/swkbd/swkbd.cpp b/src/Cafe/OS/libs/swkbd/swkbd.cpp index 6cc88874..d30992b0 100644 --- a/src/Cafe/OS/libs/swkbd/swkbd.cpp +++ b/src/Cafe/OS/libs/swkbd/swkbd.cpp @@ -276,7 +276,6 @@ void swkbdExport_SwkbdDisappearKeyboard(PPCInterpreter_t* hCPU) void swkbdExport_SwkbdGetInputFormString(PPCInterpreter_t* hCPU) { - debug_printf("SwkbdGetInputFormString__3RplFv LR: %08x\n", hCPU->spr.LR); for(sint32 i=0; iformStringLength; i++) { swkbdInternalState->formStringBufferBE[i] = _swapEndianU16(swkbdInternalState->formStringBuffer[i]); @@ -287,7 +286,6 @@ void swkbdExport_SwkbdGetInputFormString(PPCInterpreter_t* hCPU) void swkbdExport_SwkbdIsDecideOkButton(PPCInterpreter_t* hCPU) { - debug_printf("SwkbdIsDecideOkButton__3RplFPb LR: %08x\n", hCPU->spr.LR); if (swkbdInternalState->decideButtonWasPressed) osLib_returnFromFunction(hCPU, 1); else From fda5ec269741ceb6b25628e365714055363f2a17 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 10 Sep 2023 08:13:53 +0200 Subject: [PATCH 026/101] ih264d: Small optimizations and experiments with multi-threading Using the multi-threaded decoder doesn't seem to be worth it but at least we have a way to enable it now --- dependencies/ih264d/CMakeLists.txt | 6 +++ dependencies/ih264d/common/ithread.c | 50 +++++++++++++++---- .../ih264d/common/x86/ih264_platform_macros.h | 4 +- src/Cafe/OS/libs/h264_avc/H264Dec.cpp | 16 +++++- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/dependencies/ih264d/CMakeLists.txt b/dependencies/ih264d/CMakeLists.txt index 212cf346..d97d6dda 100644 --- a/dependencies/ih264d/CMakeLists.txt +++ b/dependencies/ih264d/CMakeLists.txt @@ -183,4 +183,10 @@ endif() if(MSVC) set_property(TARGET ih264d PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + +# tune settings for slightly better performance +target_compile_options(ih264d PRIVATE $<$:/Oi>) # enable intrinsic functions +target_compile_options(ih264d PRIVATE $<$:/Ot>) # favor speed +target_compile_options(ih264d PRIVATE "/GS-") # disable runtime checks + endif() diff --git a/dependencies/ih264d/common/ithread.c b/dependencies/ih264d/common/ithread.c index d710e323..2c25bdb0 100644 --- a/dependencies/ih264d/common/ithread.c +++ b/dependencies/ih264d/common/ithread.c @@ -85,28 +85,59 @@ UWORD32 ithread_get_mutex_lock_size(void) return sizeof(CRITICAL_SECTION); } +struct _ithread_launch_param +{ + void (*startFunc)(void* argument); + void* argument; +}; + +DWORD WINAPI _ithread_WinThreadStartRoutine(LPVOID lpThreadParameter) +{ + struct _ithread_launch_param* param = (struct _ithread_launch_param*)lpThreadParameter; + typedef void *(*ThreadStartRoutineType)(void *); + ThreadStartRoutineType pfnThreadRoutine = (ThreadStartRoutineType)param->startFunc; + void* arg = param->argument; + free(param); + pfnThreadRoutine(arg); + return 0; +} + WORD32 ithread_create(void* thread_handle, void* attribute, void* strt, void* argument) { - //UNUSED(attribute); - //return pthread_create((pthread_t*)thread_handle, NULL, (void* (*)(void*)) strt, argument); - __debugbreak(); + UNUSED(attribute); + struct _ithread_launch_param* param = malloc(sizeof(struct _ithread_launch_param)); + param->startFunc = (void (*)(void*))strt; + param->argument = argument; + HANDLE *handle = (HANDLE*)thread_handle; + *handle = CreateThread(NULL, 0, _ithread_WinThreadStartRoutine, param, 0, NULL); + if(*handle == NULL) + { + return -1; + } return 0; } WORD32 ithread_join(void* thread_handle, void** val_ptr) { //UNUSED(val_ptr); - //pthread_t* pthread_handle = (pthread_t*)thread_handle; - //return pthread_join(*pthread_handle, NULL); - - __debugbreak(); - return 0; + HANDLE *handle = (HANDLE*)thread_handle; + DWORD result = WaitForSingleObject(*handle, INFINITE); + if(result == WAIT_OBJECT_0) + { + CloseHandle(*handle); + return 0; + } + else + { + return -1; + } } WORD32 ithread_get_mutex_struct_size(void) { return sizeof(CRITICAL_SECTION); } + WORD32 ithread_mutex_init(void* mutex) { InitializeCriticalSection((LPCRITICAL_SECTION)mutex); @@ -153,7 +184,6 @@ UWORD32 ithread_get_sem_struct_size(void) //return(sizeof(sem_t)); } - WORD32 ithread_sem_init(void* sem, WORD32 pshared, UWORD32 value) { __debugbreak(); @@ -168,7 +198,6 @@ WORD32 ithread_sem_post(void* sem) //return sem_post((sem_t*)sem); } - WORD32 ithread_sem_wait(void* sem) { __debugbreak(); @@ -176,7 +205,6 @@ WORD32 ithread_sem_wait(void* sem) //return sem_wait((sem_t*)sem); } - WORD32 ithread_sem_destroy(void* sem) { __debugbreak(); diff --git a/dependencies/ih264d/common/x86/ih264_platform_macros.h b/dependencies/ih264d/common/x86/ih264_platform_macros.h index ebc1b106..22de33d6 100644 --- a/dependencies/ih264d/common/x86/ih264_platform_macros.h +++ b/dependencies/ih264d/common/x86/ih264_platform_macros.h @@ -79,10 +79,8 @@ static inline int __builtin_clz(unsigned x) { unsigned long n; - if (x == 0) - return 32; _BitScanReverse(&n, x); - return 31 - n; + return n ^ 31; } static inline int __builtin_ctz(unsigned x) { diff --git a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp index 88ce272a..d88a29d4 100644 --- a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp +++ b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp @@ -254,6 +254,8 @@ namespace H264 m_codecCtx->pv_fxns = (void*)&ih264d_api_function; m_codecCtx->u4_size = sizeof(iv_obj_t); + SetDecoderCoreCount(1); + m_isBufferedMode = isBufferedMode; UpdateParameters(false); @@ -278,6 +280,19 @@ namespace H264 m_codecCtx = nullptr; } + void SetDecoderCoreCount(uint32 coreCount) + { + ih264d_ctl_set_num_cores_ip_t s_set_cores_ip; + ih264d_ctl_set_num_cores_op_t s_set_cores_op; + s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_set_cores_ip.e_sub_cmd = (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES; + s_set_cores_ip.u4_num_cores = coreCount; // valid numbers are 1-4 + s_set_cores_ip.u4_size = sizeof(ih264d_ctl_set_num_cores_ip_t); + s_set_cores_op.u4_size = sizeof(ih264d_ctl_set_num_cores_op_t); + IV_API_CALL_STATUS_T status = ih264d_api_function(m_codecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op); + cemu_assert(status == IV_SUCCESS); + } + static bool GetImageInfo(uint8* stream, uint32 length, uint32& imageWidth, uint32& imageHeight) { // create temporary decoder @@ -702,7 +717,6 @@ namespace H264 decodeResult = m_bufferedResults.front(); m_bufferedResults.erase(m_bufferedResults.begin()); } - private: iv_obj_t* m_codecCtx{nullptr}; bool m_hasBufferSizeInfo{ false }; From b902aa20489fbeac4ec8db2ffec95c5fa9da05fd Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:59:30 +0200 Subject: [PATCH 027/101] Logging: Refactor and optimizations --- .../coreinit/coreinit_Synchronization.cpp | 62 ++++++------ src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp | 44 ++++----- src/Cafe/OS/libs/nn_olv/nn_olv.cpp | 18 ++-- .../nn_olv/nn_olv_DownloadCommunityTypes.h | 38 +++---- .../OS/libs/nn_olv/nn_olv_InitializeTypes.h | 18 ++-- src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp | 98 +++++++++---------- .../libs/nn_olv/nn_olv_UploadCommunityTypes.h | 38 +++---- .../libs/nn_olv/nn_olv_UploadFavoriteTypes.h | 32 +++--- src/Cemu/Logging/CemuLogging.cpp | 71 +++++--------- src/Cemu/Logging/CemuLogging.h | 80 ++++++++------- src/config/ActiveSettings.h | 4 +- src/config/CemuConfig.cpp | 6 +- src/config/CemuConfig.h | 5 +- src/config/ConfigValue.h | 2 +- src/gui/MainWindow.cpp | 13 ++- 15 files changed, 263 insertions(+), 266 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp index 92c90a9d..9e5de19e 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp @@ -621,49 +621,49 @@ namespace coreinit OSInitEvent(g_rendezvousEvent.GetPtr(), OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO); // OSEvent - cafeExportRegister("coreinit", OSInitEvent, LogType::ThreadSync); - cafeExportRegister("coreinit", OSInitEventEx, LogType::ThreadSync); - cafeExportRegister("coreinit", OSResetEvent, LogType::ThreadSync); - cafeExportRegister("coreinit", OSWaitEvent, LogType::ThreadSync); - cafeExportRegister("coreinit", OSWaitEventWithTimeout, LogType::ThreadSync); - cafeExportRegister("coreinit", OSSignalEvent, LogType::ThreadSync); - cafeExportRegister("coreinit", OSSignalEventAll, LogType::ThreadSync); + cafeExportRegister("coreinit", OSInitEvent, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSInitEventEx, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSResetEvent, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSWaitEvent, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSWaitEventWithTimeout, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSSignalEvent, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSSignalEventAll, LogType::CoreinitThreadSync); // OSRendezvous - cafeExportRegister("coreinit", OSInitRendezvous, LogType::ThreadSync); - cafeExportRegister("coreinit", OSWaitRendezvous, LogType::ThreadSync); + cafeExportRegister("coreinit", OSInitRendezvous, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSWaitRendezvous, LogType::CoreinitThreadSync); // OSMutex - cafeExportRegister("coreinit", OSInitMutex, LogType::ThreadSync); - cafeExportRegister("coreinit", OSInitMutexEx, LogType::ThreadSync); - cafeExportRegister("coreinit", OSLockMutex, LogType::ThreadSync); - cafeExportRegister("coreinit", OSTryLockMutex, LogType::ThreadSync); - cafeExportRegister("coreinit", OSUnlockMutex, LogType::ThreadSync); + cafeExportRegister("coreinit", OSInitMutex, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSInitMutexEx, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSLockMutex, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSTryLockMutex, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSUnlockMutex, LogType::CoreinitThreadSync); // OSCond - cafeExportRegister("coreinit", OSInitCond, LogType::ThreadSync); - cafeExportRegister("coreinit", OSInitCondEx, LogType::ThreadSync); - cafeExportRegister("coreinit", OSSignalCond, LogType::ThreadSync); - cafeExportRegister("coreinit", OSWaitCond, LogType::ThreadSync); + cafeExportRegister("coreinit", OSInitCond, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSInitCondEx, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSSignalCond, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSWaitCond, LogType::CoreinitThreadSync); // OSSemaphore - cafeExportRegister("coreinit", OSInitSemaphore, LogType::ThreadSync); - cafeExportRegister("coreinit", OSInitSemaphoreEx, LogType::ThreadSync); - cafeExportRegister("coreinit", OSWaitSemaphore, LogType::ThreadSync); - cafeExportRegister("coreinit", OSTryWaitSemaphore, LogType::ThreadSync); - cafeExportRegister("coreinit", OSSignalSemaphore, LogType::ThreadSync); - cafeExportRegister("coreinit", OSGetSemaphoreCount, LogType::ThreadSync); + cafeExportRegister("coreinit", OSInitSemaphore, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSInitSemaphoreEx, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSWaitSemaphore, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSTryWaitSemaphore, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSSignalSemaphore, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSGetSemaphoreCount, LogType::CoreinitThreadSync); // OSFastMutex - cafeExportRegister("coreinit", OSFastMutex_Init, LogType::ThreadSync); - cafeExportRegister("coreinit", OSFastMutex_Lock, LogType::ThreadSync); - cafeExportRegister("coreinit", OSFastMutex_TryLock, LogType::ThreadSync); - cafeExportRegister("coreinit", OSFastMutex_Unlock, LogType::ThreadSync); + cafeExportRegister("coreinit", OSFastMutex_Init, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSFastMutex_Lock, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSFastMutex_TryLock, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSFastMutex_Unlock, LogType::CoreinitThreadSync); // OSFastCond - cafeExportRegister("coreinit", OSFastCond_Init, LogType::ThreadSync); - cafeExportRegister("coreinit", OSFastCond_Wait, LogType::ThreadSync); - cafeExportRegister("coreinit", OSFastCond_Signal, LogType::ThreadSync); + cafeExportRegister("coreinit", OSFastCond_Init, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSFastCond_Wait, LogType::CoreinitThreadSync); + cafeExportRegister("coreinit", OSFastCond_Signal, LogType::CoreinitThreadSync); } }; diff --git a/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp b/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp index d7555394..27a858c1 100644 --- a/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp +++ b/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp @@ -216,7 +216,7 @@ void nnNfpExport_SetDeactivateEvent(PPCInterpreter_t* hCPU) ppcDefineParamStructPtr(osEvent, coreinit::OSEvent, 0); ppcDefineParamMPTR(osEventMPTR, 0); - cemuLog_log(LogType::nn_nfp, "SetDeactivateEvent(0x{:08x})", osEventMPTR); + cemuLog_log(LogType::NN_NFP, "SetDeactivateEvent(0x{:08x})", osEventMPTR); coreinit::OSInitEvent(osEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO); @@ -241,7 +241,7 @@ void nnNfpExport_Initialize(PPCInterpreter_t* hCPU) void nnNfpExport_StartDetection(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "StartDetection()"); + cemuLog_log(LogType::NN_NFP, "StartDetection()"); nnNfpLock(); nfp_data.isDetecting = true; nnNfpUnlock(); @@ -250,7 +250,7 @@ void nnNfpExport_StartDetection(PPCInterpreter_t* hCPU) void nnNfpExport_StopDetection(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "StopDetection()"); + cemuLog_log(LogType::NN_NFP, "StopDetection()"); nnNfpLock(); nfp_data.isDetecting = false; nnNfpUnlock(); @@ -274,7 +274,7 @@ static_assert(sizeof(nfpTagInfo_t) == 0x54, "nfpTagInfo_t has invalid size"); void nnNfpExport_GetTagInfo(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "GetTagInfo(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "GetTagInfo(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(tagInfo, nfpTagInfo_t, 0); nnNfpLock(); @@ -306,7 +306,7 @@ typedef struct uint32 NFCGetTagInfo(uint32 index, uint32 timeout, MPTR functionPtr, void* userParam) { - cemuLog_log(LogType::nn_nfp, "NFCGetTagInfo({},{},0x{:08x},0x{:08x})", index, timeout, functionPtr, userParam?memory_getVirtualOffsetFromPointer(userParam):0); + cemuLog_log(LogType::NN_NFP, "NFCGetTagInfo({},{},0x{:08x},0x{:08x})", index, timeout, functionPtr, userParam ? memory_getVirtualOffsetFromPointer(userParam) : 0); cemu_assert(index == 0); @@ -331,7 +331,7 @@ uint32 NFCGetTagInfo(uint32 index, uint32 timeout, MPTR functionPtr, void* userP void nnNfpExport_Mount(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "Mount()"); + cemuLog_log(LogType::NN_NFP, "Mount()"); nnNfpLock(); if (nfp_data.hasActiveAmiibo == false) { @@ -348,14 +348,14 @@ void nnNfpExport_Mount(PPCInterpreter_t* hCPU) void nnNfpExport_Unmount(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "Unmount()"); + cemuLog_log(LogType::NN_NFP, "Unmount()"); nfp_data.hasOpenApplicationArea = false; osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0)); } void nnNfpExport_MountRom(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "MountRom()"); + cemuLog_log(LogType::NN_NFP, "MountRom()"); nnNfpLock(); if (nfp_data.hasActiveAmiibo == false) { @@ -386,7 +386,7 @@ static_assert(sizeof(nfpRomInfo_t) == 0x36, "nfpRomInfo_t has invalid size"); void nnNfpExport_GetNfpRomInfo(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "GetNfpRomInfo(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "GetNfpRomInfo(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(romInfo, nfpRomInfo_t, 0); nnNfpLock(); @@ -438,7 +438,7 @@ static_assert(offsetof(nfpCommonData_t, applicationAreaSize) == 0xE, "nfpCommonD void nnNfpExport_GetNfpCommonInfo(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "GetNfpCommonInfo(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "GetNfpCommonInfo(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(commonInfo, nfpCommonData_t, 0); nnNfpLock(); @@ -492,7 +492,7 @@ typedef struct void nnNfpExport_GetNfpRegisterInfo(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "GetNfpRegisterInfo(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "GetNfpRegisterInfo(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(registerInfo, nfpRegisterInfo_t, 0); if(!registerInfo) @@ -515,7 +515,7 @@ void nnNfpExport_GetNfpRegisterInfo(PPCInterpreter_t* hCPU) void nnNfpExport_InitializeRegisterInfoSet(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "InitializeRegisterInfoSet(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "InitializeRegisterInfoSet(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(registerInfoSet, nfpRegisterInfoSet_t, 0); memset(registerInfoSet, 0, sizeof(nfpRegisterInfoSet_t)); @@ -525,7 +525,7 @@ void nnNfpExport_InitializeRegisterInfoSet(PPCInterpreter_t* hCPU) void nnNfpExport_SetNfpRegisterInfo(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "SetNfpRegisterInfo(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "SetNfpRegisterInfo(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(registerInfoSet, nfpRegisterInfoSet_t, 0); memcpy(nfp_data.amiiboInternal.amiiboSettings.mii, registerInfoSet->ownerMii, sizeof(nfp_data.amiiboInternal.amiiboSettings.mii)); @@ -538,7 +538,7 @@ void nnNfpExport_SetNfpRegisterInfo(PPCInterpreter_t* hCPU) void nnNfpExport_IsExistApplicationArea(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "IsExistApplicationArea()"); + cemuLog_log(LogType::NN_NFP, "IsExistApplicationArea()"); if (!nfp_data.hasActiveAmiibo || !nfp_data.isMounted) { osLib_returnFromFunction(hCPU, 0); @@ -550,7 +550,7 @@ void nnNfpExport_IsExistApplicationArea(PPCInterpreter_t* hCPU) void nnNfpExport_OpenApplicationArea(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "OpenApplicationArea(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "OpenApplicationArea(0x{:08x})", hCPU->gpr[3]); ppcDefineParamU32(appAreaId, 0); // note - this API doesn't fail if the application area has already been opened? @@ -575,7 +575,7 @@ void nnNfpExport_OpenApplicationArea(PPCInterpreter_t* hCPU) void nnNfpExport_ReadApplicationArea(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "ReadApplicationArea(0x{:08x}, 0x{:x})", hCPU->gpr[3], hCPU->gpr[4]); + cemuLog_log(LogType::NN_NFP, "ReadApplicationArea(0x{:08x}, 0x{:x})", hCPU->gpr[3], hCPU->gpr[4]); ppcDefineParamPtr(bufferPtr, uint8*, 0); ppcDefineParamU32(len, 1); @@ -592,7 +592,7 @@ void nnNfpExport_ReadApplicationArea(PPCInterpreter_t* hCPU) void nnNfpExport_WriteApplicationArea(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "WriteApplicationArea(0x{:08x}, 0x{:x}, 0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); + cemuLog_log(LogType::NN_NFP, "WriteApplicationArea(0x{:08x}, 0x{:x}, 0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); ppcDefineParamPtr(bufferPtr, uint8*, 0); ppcDefineParamU32(len, 1); @@ -628,7 +628,7 @@ typedef struct void nnNfpExport_CreateApplicationArea(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "CreateApplicationArea(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "CreateApplicationArea(0x{:08x})", hCPU->gpr[3]); ppcDefineParamPtr(createInfo, NfpCreateInfo_t, 0); if (nfp_data.hasOpenApplicationArea || (nfp_data.amiiboInternal.amiiboSettings.flags&0x20)) @@ -677,7 +677,7 @@ void nnNfpExport_CreateApplicationArea(PPCInterpreter_t* hCPU) void nnNfpExport_DeleteApplicationArea(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "DeleteApplicationArea()"); + cemuLog_log(LogType::NN_NFP, "DeleteApplicationArea()"); if (nfp_data.isReadOnly) { @@ -707,7 +707,7 @@ void nnNfpExport_DeleteApplicationArea(PPCInterpreter_t* hCPU) void nnNfpExport_Flush(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "Flush()"); + cemuLog_log(LogType::NN_NFP, "Flush()"); // write Amiibo data if (nfp_data.isReadOnly) @@ -748,7 +748,7 @@ static_assert(offsetof(AmiiboSettingsArgs_t, commonInfo) == 0x114); void nnNfpExport_GetAmiiboSettingsArgs(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "GetAmiiboSettingsArgs(0x{:08x})", hCPU->gpr[3]); + cemuLog_log(LogType::NN_NFP, "GetAmiiboSettingsArgs(0x{:08x})", hCPU->gpr[3]); ppcDefineParamStructPtr(settingsArg, AmiiboSettingsArgs_t, 0); memset(settingsArg, 0, sizeof(AmiiboSettingsArgs_t)); @@ -917,7 +917,7 @@ void nnNfp_update() void nnNfpExport_GetNfpState(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::nn_nfp, "GetNfpState()"); + cemuLog_log(LogType::NN_NFP, "GetNfpState()"); // workaround for Mario Party 10 eating CPU cycles in an infinite loop (maybe due to incorrect NFP detection handling?) uint64 titleId = CafeSystem::GetForegroundTitleId(); diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp index 25245b5c..99c113c4 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp @@ -123,20 +123,20 @@ namespace nn loadOliveUploadFavoriteTypes(); loadOlivePostAndTopicTypes(); - cafeExportRegisterFunc(GetErrorCode, "nn_olv", "GetErrorCode__Q2_2nn3olvFRCQ2_2nn6Result", LogType::None); + cafeExportRegisterFunc(GetErrorCode, "nn_olv", "GetErrorCode__Q2_2nn3olvFRCQ2_2nn6Result", LogType::NN_OLV); osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken); - cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadPostDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv28UploadPostDataByPostAppParam", LogType::Force); - cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadCommentDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv31UploadCommentDataByPostAppParam", LogType::Force); - cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadDirectMessageDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv37UploadDirectMessageDataByPostAppParam", LogType::Force); + cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadPostDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv28UploadPostDataByPostAppParam", LogType::NN_OLV); + cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadCommentDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv31UploadCommentDataByPostAppParam", LogType::NN_OLV); + cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadDirectMessageDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv37UploadDirectMessageDataByPostAppParam", LogType::NN_OLV); - cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultByPostApp__Q2_2nn3olvFv", LogType::Force); - cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultWithUploadedPostDataByPostApp__Q2_2nn3olvFPQ3_2nn3olv16UploadedPostData", LogType::Force); - cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultWithUploadedDirectMessageDataByPostApp__Q2_2nn3olvFPQ3_2nn3olv25UploadedDirectMessageData", LogType::Force); - cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultWithUploadedCommentDataByPostApp__Q2_2nn3olvFPQ3_2nn3olv19UploadedCommentData", LogType::Force); + cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultByPostApp__Q2_2nn3olvFv", LogType::NN_OLV); + cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultWithUploadedPostDataByPostApp__Q2_2nn3olvFPQ3_2nn3olv16UploadedPostData", LogType::NN_OLV); + cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultWithUploadedDirectMessageDataByPostApp__Q2_2nn3olvFPQ3_2nn3olv25UploadedDirectMessageData", LogType::NN_OLV); + cafeExportRegisterFunc(StubPostAppResult, "nn_olv", "GetResultWithUploadedCommentDataByPostApp__Q2_2nn3olvFPQ3_2nn3olv19UploadedCommentData", LogType::NN_OLV); - cafeExportRegisterFunc(UploadedPostData_GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv16UploadedPostDataCFv", LogType::Force); + cafeExportRegisterFunc(UploadedPostData_GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv16UploadedPostDataCFv", LogType::NN_OLV); } void unload() // not called yet diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h index 3f5df35c..794195a1 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h @@ -501,30 +501,30 @@ namespace nn static void loadOliveDownloadCommunityTypes() { - cafeExportRegisterFunc(DownloadedCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv23DownloadedCommunityDataFv", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv23DownloadedCommunityDataCFUi", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv23DownloadedCommunityDataCFPcUi", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv23DownloadedCommunityDataCFPwUi", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv23DownloadedCommunityDataCFPwUi", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv23DownloadedCommunityDataCFPUcPUiUi", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv23DownloadedCommunityDataCFPUcPUiUi", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerMiiData, "nn_olv", "GetOwnerMiiData__Q3_2nn3olv23DownloadedCommunityDataCFP12FFLStoreData", LogType::None); - cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerMiiNickname, "nn_olv", "GetOwnerMiiNickname__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None); + cafeExportRegisterFunc(DownloadedCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv23DownloadedCommunityDataFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv23DownloadedCommunityDataCFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv23DownloadedCommunityDataCFPcUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv23DownloadedCommunityDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv23DownloadedCommunityDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv23DownloadedCommunityDataCFPUcPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv23DownloadedCommunityDataCFPUcPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerMiiData, "nn_olv", "GetOwnerMiiData__Q3_2nn3olv23DownloadedCommunityDataCFP12FFLStoreData", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerMiiNickname, "nn_olv", "GetOwnerMiiNickname__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::NN_OLV); - cafeExportRegisterFunc(DownloadCommunityDataListParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv30DownloadCommunityDataListParamFv", LogType::None); - cafeExportRegisterFunc(DownloadCommunityDataListParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::None); - cafeExportRegisterFunc(DownloadCommunityDataListParam::__SetCommunityDataMaxNum, "nn_olv", "SetCommunityDataMaxNum__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::None); - cafeExportRegisterFunc(DownloadCommunityDataListParam::__GetRawDataUrl, "nn_olv", "GetRawDataUrl__Q3_2nn3olv30DownloadCommunityDataListParamCFPcUi", LogType::None); + cafeExportRegisterFunc(DownloadCommunityDataListParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv30DownloadCommunityDataListParamFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadCommunityDataListParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadCommunityDataListParam::__SetCommunityDataMaxNum, "nn_olv", "SetCommunityDataMaxNum__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadCommunityDataListParam::__GetRawDataUrl, "nn_olv", "GetRawDataUrl__Q3_2nn3olv30DownloadCommunityDataListParamCFPcUi", LogType::NN_OLV); cafeExportRegisterFunc((sint32 (*)(DownloadCommunityDataListParam*, uint32))DownloadCommunityDataListParam::__SetCommunityId, - "nn_olv", "SetCommunityId__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::None); + "nn_olv", "SetCommunityId__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::NN_OLV); cafeExportRegisterFunc((sint32(*)(DownloadCommunityDataListParam*, uint32, uint8))DownloadCommunityDataListParam::__SetCommunityId, - "nn_olv", "SetCommunityId__Q3_2nn3olv30DownloadCommunityDataListParamFUiUc", LogType::None); + "nn_olv", "SetCommunityId__Q3_2nn3olv30DownloadCommunityDataListParamFUiUc", LogType::NN_OLV); - cafeExportRegisterFunc(DownloadCommunityDataList, "nn_olv", "DownloadCommunityDataList__Q2_2nn3olvFPQ3_2nn3olv23DownloadedCommunityDataPUiUiPCQ3_2nn3olv30DownloadCommunityDataListParam", LogType::None); + cafeExportRegisterFunc(DownloadCommunityDataList, "nn_olv", "DownloadCommunityDataList__Q2_2nn3olvFPQ3_2nn3olv23DownloadedCommunityDataPUiUiPCQ3_2nn3olv30DownloadCommunityDataListParam", LogType::NN_OLV); } } } \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h index 603b167c..51dce8fe 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h @@ -113,16 +113,16 @@ namespace nn static void loadOliveInitializeTypes() { - cafeExportRegisterFunc(Initialize, "nn_olv", "Initialize__Q2_2nn3olvFPCQ3_2nn3olv15InitializeParam", LogType::None); - cafeExportRegisterFunc(IsInitialized, "nn_olv", "IsInitialized__Q2_2nn3olvFv", LogType::None); - cafeExportRegisterFunc(Report::GetReportTypes, "nn_olv", "GetReportTypes__Q3_2nn3olv6ReportFv", LogType::None); - cafeExportRegisterFunc(Report::SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv6ReportFUi", LogType::None); + cafeExportRegisterFunc(Initialize, "nn_olv", "Initialize__Q2_2nn3olvFPCQ3_2nn3olv15InitializeParam", LogType::NN_OLV); + cafeExportRegisterFunc(IsInitialized, "nn_olv", "IsInitialized__Q2_2nn3olvFv", LogType::NN_OLV); + cafeExportRegisterFunc(Report::GetReportTypes, "nn_olv", "GetReportTypes__Q3_2nn3olv6ReportFv", LogType::NN_OLV); + cafeExportRegisterFunc(Report::SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv6ReportFUi", LogType::NN_OLV); - cafeExportRegisterFunc(InitializeParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv15InitializeParamFv", LogType::None); - cafeExportRegisterFunc(InitializeParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv15InitializeParamFUi", LogType::None); - cafeExportRegisterFunc(InitializeParam::__SetWork, "nn_olv", "SetWork__Q3_2nn3olv15InitializeParamFPUcUi", LogType::None); - cafeExportRegisterFunc(InitializeParam::__SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv15InitializeParamFUi", LogType::None); - cafeExportRegisterFunc(InitializeParam::__SetSysArgs, "nn_olv", "SetSysArgs__Q3_2nn3olv15InitializeParamFPCvUi", LogType::None); + cafeExportRegisterFunc(InitializeParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv15InitializeParamFv", LogType::NN_OLV); + cafeExportRegisterFunc(InitializeParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv15InitializeParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(InitializeParam::__SetWork, "nn_olv", "SetWork__Q3_2nn3olv15InitializeParamFPUcUi", LogType::NN_OLV); + cafeExportRegisterFunc(InitializeParam::__SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv15InitializeParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(InitializeParam::__SetSysArgs, "nn_olv", "SetSysArgs__Q3_2nn3olv15InitializeParamFPCvUi", LogType::NN_OLV); } } } \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp index 722e5584..5257530d 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp @@ -391,71 +391,71 @@ namespace nn void loadOlivePostAndTopicTypes() { - cafeExportRegisterFunc(GetSystemTopicDataListFromRawData, "nn_olv", "GetSystemTopicDataListFromRawData__Q3_2nn3olv6hiddenFPQ4_2nn3olv6hidden29DownloadedSystemTopicDataListPQ4_2nn3olv6hidden24DownloadedSystemPostDataPUiUiPCUcT4", LogType::None); + cafeExportRegisterFunc(GetSystemTopicDataListFromRawData, "nn_olv", "GetSystemTopicDataListFromRawData__Q3_2nn3olv6hiddenFPQ4_2nn3olv6hidden29DownloadedSystemTopicDataListPQ4_2nn3olv6hidden24DownloadedSystemPostDataPUiUiPCUcT4", LogType::NN_OLV); // DownloadedDataBase getters - cafeExportRegisterFunc(DownloadedDataBase::TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetUserPid, "nn_olv", "GetUserPid__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetPostDate, "nn_olv", "GetPostDate__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetFeeling, "nn_olv", "GetFeeling__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetRegionId, "nn_olv", "GetRegionId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetPlatformId, "nn_olv", "GetPlatformId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetLanguageId, "nn_olv", "GetLanguageId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetCountryId, "nn_olv", "GetCountryId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetExternalUrl, "nn_olv", "GetExternalUrl__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetMiiData1, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFP12FFLStoreData", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetMiiNickname, "nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetBodyText, "nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetBodyMemo, "nn_olv", "GetBodyMemo__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetTopicTag, "nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetMiiData2, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::DownloadExternalImageData, "nn_olv", "DownloadExternalImageData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi", LogType::None); - cafeExportRegisterFunc(DownloadedDataBase::GetExternalImageDataSize, "nn_olv", "GetExternalImageDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None); + cafeExportRegisterFunc(DownloadedDataBase::TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetUserPid, "nn_olv", "GetUserPid__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetPostDate, "nn_olv", "GetPostDate__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetFeeling, "nn_olv", "GetFeeling__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetRegionId, "nn_olv", "GetRegionId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetPlatformId, "nn_olv", "GetPlatformId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetLanguageId, "nn_olv", "GetLanguageId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetCountryId, "nn_olv", "GetCountryId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetExternalUrl, "nn_olv", "GetExternalUrl__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetMiiData1, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFP12FFLStoreData", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetMiiNickname, "nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetBodyText, "nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetBodyMemo, "nn_olv", "GetBodyMemo__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetTopicTag, "nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetMiiData2, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::DownloadExternalImageData, "nn_olv", "DownloadExternalImageData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedDataBase::GetExternalImageDataSize, "nn_olv", "GetExternalImageDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::NN_OLV); // DownloadedPostData getters - cafeExportRegisterFunc(DownloadedPostData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None); - cafeExportRegisterFunc(DownloadedPostData::GetEmpathyCount, "nn_olv", "GetEmpathyCount__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None); - cafeExportRegisterFunc(DownloadedPostData::GetCommentCount, "nn_olv", "GetCommentCount__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None); - cafeExportRegisterFunc(DownloadedPostData::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None); + cafeExportRegisterFunc(DownloadedPostData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedPostData::GetEmpathyCount, "nn_olv", "GetEmpathyCount__Q3_2nn3olv18DownloadedPostDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedPostData::GetCommentCount, "nn_olv", "GetCommentCount__Q3_2nn3olv18DownloadedPostDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadedPostData::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::NN_OLV); // DownloadedSystemPostData getters - cafeExportRegisterFunc(hidden::DownloadedSystemPostData::GetTitleId, "nn_olv", "GetTitleId__Q4_2nn3olv6hidden24DownloadedSystemPostDataCFv", LogType::None); + cafeExportRegisterFunc(hidden::DownloadedSystemPostData::GetTitleId, "nn_olv", "GetTitleId__Q4_2nn3olv6hidden24DownloadedSystemPostDataCFv", LogType::NN_OLV); // DownloadedTopicData getters - cafeExportRegisterFunc(DownloadedTopicData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv19DownloadedTopicDataCFv", LogType::None); + cafeExportRegisterFunc(DownloadedTopicData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv19DownloadedTopicDataCFv", LogType::NN_OLV); // DownloadedSystemTopicData getters - cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::TestFlags, "nn_olv", "TestFlags__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFUi", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleId, "nn_olv", "GetTitleId__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleIdNum, "nn_olv", "GetTitleIdNum__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleText, "nn_olv", "GetTitleText__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPwUi", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleIconData, "nn_olv", "GetTitleIconData__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPUcPUiUi", LogType::None); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::TestFlags, "nn_olv", "TestFlags__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFUi", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleId, "nn_olv", "GetTitleId__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleIdNum, "nn_olv", "GetTitleIdNum__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleText, "nn_olv", "GetTitleText__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleIconData, "nn_olv", "GetTitleIconData__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPUcPUiUi", LogType::NN_OLV); // DownloadedSystemTopicDataList getters - cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicDataNum, "nn_olv", "GetDownloadedSystemTopicDataNum__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFv", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostDataNum, "nn_olv", "GetDownloadedSystemPostDataNum__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicData, "nn_olv", "GetDownloadedSystemTopicData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::None); - cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostData, "nn_olv", "GetDownloadedSystemPostData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFiT1", LogType::None); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicDataNum, "nn_olv", "GetDownloadedSystemTopicDataNum__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFv", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostDataNum, "nn_olv", "GetDownloadedSystemPostDataNum__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicData, "nn_olv", "GetDownloadedSystemTopicData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::NN_OLV); + cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostData, "nn_olv", "GetDownloadedSystemPostData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFiT1", LogType::NN_OLV); // DownloadPostDataListParam constructor and getters - cafeExportRegisterFunc(DownloadPostDataListParam::Construct, "nn_olv", "__ct__Q3_2nn3olv25DownloadPostDataListParamFv", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetLanguageId, "nn_olv", "SetLanguageId__Q3_2nn3olv25DownloadPostDataListParamFUc", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchKey, "nn_olv", "SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCwUc", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchKeySingle, "nn_olv", "SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCw", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchPid, "nn_olv", "SetSearchPid__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetPostId, "nn_olv", "SetPostId__Q3_2nn3olv25DownloadPostDataListParamFPCcUi", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetPostDate, "nn_olv", "SetPostDate__Q3_2nn3olv25DownloadPostDataListParamFL", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetPostDataMaxNum, "nn_olv", "SetPostDataMaxNum__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); - cafeExportRegisterFunc(DownloadPostDataListParam::SetBodyTextMaxLength, "nn_olv", "SetBodyTextMaxLength__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::Construct, "nn_olv", "__ct__Q3_2nn3olv25DownloadPostDataListParamFv", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetLanguageId, "nn_olv", "SetLanguageId__Q3_2nn3olv25DownloadPostDataListParamFUc", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchKey, "nn_olv", "SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCwUc", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchKeySingle, "nn_olv", "SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCw", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetSearchPid, "nn_olv", "SetSearchPid__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetPostId, "nn_olv", "SetPostId__Q3_2nn3olv25DownloadPostDataListParamFPCcUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetPostDate, "nn_olv", "SetPostDate__Q3_2nn3olv25DownloadPostDataListParamFL", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetPostDataMaxNum, "nn_olv", "SetPostDataMaxNum__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataListParam::SetBodyTextMaxLength, "nn_olv", "SetBodyTextMaxLength__Q3_2nn3olv25DownloadPostDataListParamFUi", LogType::NN_OLV); // URL and downloading functions - cafeExportRegisterFunc(DownloadPostDataListParam::GetRawDataUrl, "nn_olv", "GetRawDataUrl__Q3_2nn3olv25DownloadPostDataListParamCFPcUi", LogType::None); - cafeExportRegisterFunc(DownloadPostDataList, "nn_olv", "DownloadPostDataList__Q2_2nn3olvFPQ3_2nn3olv19DownloadedTopicDataPQ3_2nn3olv18DownloadedPostDataPUiUiPCQ3_2nn3olv25DownloadPostDataListParam", LogType::None); + cafeExportRegisterFunc(DownloadPostDataListParam::GetRawDataUrl, "nn_olv", "GetRawDataUrl__Q3_2nn3olv25DownloadPostDataListParamCFPcUi", LogType::NN_OLV); + cafeExportRegisterFunc(DownloadPostDataList, "nn_olv", "DownloadPostDataList__Q2_2nn3olvFPQ3_2nn3olv19DownloadedTopicDataPQ3_2nn3olv18DownloadedPostDataPUiUiPCQ3_2nn3olv25DownloadPostDataListParam", LogType::NN_OLV); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h index 4944e314..16ebd29a 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h @@ -402,30 +402,30 @@ namespace nn static void loadOliveUploadCommunityTypes() { - cafeExportRegisterFunc(UploadedCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv21UploadedCommunityDataFv", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv21UploadedCommunityDataCFUi", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv21UploadedCommunityDataCFPcUi", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv21UploadedCommunityDataCFPwUi", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv21UploadedCommunityDataCFPwUi", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv21UploadedCommunityDataCFPUcPUiUi", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(UploadedCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv21UploadedCommunityDataCFPUcPUiUi", LogType::None); + cafeExportRegisterFunc(UploadedCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv21UploadedCommunityDataFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv21UploadedCommunityDataCFUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv21UploadedCommunityDataCFPcUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv21UploadedCommunityDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv21UploadedCommunityDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv21UploadedCommunityDataCFPUcPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv21UploadedCommunityDataCFPUcPUiUi", LogType::NN_OLV); - cafeExportRegisterFunc(UploadCommunityDataParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv24UploadCommunityDataParamFv", LogType::None); - cafeExportRegisterFunc(UploadCommunityDataParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv24UploadCommunityDataParamFUi", LogType::None); - cafeExportRegisterFunc(UploadCommunityDataParam::__SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv24UploadCommunityDataParamFUi", LogType::None); - cafeExportRegisterFunc(UploadCommunityDataParam::__SetAppData, "nn_olv", "SetAppData__Q3_2nn3olv24UploadCommunityDataParamFPCUcUi", LogType::None); - cafeExportRegisterFunc(UploadCommunityDataParam::__SetTitleText, "nn_olv", "SetTitleText__Q3_2nn3olv24UploadCommunityDataParamFPCw", LogType::None); - cafeExportRegisterFunc(UploadCommunityDataParam::__SetDescriptionText, "nn_olv", "SetDescriptionText__Q3_2nn3olv24UploadCommunityDataParamFPCw", LogType::None); - cafeExportRegisterFunc(UploadCommunityDataParam::__SetIconData, "nn_olv", "SetIconData__Q3_2nn3olv24UploadCommunityDataParamFPCUcUi", LogType::None); + cafeExportRegisterFunc(UploadCommunityDataParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv24UploadCommunityDataParamFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadCommunityDataParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv24UploadCommunityDataParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadCommunityDataParam::__SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv24UploadCommunityDataParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadCommunityDataParam::__SetAppData, "nn_olv", "SetAppData__Q3_2nn3olv24UploadCommunityDataParamFPCUcUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadCommunityDataParam::__SetTitleText, "nn_olv", "SetTitleText__Q3_2nn3olv24UploadCommunityDataParamFPCw", LogType::NN_OLV); + cafeExportRegisterFunc(UploadCommunityDataParam::__SetDescriptionText, "nn_olv", "SetDescriptionText__Q3_2nn3olv24UploadCommunityDataParamFPCw", LogType::NN_OLV); + cafeExportRegisterFunc(UploadCommunityDataParam::__SetIconData, "nn_olv", "SetIconData__Q3_2nn3olv24UploadCommunityDataParamFPCUcUi", LogType::NN_OLV); cafeExportRegisterFunc((sint32(*)(UploadCommunityDataParam const*))UploadCommunityData, - "nn_olv", "UploadCommunityData__Q2_2nn3olvFPCQ3_2nn3olv24UploadCommunityDataParam", LogType::None); + "nn_olv", "UploadCommunityData__Q2_2nn3olvFPCQ3_2nn3olv24UploadCommunityDataParam", LogType::NN_OLV); cafeExportRegisterFunc((sint32(*)(UploadedCommunityData *, UploadCommunityDataParam const*))UploadCommunityData, - "nn_olv", "UploadCommunityData__Q2_2nn3olvFPQ3_2nn3olv21UploadedCommunityDataPCQ3_2nn3olv24UploadCommunityDataParam", LogType::None); + "nn_olv", "UploadCommunityData__Q2_2nn3olvFPQ3_2nn3olv21UploadedCommunityDataPCQ3_2nn3olv24UploadCommunityDataParam", LogType::NN_OLV); } } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h index 05ef1dd4..dfa43ec3 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h @@ -315,27 +315,27 @@ namespace nn static void loadOliveUploadFavoriteTypes() { - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv31UploadedFavoriteToCommunityDataFv", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFUi", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPcUi", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPwUi", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPwUi", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPUcPUiUi", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::None); - cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPUcPUiUi", LogType::None); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv31UploadedFavoriteToCommunityDataFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPcUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPwUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPUcPUiUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPUcPUiUi", LogType::NN_OLV); - cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFv", LogType::None); - cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFUi", LogType::None); - cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetCommunityCode, "nn_olv", "SetCommunityCode__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFPCc", LogType::None); - cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFUi", LogType::None); + cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFv", LogType::NN_OLV); + cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFUi", LogType::NN_OLV); + cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetCommunityCode, "nn_olv", "SetCommunityCode__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFPCc", LogType::NN_OLV); + cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFUi", LogType::NN_OLV); cafeExportRegisterFunc((sint32(*)(const UploadFavoriteToCommunityDataParam*))UploadFavoriteToCommunityData, - "nn_olv", "UploadFavoriteToCommunityData__Q2_2nn3olvFPCQ3_2nn3olv34UploadFavoriteToCommunityDataParam", LogType::None); + "nn_olv", "UploadFavoriteToCommunityData__Q2_2nn3olvFPCQ3_2nn3olv34UploadFavoriteToCommunityDataParam", LogType::NN_OLV); cafeExportRegisterFunc((sint32(*)(UploadedFavoriteToCommunityData*, const UploadFavoriteToCommunityDataParam*))UploadFavoriteToCommunityData, - "nn_olv", "UploadFavoriteToCommunityData__Q2_2nn3olvFPQ3_2nn3olv31UploadedFavoriteToCommunityDataPCQ3_2nn3olv34UploadFavoriteToCommunityDataParam", LogType::None); + "nn_olv", "UploadFavoriteToCommunityData__Q2_2nn3olvFPQ3_2nn3olv31UploadedFavoriteToCommunityDataPCQ3_2nn3olv34UploadFavoriteToCommunityDataParam", LogType::NN_OLV); } } diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index ed8441a7..ac228530 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -1,8 +1,8 @@ #include "CemuLogging.h" -#include "config/CemuConfig.h" #include "gui/LoggingWindow.h" -#include "config/ActiveSettings.h" #include "util/helpers/helpers.h" +#include "config/CemuConfig.h" +#include "config/ActiveSettings.h" #include #include @@ -10,6 +10,8 @@ #include +uint64 s_loggingFlagMask = cemuLog_getFlag(LogType::Force); + struct _LogContext { std::condition_variable_any log_condition; @@ -33,48 +35,32 @@ struct _LogContext const std::map g_logging_window_mapping { - {LogType::UnsupportedAPI, "Unsupported API calls"}, - {LogType::CoreinitLogging, "Coreinit Logging"}, - {LogType::CoreinitFile, "Coreinit File-Access"}, - {LogType::ThreadSync, "Coreinit Thread-Synchronization"}, - {LogType::CoreinitMem, "Coreinit Memory"}, - {LogType::CoreinitMP, "Coreinit MP"}, - {LogType::CoreinitThread, "Coreinit Thread"}, - {LogType::nn_nfp, "nn::nfp"}, - {LogType::GX2, "GX2"}, - {LogType::SoundAPI, "Audio"}, - {LogType::InputAPI, "Input"}, - {LogType::Socket, "Socket"}, - {LogType::Save, "Save"}, - {LogType::H264, "H264"}, - {LogType::Patches, "Graphic pack patches"}, - {LogType::TextureCache, "Texture cache"}, - {LogType::TextureReadback, "Texture readback"}, - {LogType::OpenGLLogging, "OpenGL debug output"}, - {LogType::VulkanValidation, "Vulkan validation layer"}, + {LogType::UnsupportedAPI, "Unsupported API calls"}, + {LogType::CoreinitLogging, "Coreinit Logging"}, + {LogType::CoreinitFile, "Coreinit File-Access"}, + {LogType::CoreinitThreadSync, "Coreinit Thread-Synchronization"}, + {LogType::CoreinitMem, "Coreinit Memory"}, + {LogType::CoreinitMP, "Coreinit MP"}, + {LogType::CoreinitThread, "Coreinit Thread"}, + {LogType::NN_NFP, "nn::nfp"}, + {LogType::GX2, "GX2"}, + {LogType::SoundAPI, "Audio"}, + {LogType::InputAPI, "Input"}, + {LogType::Socket, "Socket"}, + {LogType::Save, "Save"}, + {LogType::H264, "H264"}, + {LogType::Patches, "Graphic pack patches"}, + {LogType::TextureCache, "Texture cache"}, + {LogType::TextureReadback, "Texture readback"}, + {LogType::OpenGLLogging, "OpenGL debug output"}, + {LogType::VulkanValidation, "Vulkan validation layer"}, }; -uint64 cemuLog_getFlag(LogType type) -{ - return type <= LogType::Force ? 0 : (1ULL << ((uint64)type - 1)); -} - bool cemuLog_advancedPPCLoggingEnabled() { return GetConfig().advanced_ppc_logging; } -bool cemuLog_isLoggingEnabled(LogType type) -{ - if (type == LogType::Placeholder) - return false; - - if (type == LogType::None) - return false; - - return (type == LogType::Force) || ((GetConfig().log_flag.GetValue() & cemuLog_getFlag(type)) != 0); -} - void cemuLog_thread() { SetThreadName("cemuLog_thread"); @@ -198,12 +184,7 @@ std::unique_lock cemuLog_acquire() return std::unique_lock(LogContext.log_mutex); } -void cemuLog_setFlag(LogType loggingType, bool isEnable) +void cemuLog_setActiveLoggingFlags(uint64 flagMask) { - if (isEnable) - GetConfig().log_flag = GetConfig().log_flag.GetValue() | cemuLog_getFlag(loggingType); - else - GetConfig().log_flag = GetConfig().log_flag.GetValue() & ~cemuLog_getFlag(loggingType); - - g_config.Save(); -} \ No newline at end of file + s_loggingFlagMask = flagMask | cemuLog_getFlag(LogType::Force); +} diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index d55256c9..e6599c5a 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -1,43 +1,44 @@ #pragma once +extern uint64 s_loggingFlagMask; + enum class LogType : sint32 { - // note: IDs must not exceed 63 - Placeholder = -2, - None = -1, - Force = 0, // this logging type is always on - CoreinitFile = 1, - GX2 = 2, - UnsupportedAPI = 3, - ThreadSync = 4, - SoundAPI = 5, // any audio related API - InputAPI = 6, // any input related API - Socket = 7, - Save = 8, - CoreinitMem = 9, // coreinit memory functions - H264 = 10, - OpenGLLogging = 11, // OpenGL debug logging - TextureCache = 12, // texture cache warnings and info - VulkanValidation = 13, // Vulkan validation layer - nn_nfp = 14, // nn_nfp (Amiibo) API - Patches = 15, - CoreinitMP = 16, - CoreinitThread = 17, - CoreinitLogging = 18, // OSReport, OSConsoleWrite etc. - CoreinitMemoryMapping = 19, // OSGetAvailPhysAddrRange, OSAllocVirtAddr, OSMapMemory etc. - CoreinitAlarm = 23, + // note: IDs must be in range 1-64 + Force = 63, // always enabled + Placeholder = 62, // always disabled + APIErrors = Force, // alias for Force. Logs bad parameters or other API errors in OS libs - PPC_IPC = 20, - NN_AOC = 21, - NN_PDM = 22, - - TextureReadback = 30, + CoreinitFile = 0, + GX2 = 1, + UnsupportedAPI = 2, + SoundAPI = 4, // any audio related API + InputAPI = 5, // any input related API + Socket = 6, + Save = 7, + H264 = 9, + OpenGLLogging = 10, // OpenGL debug logging + TextureCache = 11, // texture cache warnings and info + VulkanValidation = 12, // Vulkan validation layer + Patches = 14, + CoreinitMem = 8, // coreinit memory functions + CoreinitMP = 15, + CoreinitThread = 16, + CoreinitLogging = 17, // OSReport, OSConsoleWrite etc. + CoreinitMemoryMapping = 18, // OSGetAvailPhysAddrRange, OSAllocVirtAddr, OSMapMemory etc. + CoreinitAlarm = 22, + CoreinitThreadSync = 3, - ProcUi = 40, + PPC_IPC = 19, + NN_AOC = 20, + NN_PDM = 21, + NN_OLV = 23, + NN_NFP = 13, - APIErrors = 0, // alias for Force. Logs bad parameters or other API errors in OS libs + TextureReadback = 29, + + ProcUi = 39, - }; template <> @@ -53,7 +54,17 @@ struct fmt::formatter : formatter { void cemuLog_writeLineToLog(std::string_view text, bool date = true, bool new_line = true); inline void cemuLog_writePlainToLog(std::string_view text) { cemuLog_writeLineToLog(text, false, false); } -bool cemuLog_isLoggingEnabled(LogType type); +void cemuLog_setActiveLoggingFlags(uint64 flagMask); + +inline uint64 cemuLog_getFlag(LogType type) +{ + return 1ULL << (uint64)type; +} + +inline bool cemuLog_isLoggingEnabled(LogType type) +{ + return (s_loggingFlagMask & cemuLog_getFlag(type)) != 0; +} bool cemuLog_log(LogType type, std::string_view text); bool cemuLog_log(LogType type, std::u8string_view text); @@ -97,6 +108,8 @@ bool cemuLog_log(LogType type, std::basic_string formatStr, TArgs&&... args) template bool cemuLog_log(LogType type, const T* format, TArgs&&... args) { + if (!cemuLog_isLoggingEnabled(type)) + return false; auto format_str = std::basic_string(format); return cemuLog_log(type, format_str, std::forward(args)...); } @@ -116,7 +129,6 @@ bool cemuLog_logDebug(LogType type, TFmt format, TArgs&&... args) bool cemuLog_advancedPPCLoggingEnabled(); uint64 cemuLog_getFlag(LogType type); -void cemuLog_setFlag(LogType type, bool enabled); fs::path cemuLog_GetLogFilePath(); void cemuLog_createLogFile(bool triggeredByCrash); diff --git a/src/config/ActiveSettings.h b/src/config/ActiveSettings.h index 1b66b525..54052741 100644 --- a/src/config/ActiveSettings.h +++ b/src/config/ActiveSettings.h @@ -69,12 +69,12 @@ private: inline static fs::path s_executable_filename; // cemu.exe inline static fs::path s_mlc_path; -public: +public: // general [[nodiscard]] static bool LoadSharedLibrariesEnabled(); [[nodiscard]] static bool DisplayDRCEnabled(); [[nodiscard]] static bool FullscreenEnabled(); - + // cpu [[nodiscard]] static CPUMode GetCPUMode(); [[nodiscard]] static uint8 GetTimerShiftFactor(); diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 833ef1c2..220a2295 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -43,6 +43,7 @@ void CemuConfig::Load(XMLConfigParser& parser) // general settings log_flag = parser.get("logflag", log_flag.GetInitValue()); + cemuLog_setActiveLoggingFlags(GetConfig().log_flag.GetValue()); advanced_ppc_logging = parser.get("advanced_ppc_logging", advanced_ppc_logging.GetInitValue()); const char* mlc = parser.get("mlc_path", ""); @@ -53,7 +54,7 @@ void CemuConfig::Load(XMLConfigParser& parser) language = parser.get("language", wxLANGUAGE_DEFAULT); use_discord_presence = parser.get("use_discord_presence", true); fullscreen_menubar = parser.get("fullscreen_menubar", false); - feral_gamemode = parser.get("feral_gamemode", false); + feral_gamemode = parser.get("feral_gamemode", false); check_update = parser.get("check_update", check_update); save_screenshot = parser.get("save_screenshot", save_screenshot); did_show_vulkan_warning = parser.get("vk_warning", did_show_vulkan_warning); @@ -62,9 +63,6 @@ void CemuConfig::Load(XMLConfigParser& parser) fullscreen = parser.get("fullscreen", fullscreen); proxy_server = parser.get("proxy_server", ""); disable_screensaver = parser.get("disable_screensaver", disable_screensaver); - - // cpu_mode = parser.get("cpu_mode", cpu_mode.GetInitValue()); - //console_region = parser.get("console_region", console_region.GetInitValue()); console_language = parser.get("console_language", console_language.GetInitValue()); window_position.x = parser.get("window_position").get("x", -1); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index 19d9ca0e..ea6c3f2c 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -353,7 +353,6 @@ struct CemuConfig }; CemuConfig(const CemuConfig&) = delete; - // // sets mlc path, updates permanent config value, saves config void SetMLCPath(fs::path path, bool save = true); @@ -365,10 +364,10 @@ struct CemuConfig ConfigValue language{ wxLANGUAGE_DEFAULT }; ConfigValue use_discord_presence{ true }; - ConfigValue mlc_path {}; + ConfigValue mlc_path{}; ConfigValue fullscreen_menubar{ false }; ConfigValue fullscreen{ false }; - ConfigValue feral_gamemode{false}; + ConfigValue feral_gamemode{false}; ConfigValue proxy_server{}; // temporary workaround because feature crashes on macOS diff --git a/src/config/ConfigValue.h b/src/config/ConfigValue.h index 11fc1e48..358af67a 100644 --- a/src/config/ConfigValue.h +++ b/src/config/ConfigValue.h @@ -39,7 +39,7 @@ public: return *this; } - [[nodiscard]] TType GetValue() const { return m_value.load(); } + [[nodiscard]] inline TType GetValue() const { return m_value.load(); } void SetValue(const TType& v) { m_value = v; } [[nodiscard]] const TType& GetInitValue() const { return m_init_value; } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index bba64a24..d0ec6e9f 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1103,7 +1103,14 @@ void MainWindow::OnDebugLoggingToggleFlagGeneric(wxCommandEvent& event) sint32 id = event.GetId(); if (id >= loggingIdBase && id < (MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 64)) { - cemuLog_setFlag(static_cast(id - loggingIdBase), event.IsChecked()); + bool isEnable = event.IsChecked(); + LogType loggingType = static_cast(id - loggingIdBase); + if (isEnable) + GetConfig().log_flag = GetConfig().log_flag.GetValue() | cemuLog_getFlag(loggingType); + else + GetConfig().log_flag = GetConfig().log_flag.GetValue() & ~cemuLog_getFlag(loggingType); + cemuLog_setActiveLoggingFlags(GetConfig().log_flag.GetValue()); + g_config.Save(); } } @@ -2190,11 +2197,11 @@ void MainWindow::RecreateMenu() debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::UnsupportedAPI), _("&Unsupported API calls"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::UnsupportedAPI)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitLogging), _("&Coreinit Logging (OSReport/OSConsole)"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitLogging)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitFile), _("&Coreinit File-Access API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitFile)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::ThreadSync), _("&Coreinit Thread-Synchronization API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::ThreadSync)); + debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThreadSync), _("&Coreinit Thread-Synchronization API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThreadSync)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitMem), _("&Coreinit Memory API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitMem)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitMP), _("&Coreinit MP API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitMP)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThread), _("&Coreinit Thread API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThread)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::nn_nfp), _("&NN NFP"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::nn_nfp)); + debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_NFP), _("&NN NFP"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::NN_NFP)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::GX2), _("&GX2 API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::GX2)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SoundAPI), _("&Audio API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::SoundAPI)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::InputAPI), _("&Input API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::InputAPI)); From 92ab87b0492713a2d4c9b980209301e7312c626b Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:20:37 +0200 Subject: [PATCH 028/101] Latte: Fix shader compilation error when subroutines are used Fixes character colors in Tekken Tag Tournament 2 --- .../Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp | 4 ++-- src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index 5ce0b76f..486b7bf5 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -4037,8 +4037,8 @@ void LatteDecompiler_emitGLSLShader(LatteDecompilerShaderContext* shaderContext, for (auto& subroutineInfo : shaderContext->list_subroutines) { sint32 subroutineMaxStackDepth = 0; - src->addFmt("bool activeMaskStackSub%04x[{}];" _CRLF, subroutineInfo.cfAddr, subroutineMaxStackDepth + 1); - src->addFmt("bool activeMaskStackCSub%04x[{}];" _CRLF, subroutineInfo.cfAddr, subroutineMaxStackDepth + 2); + src->addFmt("bool activeMaskStackSub{:04x}[{}];" _CRLF, subroutineInfo.cfAddr, subroutineMaxStackDepth + 1); + src->addFmt("bool activeMaskStackCSub{:04x}[{}];" _CRLF, subroutineInfo.cfAddr, subroutineMaxStackDepth + 2); } } // helper variables for cube maps (todo: Only emit when used) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp index 804d03cc..4061be33 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp @@ -335,6 +335,7 @@ void RendererShaderVk::CompileInternal(bool isRenderThread) if (!Shader.parse(&Resources, 100, false, messagesParseLink)) { cemuLog_log(LogType::Force, fmt::format("GLSL parsing failed for {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog())); + cemuLog_logDebug(LogType::Force, "GLSL source:\n{}", m_glslCode); cemu_assert_debug(false); FinishCompilation(); return; From 14dd7a72a7cb1a41e32b94c7bd32ef7dea418324 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 14 Sep 2023 19:46:54 +0200 Subject: [PATCH 029/101] Add coding style guidelines and clang-format file --- .clang-format | 65 ++++++++++++++++++++++++++++++++ CODING_STYLE.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 11 +++--- 3 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 .clang-format create mode 100644 CODING_STYLE.md diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..b22a1048 --- /dev/null +++ b/.clang-format @@ -0,0 +1,65 @@ +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: true + AfterControlStatement: Always + AfterEnum: true + AfterExternBlock: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeElse: true + BeforeWhile: true + SplitEmptyFunction: false +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +ColumnLimit: 0 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceBeforeCtorInitializerColon: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +TabWidth: 4 +UseTab: Always diff --git a/CODING_STYLE.md b/CODING_STYLE.md new file mode 100644 index 00000000..26e11733 --- /dev/null +++ b/CODING_STYLE.md @@ -0,0 +1,98 @@ + +# Coding style guidelines for Cemu + +This document describes the latest version of our coding-style guidelines. Since we did not use this style from the beginning, older code may not adhere to these guidelines. Nevertheless, use these rules even if the surrounding code does not match. + +Cemu comes with a `.clang-format` file which is supported by most IDEs for formatting. Avoid auto-reformatting whole files, PRs with a lot of formatting changes are difficult to review. + +## Names for variables, functions and classes + +- Always prefix class member variables with `m_` +- Always prefix static class variables with `s_` +- For variable names: Camel case, starting with a lower case letter after the prefix. Examples: `m_option`, `s_audioVolume` +- For functions/class names: Use camel case starting with a capital letter. Examples: `MyClass`, `SetActive` +- Avoid underscores in variable names after the prefix. Use `m_myVariable` instead of `m_my_variable` + +## About types + +Cemu provides it's own set of basic fixed-width types. They are: +`uint8`, `sint8`, `uint16`, `sint16`, `uint32`, `sint32`, `uint64`, `sint64`. Always use these types over something like `uint32_t`. Using `size_t` is also acceptable where suitable. Avoid C types like `int` or `long`. The only exception is when interacting with external libraries which expect these types as parameters. + +## When and where to put brackets + +Always put curly-brackets (`{ }`) on their own line. Example: + +``` +void FooBar() +{ + if (m_hasFoo) + { + ... + } +} +``` +As an exception, you can put short lambdas onto the same line: +``` +SomeFunc([]() { .... }); +``` +You can skip brackets for single-statement `if`. Example: +``` +if (cond) + action(); +``` + +## Printing + +Avoid sprintf and similar C-style formatting API. Use `fmt::format()`. +In UI related code you can use `formatWxString`, but be aware that number formatting with this function will be locale dependent! + +## Strings and encoding + +We use UTF-8 encoded `std::string` where possible. Some conversations need special handling and we have helper functions for those: +```cpp +// std::filesystem::path <-> std::string (in precompiled.h) +std::string _pathToUtf8(const fs::path& path); +fs::path _utf8ToPath(std::string_view input); + +// wxString <-> std::string +wxString to_wxString(std::string_view str); // in gui/helpers.h +std::string wxString::utf8_string(); + +``` + +## Logging + +If you want to write to log.txt use `cemuLog_log()`. The log type parameter should be mostly self-explanatory. Use `LogType::Force` if you always want to log something. For example: +`cemuLog_log(LogType::Force, "The value is {}", 123);` + +## HLE and endianness + +A pretty large part of Cemu's code base are re-implementations of various Cafe OS modules (e.g. `coreinit.rpl`, `gx2.rpl`...). These generally run in the context of the emulated process, thus special care has to be taken to use types with the correct size and endianness when interacting with memory. + +Keep in mind that the emulated Espresso CPU is 32bit big-endian, while the host architectures targeted by Cemu are 64bit litte-endian! + +To keep code simple and remove the need for manual endian-swapping, Cemu has templates and aliases of the basic types with explicit endian-ness. +For big-endian types add the suffix `be`. Example: `uint32be` + +When you need to store a pointer in the guest's memory. Use `MEMPTR`. It will automatically store any pointer as 32bit big-endian. The pointer you store must point to memory that is within the guest address space. + +## HLE interfaces + +The implementation for each HLE module is inside a namespace with a matching name. E.g. `coreinit.rpl` functions go into `coreinit` namespace. + +To expose a new function as callable from within the emulated machine, use `cafeExportRegister` or `cafeExportRegisterFunc`. Here is a short example: +```cpp +namespace coreinit +{ + uint32 OSGetCoreCount() + { + return Espresso::CORE_COUNT; + } + + void Init() + { + cafeExportRegister("coreinit", OSGetCoreCount, LogType::CoreinitThread); + } +} +``` +You may also see some code which uses `osLib_addFunction` directly. This is a deprecated way of registering functions. \ No newline at end of file diff --git a/README.md b/README.md index 01eb46fe..e57cb483 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Matrix Server](https://img.shields.io/matrix/cemu:cemu.info?server_fqdn=matrix.cemu.info&label=cemu:cemu.info&logo=matrix&logoColor=FFFFFF)](https://matrix.to/#/#cemu:cemu.info) This is the code repository of Cemu, a Wii U emulator that is able to run most Wii U games and homebrew in a playable state. -It's written in C/C++ and is being actively developed with new features and fixes to increase compatibility, convenience and usability. +It's written in C/C++ and is being actively developed with new features and fixes. Cemu is currently only available for 64-bit Windows, Linux & macOS devices. @@ -24,11 +24,9 @@ Cemu is currently only available for 64-bit Windows, Linux & macOS devices. ## Download -You can download the latest Cemu releases from the [GitHub Releases](https://github.com/cemu-project/Cemu/releases/) or from [Cemu's website](https://cemu.info). +You can download the latest Cemu releases for Windows, Linux and Mac from the [GitHub Releases](https://github.com/cemu-project/Cemu/releases/). For Linux you can also find Cemu on [flathub](https://flathub.org/apps/info.cemu.Cemu). -Cemu is currently only available in a portable format so no installation is required besides extracting it in a safe place. - -The native Linux build is currently a work-in-progress. See [Current State Of Linux builds](https://github.com/cemu-project/Cemu/issues/107) for more information about the things to be aware of. +On Windows Cemu is currently only available in a portable format so no installation is required besides extracting it in a safe place. The native macOS build is currently purely experimental and should not be considered stable or ready for issue-free gameplay. There are also known issues with degraded performance due to the use of MoltenVK and Rosetta for ARM Macs. We appreciate your patience while we improve Cemu for macOS. @@ -36,7 +34,7 @@ Pre-2.0 releases can be found on Cemu's [changelog page](https://cemu.info/chang ## Build Instructions -To compile Cemu yourself on Windows, Linux or macOS, view the [BUILD.md file](/BUILD.md). +To compile Cemu yourself on Windows, Linux or macOS, view [BUILD.md](/BUILD.md). ## Issues @@ -46,6 +44,7 @@ The old bug tracker can be found at [bugs.cemu.info](https://bugs.cemu.info) and ## Contributing Pull requests are very welcome. For easier coordination you can visit the developer discussion channel on [Discord](https://discord.gg/5psYsup) or alternatively the [Matrix Server](https://matrix.to/#/#cemu:cemu.info). +Before submitting a pull request, please read and follow our code style guidelines listed in [CODING_STYLE.md](/CODING_STYLE.md). If coding isn't your thing, testing games and making detailed bug reports or updating the (usually outdated) compatibility wiki is also appreciated! From 2a735f1fb72367ce72a6fae9a6034dd7ab979a2c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:22:54 +0200 Subject: [PATCH 030/101] coreinit: Use native COS locks instead of STL --- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 17 +++++++++-------- src/Cafe/OS/libs/coreinit/coreinit_FS.h | 4 +++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 26636eae..a2f59d4d 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -42,17 +42,16 @@ bool strcpy_whole(char* dst, size_t dstLength, const char* src) namespace coreinit { - std::mutex sFSClientLock; - std::recursive_mutex sFSGlobalMutex; + SysAllocator s_fsGlobalMutex; inline void FSLockMutex() { - sFSGlobalMutex.lock(); + OSLockMutex(&s_fsGlobalMutex); } inline void FSUnlockMutex() { - sFSGlobalMutex.unlock(); + OSUnlockMutex(&s_fsGlobalMutex); } void _debugVerifyCommand(const char* stage, FSCmdBlockBody_t* fsCmdBlockBody); @@ -251,7 +250,7 @@ namespace coreinit fsCmdQueueBE->dequeueHandlerFuncMPTR = _swapEndianU32(dequeueHandlerFuncMPTR); fsCmdQueueBE->numCommandsInFlight = 0; fsCmdQueueBE->numMaxCommandsInFlight = numMaxCommandsInFlight; - coreinit::OSInitMutexEx(&fsCmdQueueBE->mutex, nullptr); + coreinit::OSFastMutex_Init(&fsCmdQueueBE->fastMutex, nullptr); fsCmdQueueBE->firstMPTR = _swapEndianU32(0); fsCmdQueueBE->lastMPTR = _swapEndianU32(0); } @@ -672,12 +671,12 @@ namespace coreinit _debugVerifyCommand("FSCmdSubmitResult", fsCmdBlockBody); FSClientBody_t* fsClientBody = fsCmdBlockBody->fsClientBody.GetPtr(); - sFSClientLock.lock(); // OSFastMutex_Lock(&fsClientBody->fsCmdQueue.mutex) + OSFastMutex_Lock(&fsClientBody->fsCmdQueue.fastMutex); fsCmdBlockBody->cancelState &= ~(1 << 0); // clear cancel bit if (fsClientBody->currentCmdBlockBody.GetPtr() == fsCmdBlockBody) fsClientBody->currentCmdBlockBody = nullptr; fsCmdBlockBody->statusCode = _swapEndianU32(FSA_CMD_STATUS_CODE_D900A24); - sFSClientLock.unlock(); + OSFastMutex_Unlock(&fsClientBody->fsCmdQueue.fastMutex); // send result via msg queue or callback cemu_assert_debug(!fsCmdBlockBody->asyncResult.fsAsyncParamsNew.ioMsgQueue != !fsCmdBlockBody->asyncResult.fsAsyncParamsNew.userCallback); // either must be set fsCmdBlockBody->ukn09EA = 0; @@ -1433,7 +1432,7 @@ namespace coreinit return (FSStatus)FS_RESULT::SUCCESS; } - sint32 FSAppendFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 size, uint32 count, uint32 fileHandle, uint32 errorMask) + sint32 FSAppendFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 size, uint32 count, uint32 fileHandle, uint32 errorMask) { StackAllocator asyncParams; __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); @@ -2640,6 +2639,8 @@ namespace coreinit void InitializeFS() { + OSInitMutex(&s_fsGlobalMutex); + cafeExportRegister("coreinit", FSInit, LogType::CoreinitFile); cafeExportRegister("coreinit", FSShutdown, LogType::CoreinitFile); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.h b/src/Cafe/OS/libs/coreinit/coreinit_FS.h index 0355c9aa..2a57f7da 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.h @@ -42,7 +42,7 @@ namespace coreinit /* +0x00 */ MPTR firstMPTR; /* +0x04 */ MPTR lastMPTR; - /* +0x08 */ OSMutex mutex; + /* +0x08 */ OSFastMutex fastMutex; /* +0x34 */ MPTR dequeueHandlerFuncMPTR; /* +0x38 */ uint32be numCommandsInFlight; /* +0x3C */ uint32 numMaxCommandsInFlight; @@ -50,6 +50,8 @@ namespace coreinit }; DEFINE_ENUM_FLAG_OPERATORS(FSCmdQueue::QUEUE_FLAG); + static_assert(sizeof(FSCmdQueue) == 0x44); + #define FS_CLIENT_BUFFER_SIZE (5888) #define FS_CMD_BLOCK_SIZE (2688) From 98b5a8758ab30bb4d04ab99143737b48a43aa71f Mon Sep 17 00:00:00 2001 From: Simon <113838661+ssievert42@users.noreply.github.com> Date: Tue, 19 Sep 2023 01:27:40 +0200 Subject: [PATCH 031/101] nsyshid: Add backends for cross platform USB passthrough support (#950) --- .github/workflows/build.yml | 2 +- BUILD.md | 2 +- CMakeLists.txt | 17 + cmake/Findlibusb.cmake | 20 + src/Cafe/CMakeLists.txt | 19 + .../OS/libs/nsyshid/AttachDefaultBackends.cpp | 41 + src/Cafe/OS/libs/nsyshid/Backend.h | 141 +++ src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp | 791 +++++++++++++ src/Cafe/OS/libs/nsyshid/BackendLibusb.h | 129 ++ .../OS/libs/nsyshid/BackendWindowsHID.cpp | 454 +++++++ src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h | 66 ++ src/Cafe/OS/libs/nsyshid/Whitelist.cpp | 53 + src/Cafe/OS/libs/nsyshid/Whitelist.h | 32 + src/Cafe/OS/libs/nsyshid/nsyshid.cpp | 1038 ++++++++--------- src/Cafe/OS/libs/nsyshid/nsyshid.h | 9 +- .../api/Wiimote/windows/WinWiimoteDevice.cpp | 3 + vcpkg.json | 3 +- 17 files changed, 2297 insertions(+), 523 deletions(-) create mode 100644 cmake/Findlibusb.cmake create mode 100644 src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp create mode 100644 src/Cafe/OS/libs/nsyshid/Backend.h create mode 100644 src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp create mode 100644 src/Cafe/OS/libs/nsyshid/BackendLibusb.h create mode 100644 src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp create mode 100644 src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h create mode 100644 src/Cafe/OS/libs/nsyshid/Whitelist.cpp create mode 100644 src/Cafe/OS/libs/nsyshid/Whitelist.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a91d562b..eb6ac099 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -232,7 +232,7 @@ jobs: - name: "Install system dependencies" run: | brew update - brew install llvm@15 ninja nasm molten-vk + brew install llvm@15 ninja nasm molten-vk automake libtool - name: "Bootstrap vcpkg" run: | diff --git a/BUILD.md b/BUILD.md index 5ff9bfd5..da6c03ce 100644 --- a/BUILD.md +++ b/BUILD.md @@ -86,7 +86,7 @@ You can skip this section if you have an Intel Mac. Every time you compile, you ### Installing dependencies -`brew install boost git cmake llvm ninja nasm molten-vk` +`brew install boost git cmake llvm ninja nasm molten-vk automake libtool` ### Build Cemu using cmake and clang 1. `git clone --recursive https://github.com/cemu-project/Cemu` diff --git a/CMakeLists.txt b/CMakeLists.txt index 34a28a06..a5749acb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,23 @@ if (WIN32) endif() option(ENABLE_CUBEB "Enabled cubeb backend" ON) +# usb hid backends +if (WIN32) + option(ENABLE_NSYSHID_WINDOWS_HID "Enables the native Windows HID backend for nsyshid" ON) +endif () +# libusb and windows hid backends shouldn't be active at the same time; otherwise we'd see all devices twice! +if (NOT ENABLE_NSYSHID_WINDOWS_HID) + option(ENABLE_NSYSHID_LIBUSB "Enables the libusb backend for nsyshid" ON) +else () + set(ENABLE_NSYSHID_LIBUSB OFF CACHE BOOL "" FORCE) +endif () +if (ENABLE_NSYSHID_WINDOWS_HID) + add_compile_definitions(NSYSHID_ENABLE_BACKEND_WINDOWS_HID) +endif () +if (ENABLE_NSYSHID_LIBUSB) + add_compile_definitions(NSYSHID_ENABLE_BACKEND_LIBUSB) +endif () + option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON) set(THREADS_PREFER_PTHREAD_FLAG true) diff --git a/cmake/Findlibusb.cmake b/cmake/Findlibusb.cmake new file mode 100644 index 00000000..85da6736 --- /dev/null +++ b/cmake/Findlibusb.cmake @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2022 Andrea Pappacoda +# SPDX-License-Identifier: ISC + +find_package(libusb CONFIG) +if (NOT libusb_FOUND) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_search_module(libusb IMPORTED_TARGET GLOBAL libusb-1.0 libusb) + if (libusb_FOUND) + add_library(libusb::libusb ALIAS PkgConfig::libusb) + endif () + endif () +endif () + +find_package_handle_standard_args(libusb + REQUIRED_VARS + libusb_LINK_LIBRARIES + libusb_FOUND + VERSION_VAR libusb_VERSION +) diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index b7656789..29c5a0b3 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -434,6 +434,14 @@ add_library(CemuCafe OS/libs/nn_uds/nn_uds.h OS/libs/nsyshid/nsyshid.cpp OS/libs/nsyshid/nsyshid.h + OS/libs/nsyshid/Backend.h + OS/libs/nsyshid/AttachDefaultBackends.cpp + OS/libs/nsyshid/Whitelist.cpp + OS/libs/nsyshid/Whitelist.h + OS/libs/nsyshid/BackendLibusb.cpp + OS/libs/nsyshid/BackendLibusb.h + OS/libs/nsyshid/BackendWindowsHID.cpp + OS/libs/nsyshid/BackendWindowsHID.h OS/libs/nsyskbd/nsyskbd.cpp OS/libs/nsyskbd/nsyskbd.h OS/libs/nsysnet/nsysnet.cpp @@ -524,6 +532,17 @@ if (ENABLE_WAYLAND) target_link_libraries(CemuCafe PUBLIC Wayland::Client) endif() +if (ENABLE_NSYSHID_LIBUSB) + if (ENABLE_VCPKG) + find_package(libusb CONFIG REQUIRED) + target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS}) + target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES}) + else () + find_package(libusb MODULE REQUIRED) + target_link_libraries(CemuCafe PRIVATE libusb::libusb) + endif () +endif () + if (ENABLE_WXWIDGETS) target_link_libraries(CemuCafe PRIVATE wx::base wx::core) endif() diff --git a/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp b/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp new file mode 100644 index 00000000..6e6cb123 --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp @@ -0,0 +1,41 @@ +#include "nsyshid.h" +#include "Backend.h" + +#if NSYSHID_ENABLE_BACKEND_LIBUSB + +#include "BackendLibusb.h" + +#endif + +#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID + +#include "BackendWindowsHID.h" + +#endif + +namespace nsyshid::backend +{ + void AttachDefaultBackends() + { +#if NSYSHID_ENABLE_BACKEND_LIBUSB + // add libusb backend + { + auto backendLibusb = std::make_shared(); + if (backendLibusb->IsInitialisedOk()) + { + AttachBackend(backendLibusb); + } + } +#endif // NSYSHID_ENABLE_BACKEND_LIBUSB +#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID + // add windows hid backend + { + auto backendWindowsHID = std::make_shared(); + if (backendWindowsHID->IsInitialisedOk()) + { + AttachBackend(backendWindowsHID); + } + } +#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID + } +} // namespace nsyshid::backend diff --git a/src/Cafe/OS/libs/nsyshid/Backend.h b/src/Cafe/OS/libs/nsyshid/Backend.h new file mode 100644 index 00000000..641104f5 --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/Backend.h @@ -0,0 +1,141 @@ +#ifndef CEMU_NSYSHID_BACKEND_H +#define CEMU_NSYSHID_BACKEND_H + +#include +#include +#include + +#include "Common/precompiled.h" + +namespace nsyshid +{ + typedef struct + { + /* +0x00 */ uint32be handle; + /* +0x04 */ uint32 ukn04; + /* +0x08 */ uint16 vendorId; // little-endian ? + /* +0x0A */ uint16 productId; // little-endian ? + /* +0x0C */ uint8 ifIndex; + /* +0x0D */ uint8 subClass; + /* +0x0E */ uint8 protocol; + /* +0x0F */ uint8 paddingGuessed0F; + /* +0x10 */ uint16be maxPacketSizeRX; + /* +0x12 */ uint16be maxPacketSizeTX; + } HID_t; + + static_assert(offsetof(HID_t, vendorId) == 0x8, ""); + static_assert(offsetof(HID_t, productId) == 0xA, ""); + static_assert(offsetof(HID_t, ifIndex) == 0xC, ""); + static_assert(offsetof(HID_t, protocol) == 0xE, ""); + + class Device { + public: + Device() = delete; + + Device(uint16 vendorId, + uint16 productId, + uint8 interfaceIndex, + uint8 interfaceSubClass, + uint8 protocol); + + Device(const Device& device) = delete; + + Device& operator=(const Device& device) = delete; + + virtual ~Device() = default; + + HID_t* m_hid; // this info is passed to applications and must remain intact + + uint16 m_vendorId; + uint16 m_productId; + uint8 m_interfaceIndex; + uint8 m_interfaceSubClass; + uint8 m_protocol; + uint16 m_maxPacketSizeRX; + uint16 m_maxPacketSizeTX; + + virtual void AssignHID(HID_t* hid); + + virtual bool Open() = 0; + + virtual void Close() = 0; + + virtual bool IsOpened() = 0; + + enum class ReadResult + { + Success, + Error, + ErrorTimeout, + }; + + virtual ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) = 0; + + enum class WriteResult + { + Success, + Error, + ErrorTimeout, + }; + + virtual WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) = 0; + + virtual bool GetDescriptor(uint8 descType, + uint8 descIndex, + uint8 lang, + uint8* output, + uint32 outputMaxLength) = 0; + + virtual bool SetProtocol(uint32 ifIndef, uint32 protocol) = 0; + + virtual bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) = 0; + }; + + class Backend { + public: + Backend(); + + Backend(const Backend& backend) = delete; + + Backend& operator=(const Backend& backend) = delete; + + virtual ~Backend() = default; + + void DetachAllDevices(); + + // called from nsyshid when this backend is attached - do not call this yourself! + void OnAttach(); + + // called from nsyshid when this backend is detached - do not call this yourself! + void OnDetach(); + + bool IsBackendAttached(); + + virtual bool IsInitialisedOk() = 0; + + protected: + // try to attach a device - only works if this backend is attached + bool AttachDevice(const std::shared_ptr& device); + + void DetachDevice(const std::shared_ptr& device); + + std::shared_ptr FindDevice(std::function&)> isWantedDevice); + + bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId); + + // called from OnAttach() - attach devices that your backend can see here + virtual void AttachVisibleDevices() = 0; + + private: + std::list> m_devices; + std::recursive_mutex m_devicesMutex; + bool m_isAttached; + }; + + namespace backend + { + void AttachDefaultBackends(); + } +} // namespace nsyshid + +#endif // CEMU_NSYSHID_BACKEND_H diff --git a/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp b/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp new file mode 100644 index 00000000..4f88b7ed --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp @@ -0,0 +1,791 @@ +#include "BackendLibusb.h" + +#if NSYSHID_ENABLE_BACKEND_LIBUSB + +namespace nsyshid::backend::libusb +{ + BackendLibusb::BackendLibusb() + : m_ctx(nullptr), + m_initReturnCode(0), + m_callbackRegistered(false), + m_hotplugCallbackHandle(0), + m_hotplugThreadStop(false) + { + m_initReturnCode = libusb_init(&m_ctx); + if (m_initReturnCode < 0) + { + m_ctx = nullptr; + cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb with return code %i", + m_initReturnCode); + return; + } + + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + { + int ret = libusb_hotplug_register_callback(m_ctx, + (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), + (libusb_hotplug_flag)0, + LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, + HotplugCallback, + this, + &m_hotplugCallbackHandle); + if (ret != LIBUSB_SUCCESS) + { + cemuLog_logDebug(LogType::Force, + "nsyshid::BackendLibusb: failed to register hotplug callback with return code %i", + ret); + } + else + { + cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: registered hotplug callback"); + m_callbackRegistered = true; + m_hotplugThread = std::thread([this] { + while (!m_hotplugThreadStop) + { + timeval timeout{ + .tv_sec = 1, + .tv_usec = 0, + }; + int ret = libusb_handle_events_timeout_completed(m_ctx, &timeout, nullptr); + if (ret != 0) + { + cemuLog_logDebug(LogType::Force, + "nsyshid::BackendLibusb: hotplug thread: error handling events: {}", + ret); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + } + }); + } + } + else + { + cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: hotplug not supported by this version of libusb"); + } + } + + bool BackendLibusb::IsInitialisedOk() + { + return m_initReturnCode == 0; + } + + void BackendLibusb::AttachVisibleDevices() + { + // add all currently connected devices + libusb_device** devices; + ssize_t deviceCount = libusb_get_device_list(m_ctx, &devices); + if (deviceCount < 0) + { + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb: failed to get usb devices"); + return; + } + libusb_device* dev; + for (int i = 0; (dev = devices[i]) != nullptr; i++) + { + auto device = CheckAndCreateDevice(dev); + if (device != nullptr) + { + if (IsDeviceWhitelisted(device->m_vendorId, device->m_productId)) + { + if (!AttachDevice(device)) + { + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb: failed to attach device: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + } + } + else + { + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb: device not on whitelist: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + } + } + } + + libusb_free_device_list(devices, 1); + } + + int BackendLibusb::HotplugCallback(libusb_context* ctx, + libusb_device* dev, + libusb_hotplug_event event, + void* user_data) + { + if (user_data) + { + BackendLibusb* backend = static_cast(user_data); + return backend->OnHotplug(dev, event); + } + return 0; + } + + int BackendLibusb::OnHotplug(libusb_device* dev, libusb_hotplug_event event) + { + struct libusb_device_descriptor desc; + int ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) + { + cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): failed to get device descriptor"); + return 0; + } + + switch (event) + { + case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: + { + cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device arrived: {:04x}:{:04x}", + desc.idVendor, + desc.idProduct); + auto device = CheckAndCreateDevice(dev); + if (device != nullptr) + { + if (IsDeviceWhitelisted(device->m_vendorId, device->m_productId)) + { + if (!AttachDevice(device)) + { + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb::OnHotplug(): failed to attach device: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + } + } + else + { + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb::OnHotplug(): device not on whitelist: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + } + } + } + break; + case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: + { + cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device left: {:04x}:{:04x}", + desc.idVendor, + desc.idProduct); + auto device = FindLibusbDevice(dev); + if (device != nullptr) + { + DetachDevice(device); + } + } + break; + } + + return 0; + } + + BackendLibusb::~BackendLibusb() + { + if (m_callbackRegistered) + { + m_hotplugThreadStop = true; + libusb_hotplug_deregister_callback(m_ctx, m_hotplugCallbackHandle); + m_hotplugThread.join(); + } + DetachAllDevices(); + if (m_ctx) + { + libusb_exit(m_ctx); + m_ctx = nullptr; + } + } + + std::shared_ptr BackendLibusb::FindLibusbDevice(libusb_device* dev) + { + libusb_device_descriptor desc; + int ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) + { + cemuLog_logDebug(LogType::Force, + "nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor"); + return nullptr; + } + uint8 busNumber = libusb_get_bus_number(dev); + uint8 deviceAddress = libusb_get_device_address(dev); + auto device = FindDevice([desc, busNumber, deviceAddress](const std::shared_ptr& d) -> bool { + auto device = std::dynamic_pointer_cast(d); + if (device != nullptr && + desc.idVendor == device->m_vendorId && + desc.idProduct == device->m_productId && + busNumber == device->m_libusbBusNumber && + deviceAddress == device->m_libusbDeviceAddress) + { + // we found our device! + return true; + } + return false; + }); + + if (device != nullptr) + { + return device; + } + return nullptr; + } + + std::shared_ptr BackendLibusb::CheckAndCreateDevice(libusb_device* dev) + { + struct libusb_device_descriptor desc; + int ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) + { + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb::CheckAndCreateDevice(): failed to get device descriptor; return code: %i", + ret); + return nullptr; + } + if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241) + { + cemuLog_logDebug(LogType::Force, + "nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected"); + } + auto device = std::make_shared(m_ctx, + desc.idVendor, + desc.idProduct, + 1, + 2, + 0, + libusb_get_bus_number(dev), + libusb_get_device_address(dev)); + // figure out device endpoints + if (!FindDefaultDeviceEndpoints(dev, + device->m_libusbHasEndpointIn, + device->m_libusbEndpointIn, + device->m_maxPacketSizeRX, + device->m_libusbHasEndpointOut, + device->m_libusbEndpointOut, + device->m_maxPacketSizeTX)) + { + // most likely couldn't read config descriptor + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb::CheckAndCreateDevice(): failed to find default endpoints for device: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + return nullptr; + } + return device; + } + + bool BackendLibusb::FindDefaultDeviceEndpoints(libusb_device* dev, bool& endpointInFound, uint8& endpointIn, + uint16& endpointInMaxPacketSize, bool& endpointOutFound, + uint8& endpointOut, uint16& endpointOutMaxPacketSize) + { + endpointInFound = false; + endpointIn = 0; + endpointInMaxPacketSize = 0; + endpointOutFound = false; + endpointOut = 0; + endpointOutMaxPacketSize = 0; + + struct libusb_config_descriptor* conf = nullptr; + int ret = libusb_get_active_config_descriptor(dev, &conf); + + if (ret == 0) + { + for (uint8 interfaceIndex = 0; interfaceIndex < conf->bNumInterfaces; interfaceIndex++) + { + const struct libusb_interface& interface = conf->interface[interfaceIndex]; + for (int altsettingIndex = 0; altsettingIndex < interface.num_altsetting; altsettingIndex++) + { + const struct libusb_interface_descriptor& altsetting = interface.altsetting[altsettingIndex]; + for (uint8 endpointIndex = 0; endpointIndex < altsetting.bNumEndpoints; endpointIndex++) + { + const struct libusb_endpoint_descriptor& endpoint = altsetting.endpoint[endpointIndex]; + // figure out direction + if ((endpoint.bEndpointAddress & (1 << 7)) != 0) + { + // in + if (!endpointInFound) + { + endpointInFound = true; + endpointIn = endpoint.bEndpointAddress; + endpointInMaxPacketSize = endpoint.wMaxPacketSize; + } + } + else + { + // out + if (!endpointOutFound) + { + endpointOutFound = true; + endpointOut = endpoint.bEndpointAddress; + endpointOutMaxPacketSize = endpoint.wMaxPacketSize; + } + } + } + } + } + libusb_free_config_descriptor(conf); + return true; + } + return false; + } + + DeviceLibusb::DeviceLibusb(libusb_context* ctx, + uint16 vendorId, + uint16 productId, + uint8 interfaceIndex, + uint8 interfaceSubClass, + uint8 protocol, + uint8 libusbBusNumber, + uint8 libusbDeviceAddress) + : Device(vendorId, + productId, + interfaceIndex, + interfaceSubClass, + protocol), + m_ctx(ctx), + m_libusbHandle(nullptr), + m_handleInUseCounter(-1), + m_libusbBusNumber(libusbBusNumber), + m_libusbDeviceAddress(libusbDeviceAddress), + m_libusbHasEndpointIn(false), + m_libusbEndpointIn(0), + m_libusbHasEndpointOut(false), + m_libusbEndpointOut(0) + { + } + + DeviceLibusb::~DeviceLibusb() + { + CloseDevice(); + } + + bool DeviceLibusb::Open() + { + std::unique_lock lock(m_handleMutex); + if (IsOpened()) + { + return true; + } + // we may still be in the process of closing the device; wait for that to finish + while (m_handleInUseCounter != -1) + { + m_handleInUseCounterDecremented.wait(lock); + } + + libusb_device** devices; + ssize_t deviceCount = libusb_get_device_list(m_ctx, &devices); + if (deviceCount < 0) + { + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::open(): failed to get usb devices"); + return false; + } + libusb_device* dev; + libusb_device* found = nullptr; + for (int i = 0; (dev = devices[i]) != nullptr; i++) + { + struct libusb_device_descriptor desc; + int ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) + { + cemuLog_log(LogType::Force, + "nsyshid::DeviceLibusb::open(): failed to get device descriptor; return code: %i", + ret); + libusb_free_device_list(devices, 1); + return false; + } + if (desc.idVendor == this->m_vendorId && + desc.idProduct == this->m_productId && + libusb_get_bus_number(dev) == this->m_libusbBusNumber && + libusb_get_device_address(dev) == this->m_libusbDeviceAddress) + { + // we found our device! + found = dev; + break; + } + } + + if (found != nullptr) + { + { + int ret = libusb_open(dev, &(this->m_libusbHandle)); + if (ret < 0) + { + this->m_libusbHandle = nullptr; + cemuLog_log(LogType::Force, + "nsyshid::DeviceLibusb::open(): failed to open device; return code: %i", + ret); + libusb_free_device_list(devices, 1); + return false; + } + this->m_handleInUseCounter = 0; + } + if (libusb_kernel_driver_active(this->m_libusbHandle, 0) == 1) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): kernel driver active"); + if (libusb_detach_kernel_driver(this->m_libusbHandle, 0) == 0) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): kernel driver detached"); + } + else + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): failed to detach kernel driver"); + } + } + { + int ret = libusb_claim_interface(this->m_libusbHandle, 0); + if (ret != 0) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface"); + } + } + } + + libusb_free_device_list(devices, 1); + return found != nullptr; + } + + void DeviceLibusb::Close() + { + CloseDevice(); + } + + void DeviceLibusb::CloseDevice() + { + std::unique_lock lock(m_handleMutex); + if (IsOpened()) + { + auto handle = m_libusbHandle; + m_libusbHandle = nullptr; + while (m_handleInUseCounter > 0) + { + m_handleInUseCounterDecremented.wait(lock); + } + libusb_release_interface(handle, 0); + libusb_close(handle); + m_handleInUseCounter = -1; + m_handleInUseCounterDecremented.notify_all(); + } + } + + bool DeviceLibusb::IsOpened() + { + return m_libusbHandle != nullptr && m_handleInUseCounter >= 0; + } + + Device::ReadResult DeviceLibusb::Read(uint8* data, sint32 length, sint32& bytesRead) + { + auto handleLock = AquireHandleLock(); + if (!handleLock->IsValid()) + { + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n"); + return ReadResult::Error; + } + + const unsigned int timeout = 50; + int actualLength = 0; + int ret = 0; + do + { + ret = libusb_bulk_transfer(handleLock->GetHandle(), + this->m_libusbEndpointIn, + data, + length, + &actualLength, + timeout); + } + while (ret == LIBUSB_ERROR_TIMEOUT && actualLength == 0 && IsOpened()); + + if (ret == 0 || ret == LIBUSB_ERROR_TIMEOUT) + { + // success + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::read(): read {} of {} bytes", + actualLength, + length); + bytesRead = actualLength; + return ReadResult::Success; + } + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::read(): failed with error code: {}", + ret); + return ReadResult::Error; + } + + Device::WriteResult DeviceLibusb::Write(uint8* data, sint32 length, sint32& bytesWritten) + { + auto handleLock = AquireHandleLock(); + if (!handleLock->IsValid()) + { + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n"); + return WriteResult::Error; + } + + bytesWritten = 0; + int actualLength = 0; + int ret = libusb_bulk_transfer(handleLock->GetHandle(), + this->m_libusbEndpointOut, + data, + length, + &actualLength, + 0); + + if (ret == 0) + { + // success + bytesWritten = actualLength; + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::write(): wrote {} of {} bytes", + bytesWritten, + length); + return WriteResult::Success; + } + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::write(): failed with error code: {}", + ret); + return WriteResult::Error; + } + + bool DeviceLibusb::GetDescriptor(uint8 descType, + uint8 descIndex, + uint8 lang, + uint8* output, + uint32 outputMaxLength) + { + auto handleLock = AquireHandleLock(); + if (!handleLock->IsValid()) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::getDescriptor(): device is not opened"); + return false; + } + + if (descType == 0x02) + { + struct libusb_config_descriptor* conf = nullptr; + libusb_device* dev = libusb_get_device(handleLock->GetHandle()); + int ret = libusb_get_active_config_descriptor(dev, &conf); + + if (ret == 0) + { + std::vector configurationDescriptor(conf->wTotalLength); + uint8* currentWritePtr = &configurationDescriptor[0]; + + // configuration descriptor + cemu_assert_debug(conf->bLength == LIBUSB_DT_CONFIG_SIZE); + *(uint8*)(currentWritePtr + 0) = conf->bLength; // bLength + *(uint8*)(currentWritePtr + 1) = conf->bDescriptorType; // bDescriptorType + *(uint16be*)(currentWritePtr + 2) = conf->wTotalLength; // wTotalLength + *(uint8*)(currentWritePtr + 4) = conf->bNumInterfaces; // bNumInterfaces + *(uint8*)(currentWritePtr + 5) = conf->bConfigurationValue; // bConfigurationValue + *(uint8*)(currentWritePtr + 6) = conf->iConfiguration; // iConfiguration + *(uint8*)(currentWritePtr + 7) = conf->bmAttributes; // bmAttributes + *(uint8*)(currentWritePtr + 8) = conf->MaxPower; // MaxPower + currentWritePtr = currentWritePtr + conf->bLength; + + for (uint8_t interfaceIndex = 0; interfaceIndex < conf->bNumInterfaces; interfaceIndex++) + { + const struct libusb_interface& interface = conf->interface[interfaceIndex]; + for (int altsettingIndex = 0; altsettingIndex < interface.num_altsetting; altsettingIndex++) + { + // interface descriptor + const struct libusb_interface_descriptor& altsetting = interface.altsetting[altsettingIndex]; + cemu_assert_debug(altsetting.bLength == LIBUSB_DT_INTERFACE_SIZE); + *(uint8*)(currentWritePtr + 0) = altsetting.bLength; // bLength + *(uint8*)(currentWritePtr + 1) = altsetting.bDescriptorType; // bDescriptorType + *(uint8*)(currentWritePtr + 2) = altsetting.bInterfaceNumber; // bInterfaceNumber + *(uint8*)(currentWritePtr + 3) = altsetting.bAlternateSetting; // bAlternateSetting + *(uint8*)(currentWritePtr + 4) = altsetting.bNumEndpoints; // bNumEndpoints + *(uint8*)(currentWritePtr + 5) = altsetting.bInterfaceClass; // bInterfaceClass + *(uint8*)(currentWritePtr + 6) = altsetting.bInterfaceSubClass; // bInterfaceSubClass + *(uint8*)(currentWritePtr + 7) = altsetting.bInterfaceProtocol; // bInterfaceProtocol + *(uint8*)(currentWritePtr + 8) = altsetting.iInterface; // iInterface + currentWritePtr = currentWritePtr + altsetting.bLength; + + if (altsetting.extra_length > 0) + { + // unknown descriptors - copy the ones that we can identify ourselves + const unsigned char* extraReadPointer = altsetting.extra; + while (extraReadPointer - altsetting.extra < altsetting.extra_length) + { + uint8 bLength = *(uint8*)(extraReadPointer + 0); + if (bLength == 0) + { + // prevent endless loop + break; + } + if (extraReadPointer + bLength - altsetting.extra > altsetting.extra_length) + { + // prevent out of bounds read + break; + } + uint8 bDescriptorType = *(uint8*)(extraReadPointer + 1); + // HID descriptor + if (bDescriptorType == LIBUSB_DT_HID && bLength == 9) + { + *(uint8*)(currentWritePtr + 0) = + *(uint8*)(extraReadPointer + 0); // bLength + *(uint8*)(currentWritePtr + 1) = + *(uint8*)(extraReadPointer + 1); // bDescriptorType + *(uint16be*)(currentWritePtr + 2) = + *(uint16*)(extraReadPointer + 2); // bcdHID + *(uint8*)(currentWritePtr + 4) = + *(uint8*)(extraReadPointer + 4); // bCountryCode + *(uint8*)(currentWritePtr + 5) = + *(uint8*)(extraReadPointer + 5); // bNumDescriptors + *(uint8*)(currentWritePtr + 6) = + *(uint8*)(extraReadPointer + 6); // bDescriptorType + *(uint16be*)(currentWritePtr + 7) = + *(uint16*)(extraReadPointer + 7); // wDescriptorLength + currentWritePtr += bLength; + } + extraReadPointer += bLength; + } + } + + for (int endpointIndex = 0; endpointIndex < altsetting.bNumEndpoints; endpointIndex++) + { + // endpoint descriptor + const struct libusb_endpoint_descriptor& endpoint = altsetting.endpoint[endpointIndex]; + cemu_assert_debug(endpoint.bLength == LIBUSB_DT_ENDPOINT_SIZE || + endpoint.bLength == LIBUSB_DT_ENDPOINT_AUDIO_SIZE); + *(uint8*)(currentWritePtr + 0) = endpoint.bLength; + *(uint8*)(currentWritePtr + 1) = endpoint.bDescriptorType; + *(uint8*)(currentWritePtr + 2) = endpoint.bEndpointAddress; + *(uint8*)(currentWritePtr + 3) = endpoint.bmAttributes; + *(uint16be*)(currentWritePtr + 4) = endpoint.wMaxPacketSize; + *(uint8*)(currentWritePtr + 6) = endpoint.bInterval; + if (endpoint.bLength == LIBUSB_DT_ENDPOINT_AUDIO_SIZE) + { + *(uint8*)(currentWritePtr + 7) = endpoint.bRefresh; + *(uint8*)(currentWritePtr + 8) = endpoint.bSynchAddress; + } + currentWritePtr += endpoint.bLength; + } + } + } + uint32 bytesWritten = currentWritePtr - &configurationDescriptor[0]; + libusb_free_config_descriptor(conf); + cemu_assert_debug(bytesWritten <= conf->wTotalLength); + + memcpy(output, &configurationDescriptor[0], + std::min(outputMaxLength, bytesWritten)); + return true; + } + else + { + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::getDescriptor(): failed to get config descriptor with error code: {}", + ret); + return false; + } + } + else + { + cemu_assert_unimplemented(); + } + return false; + } + + bool DeviceLibusb::SetProtocol(uint32 ifIndex, uint32 protocol) + { + auto handleLock = AquireHandleLock(); + if (!handleLock->IsValid()) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): device is not opened"); + return false; + } + + // ToDo: implement this +#if 0 + // is this correct? Discarding "ifIndex" seems like a bad idea + int ret = libusb_set_configuration(handleLock->getHandle(), protocol); + if (ret == 0) { + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::setProtocol(): success"); + return true; + } + cemuLog_logDebug(LogType::Force, + "nsyshid::DeviceLibusb::setProtocol(): failed with error code: {}", + ret); + return false; +#endif + + // pretend that everything is fine + return true; + } + + bool DeviceLibusb::SetReport(uint8* reportData, sint32 length, uint8* originalData, + sint32 originalLength) + { + auto handleLock = AquireHandleLock(); + if (!handleLock->IsValid()) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): device is not opened"); + return false; + } + + // ToDo: implement this +#if 0 + // not sure if libusb_control_transfer() is the right candidate for this + int ret = libusb_control_transfer(handleLock->getHandle(), + bmRequestType, + bRequest, + wValue, + wIndex, + reportData, + length, + timeout); +#endif + + // pretend that everything is fine + return true; + } + + std::unique_ptr DeviceLibusb::AquireHandleLock() + { + return std::make_unique(&m_libusbHandle, + m_handleMutex, + m_handleInUseCounter, + m_handleInUseCounterDecremented, + *this); + } + + DeviceLibusb::HandleLock::HandleLock(libusb_device_handle** handle, + std::mutex& handleMutex, + std::atomic& handleInUseCounter, + std::condition_variable& handleInUseCounterDecremented, + DeviceLibusb& device) + : m_handle(nullptr), + m_handleMutex(handleMutex), + m_handleInUseCounter(handleInUseCounter), + m_handleInUseCounterDecremented(handleInUseCounterDecremented) + { + std::lock_guard lock(handleMutex); + if (device.IsOpened() && handle != nullptr && handleInUseCounter >= 0) + { + this->m_handle = *handle; + this->m_handleInUseCounter++; + } + } + + DeviceLibusb::HandleLock::~HandleLock() + { + if (IsValid()) + { + std::lock_guard lock(m_handleMutex); + m_handleInUseCounter--; + m_handleInUseCounterDecremented.notify_all(); + } + } + + bool DeviceLibusb::HandleLock::IsValid() + { + return m_handle != nullptr; + } + + libusb_device_handle* DeviceLibusb::HandleLock::GetHandle() + { + return m_handle; + } +} // namespace nsyshid::backend::libusb + +#endif // NSYSHID_ENABLE_BACKEND_LIBUSB diff --git a/src/Cafe/OS/libs/nsyshid/BackendLibusb.h b/src/Cafe/OS/libs/nsyshid/BackendLibusb.h new file mode 100644 index 00000000..216be6ce --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/BackendLibusb.h @@ -0,0 +1,129 @@ +#ifndef CEMU_NSYSHID_BACKEND_LIBUSB_H +#define CEMU_NSYSHID_BACKEND_LIBUSB_H + +#include "nsyshid.h" + +#if NSYSHID_ENABLE_BACKEND_LIBUSB + +#include +#include "Backend.h" + +namespace nsyshid::backend::libusb +{ + class BackendLibusb : public nsyshid::Backend { + public: + BackendLibusb(); + + ~BackendLibusb(); + + bool IsInitialisedOk() override; + + protected: + void AttachVisibleDevices() override; + + private: + libusb_context* m_ctx; + int m_initReturnCode; + bool m_callbackRegistered; + libusb_hotplug_callback_handle m_hotplugCallbackHandle; + std::thread m_hotplugThread; + std::atomic m_hotplugThreadStop; + + // called by libusb + static int HotplugCallback(libusb_context* ctx, libusb_device* dev, + libusb_hotplug_event event, void* user_data); + + int OnHotplug(libusb_device* dev, libusb_hotplug_event event); + + std::shared_ptr CheckAndCreateDevice(libusb_device* dev); + + std::shared_ptr FindLibusbDevice(libusb_device* dev); + + bool FindDefaultDeviceEndpoints(libusb_device* dev, + bool& endpointInFound, uint8& endpointIn, uint16& endpointInMaxPacketSize, + bool& endpointOutFound, uint8& endpointOut, uint16& endpointOutMaxPacketSize); + }; + + class DeviceLibusb : public nsyshid::Device { + public: + DeviceLibusb(libusb_context* ctx, + uint16 vendorId, + uint16 productId, + uint8 interfaceIndex, + uint8 interfaceSubClass, + uint8 protocol, + uint8 libusbBusNumber, + uint8 libusbDeviceAddress); + + ~DeviceLibusb() override; + + bool Open() override; + + void Close() override; + + bool IsOpened() override; + + ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) override; + + WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) override; + + bool GetDescriptor(uint8 descType, + uint8 descIndex, + uint8 lang, + uint8* output, + uint32 outputMaxLength) override; + + bool SetProtocol(uint32 ifIndex, uint32 protocol) override; + + bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override; + + uint8 m_libusbBusNumber; + uint8 m_libusbDeviceAddress; + bool m_libusbHasEndpointIn; + uint8 m_libusbEndpointIn; + bool m_libusbHasEndpointOut; + uint8 m_libusbEndpointOut; + + private: + void CloseDevice(); + + libusb_context* m_ctx; + std::mutex m_handleMutex; + std::atomic m_handleInUseCounter; + std::condition_variable m_handleInUseCounterDecremented; + libusb_device_handle* m_libusbHandle; + + class HandleLock { + public: + HandleLock() = delete; + + HandleLock(libusb_device_handle** handle, + std::mutex& handleMutex, + std::atomic& handleInUseCounter, + std::condition_variable& handleInUseCounterDecremented, + DeviceLibusb& device); + + ~HandleLock(); + + HandleLock(const HandleLock&) = delete; + + HandleLock& operator=(const HandleLock&) = delete; + + bool IsValid(); + + libusb_device_handle* GetHandle(); + + private: + libusb_device_handle* m_handle; + std::mutex& m_handleMutex; + std::atomic& m_handleInUseCounter; + std::condition_variable& m_handleInUseCounterDecremented; + }; + + std::unique_ptr AquireHandleLock(); + }; +} // namespace nsyshid::backend::libusb + +#endif // NSYSHID_ENABLE_BACKEND_LIBUSB + +#endif // CEMU_NSYSHID_BACKEND_LIBUSB_H diff --git a/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp b/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp new file mode 100644 index 00000000..520a0d31 --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp @@ -0,0 +1,454 @@ +#include "BackendWindowsHID.h" + +#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID + +#include +#include +#include + +#pragma comment(lib, "Setupapi.lib") +#pragma comment(lib, "hid.lib") + +DEFINE_GUID(GUID_DEVINTERFACE_HID, + 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30); + +namespace nsyshid::backend::windows +{ + BackendWindowsHID::BackendWindowsHID() + { + } + + void BackendWindowsHID::AttachVisibleDevices() + { + // add all currently connected devices + HDEVINFO hDevInfo; + SP_DEVICE_INTERFACE_DATA DevIntfData; + PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData; + SP_DEVINFO_DATA DevData; + + DWORD dwSize, dwMemberIdx; + + hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + + if (hDevInfo != INVALID_HANDLE_VALUE) + { + DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + dwMemberIdx = 0; + + SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_HID, + dwMemberIdx, &DevIntfData); + + while (GetLastError() != ERROR_NO_MORE_ITEMS) + { + DevData.cbSize = sizeof(DevData); + SetupDiGetDeviceInterfaceDetail( + hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL); + + DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + dwSize); + DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, + DevIntfDetailData, dwSize, &dwSize, &DevData)) + { + HANDLE hHIDDevice = OpenDevice(DevIntfDetailData->DevicePath); + if (hHIDDevice != INVALID_HANDLE_VALUE) + { + auto device = CheckAndCreateDevice(DevIntfDetailData->DevicePath, hHIDDevice); + if (device != nullptr) + { + if (IsDeviceWhitelisted(device->m_vendorId, device->m_productId)) + { + if (!AttachDevice(device)) + { + cemuLog_log(LogType::Force, + "nsyshid::BackendWindowsHID: failed to attach device: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + } + } + else + { + cemuLog_log(LogType::Force, + "nsyshid::BackendWindowsHID: device not on whitelist: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + } + } + CloseHandle(hHIDDevice); + } + } + HeapFree(GetProcessHeap(), 0, DevIntfDetailData); + // next + SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_HID, ++dwMemberIdx, &DevIntfData); + } + SetupDiDestroyDeviceInfoList(hDevInfo); + } + } + + BackendWindowsHID::~BackendWindowsHID() + { + } + + bool BackendWindowsHID::IsInitialisedOk() + { + return true; + } + + std::shared_ptr BackendWindowsHID::CheckAndCreateDevice(wchar_t* devicePath, HANDLE hDevice) + { + HIDD_ATTRIBUTES hidAttr; + hidAttr.Size = sizeof(HIDD_ATTRIBUTES); + if (HidD_GetAttributes(hDevice, &hidAttr) == FALSE) + return nullptr; + + auto device = std::make_shared(hidAttr.VendorID, + hidAttr.ProductID, + 1, + 2, + 0, + _wcsdup(devicePath)); + // get additional device info + sint32 maxPacketInputLength = -1; + sint32 maxPacketOutputLength = -1; + PHIDP_PREPARSED_DATA ppData = nullptr; + if (HidD_GetPreparsedData(hDevice, &ppData)) + { + HIDP_CAPS caps; + if (HidP_GetCaps(ppData, &caps) == HIDP_STATUS_SUCCESS) + { + // length includes the report id byte + maxPacketInputLength = caps.InputReportByteLength - 1; + maxPacketOutputLength = caps.OutputReportByteLength - 1; + } + HidD_FreePreparsedData(ppData); + } + if (maxPacketInputLength <= 0 || maxPacketInputLength >= 0xF000) + { + cemuLog_log(LogType::Force, "HID: Input packet length not available or out of range (length = {})", + maxPacketInputLength); + maxPacketInputLength = 0x20; + } + if (maxPacketOutputLength <= 0 || maxPacketOutputLength >= 0xF000) + { + cemuLog_log(LogType::Force, "HID: Output packet length not available or out of range (length = {})", + maxPacketOutputLength); + maxPacketOutputLength = 0x20; + } + + device->m_maxPacketSizeRX = maxPacketInputLength; + device->m_maxPacketSizeTX = maxPacketOutputLength; + + return device; + } + + DeviceWindowsHID::DeviceWindowsHID(uint16 vendorId, + uint16 productId, + uint8 interfaceIndex, + uint8 interfaceSubClass, + uint8 protocol, + wchar_t* devicePath) + : Device(vendorId, + productId, + interfaceIndex, + interfaceSubClass, + protocol), + m_devicePath(devicePath), + m_hFile(INVALID_HANDLE_VALUE) + { + } + + DeviceWindowsHID::~DeviceWindowsHID() + { + if (m_hFile != INVALID_HANDLE_VALUE) + { + CloseHandle(m_hFile); + m_hFile = INVALID_HANDLE_VALUE; + } + } + + bool DeviceWindowsHID::Open() + { + if (IsOpened()) + { + return true; + } + m_hFile = OpenDevice(m_devicePath); + if (m_hFile == INVALID_HANDLE_VALUE) + { + return false; + } + HidD_SetNumInputBuffers(m_hFile, 2); // don't cache too many reports + return true; + } + + void DeviceWindowsHID::Close() + { + if (m_hFile != INVALID_HANDLE_VALUE) + { + CloseHandle(m_hFile); + m_hFile = INVALID_HANDLE_VALUE; + } + } + + bool DeviceWindowsHID::IsOpened() + { + return m_hFile != INVALID_HANDLE_VALUE; + } + + Device::ReadResult DeviceWindowsHID::Read(uint8* data, sint32 length, sint32& bytesRead) + { + bytesRead = 0; + DWORD bt; + OVERLAPPED ovlp = {0}; + ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + uint8* tempBuffer = (uint8*)malloc(length + 1); + sint32 transferLength = 0; // minus report byte + + _debugPrintHex("HID_READ_BEFORE", data, length); + + cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", length); + BOOL readResult = ReadFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp); + if (readResult != FALSE) + { + // sometimes we get the result immediately + if (bt == 0) + transferLength = 0; + else + transferLength = bt - 1; + cemuLog_logDebug(LogType::Force, "HidRead Result received immediately (error 0x{:08x}) Length 0x{:08x}", + GetLastError(), transferLength); + } + else + { + // wait for result + cemuLog_logDebug(LogType::Force, "HidRead WaitForResult (error 0x{:08x})", GetLastError()); + // async hid read is never supposed to return unless there is a response? Lego Dimensions stops HIDRead calls as soon as one of them fails with a non-zero error (which includes time out) + DWORD r = WaitForSingleObject(ovlp.hEvent, 2000 * 100); + if (r == WAIT_TIMEOUT) + { + cemuLog_logDebug(LogType::Force, "HidRead internal timeout (error 0x{:08x})", GetLastError()); + // return -108 in case of timeout + free(tempBuffer); + CloseHandle(ovlp.hEvent); + return ReadResult::ErrorTimeout; + } + + cemuLog_logDebug(LogType::Force, "HidRead WaitHalfComplete"); + GetOverlappedResult(this->m_hFile, &ovlp, &bt, false); + if (bt == 0) + transferLength = 0; + else + transferLength = bt - 1; + cemuLog_logDebug(LogType::Force, "HidRead WaitComplete Length: 0x{:08x}", transferLength); + } + sint32 returnCode = 0; + ReadResult result = ReadResult::Success; + if (bt != 0) + { + memcpy(data, tempBuffer + 1, transferLength); + sint32 hidReadLength = transferLength; + + char debugOutput[1024] = {0}; + for (sint32 i = 0; i < transferLength; i++) + { + sprintf(debugOutput + i * 3, "%02x ", tempBuffer[1 + i]); + } + cemuLog_logDebug(LogType::Force, "HIDRead data: {}", debugOutput); + + bytesRead = transferLength; + result = ReadResult::Success; + } + else + { + cemuLog_log(LogType::Force, "Failed HID read"); + result = ReadResult::Error; + } + free(tempBuffer); + CloseHandle(ovlp.hEvent); + return result; + } + + Device::WriteResult DeviceWindowsHID::Write(uint8* data, sint32 length, sint32& bytesWritten) + { + bytesWritten = 0; + DWORD bt; + OVERLAPPED ovlp = {0}; + ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + uint8* tempBuffer = (uint8*)malloc(length + 1); + memcpy(tempBuffer + 1, data, length); + tempBuffer[0] = 0; // report byte? + + cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", length); + BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp); + if (writeResult != FALSE) + { + // sometimes we get the result immediately + cemuLog_logDebug(LogType::Force, "HidWrite Result received immediately (error 0x{:08x}) Length 0x{:08x}", + GetLastError()); + } + else + { + // wait for result + cemuLog_logDebug(LogType::Force, "HidWrite WaitForResult (error 0x{:08x})", GetLastError()); + // todo - check for error type + DWORD r = WaitForSingleObject(ovlp.hEvent, 2000); + if (r == WAIT_TIMEOUT) + { + cemuLog_logDebug(LogType::Force, "HidWrite internal timeout"); + // return -108 in case of timeout + free(tempBuffer); + CloseHandle(ovlp.hEvent); + return WriteResult::ErrorTimeout; + } + + cemuLog_logDebug(LogType::Force, "HidWrite WaitHalfComplete"); + GetOverlappedResult(this->m_hFile, &ovlp, &bt, false); + cemuLog_logDebug(LogType::Force, "HidWrite WaitComplete"); + } + + free(tempBuffer); + CloseHandle(ovlp.hEvent); + + if (bt != 0) + { + bytesWritten = length; + return WriteResult::Success; + } + return WriteResult::Error; + } + + bool DeviceWindowsHID::GetDescriptor(uint8 descType, + uint8 descIndex, + uint8 lang, + uint8* output, + uint32 outputMaxLength) + { + if (!IsOpened()) + { + cemuLog_logDebug(LogType::Force, "nsyshid::DeviceWindowsHID::getDescriptor(): device is not opened"); + return false; + } + if (descType == 0x02) + { + uint8 configurationDescriptor[0x29]; + + uint8* currentWritePtr; + + // configuration descriptor + currentWritePtr = configurationDescriptor + 0; + *(uint8*)(currentWritePtr + 0) = 9; // bLength + *(uint8*)(currentWritePtr + 1) = 2; // bDescriptorType + *(uint16be*)(currentWritePtr + 2) = 0x0029; // wTotalLength + *(uint8*)(currentWritePtr + 4) = 1; // bNumInterfaces + *(uint8*)(currentWritePtr + 5) = 1; // bConfigurationValue + *(uint8*)(currentWritePtr + 6) = 0; // iConfiguration + *(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes + *(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower + currentWritePtr = currentWritePtr + 9; + // configuration descriptor + *(uint8*)(currentWritePtr + 0) = 9; // bLength + *(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType + *(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber + *(uint8*)(currentWritePtr + 3) = 0; // bAlternateSetting + *(uint8*)(currentWritePtr + 4) = 2; // bNumEndpoints + *(uint8*)(currentWritePtr + 5) = 3; // bInterfaceClass + *(uint8*)(currentWritePtr + 6) = 0; // bInterfaceSubClass + *(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol + *(uint8*)(currentWritePtr + 8) = 0; // iInterface + currentWritePtr = currentWritePtr + 9; + // configuration descriptor + *(uint8*)(currentWritePtr + 0) = 9; // bLength + *(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType + *(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID + *(uint8*)(currentWritePtr + 4) = 0x00; // bCountryCode + *(uint8*)(currentWritePtr + 5) = 0x01; // bNumDescriptors + *(uint8*)(currentWritePtr + 6) = 0x22; // bDescriptorType + *(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength + currentWritePtr = currentWritePtr + 9; + // endpoint descriptor 1 + *(uint8*)(currentWritePtr + 0) = 7; // bLength + *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType + *(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress + *(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes + *(uint16be*)(currentWritePtr + 4) = + this->m_maxPacketSizeRX; // wMaxPacketSize + *(uint8*)(currentWritePtr + 6) = 0x01; // bInterval + currentWritePtr = currentWritePtr + 7; + // endpoint descriptor 2 + *(uint8*)(currentWritePtr + 0) = 7; // bLength + *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType + *(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress + *(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes + *(uint16be*)(currentWritePtr + 4) = + this->m_maxPacketSizeTX; // wMaxPacketSize + *(uint8*)(currentWritePtr + 6) = 0x01; // bInterval + currentWritePtr = currentWritePtr + 7; + + cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29); + + memcpy(output, configurationDescriptor, + std::min(outputMaxLength, sizeof(configurationDescriptor))); + return true; + } + else + { + cemu_assert_unimplemented(); + } + return false; + } + + bool DeviceWindowsHID::SetProtocol(uint32 ifIndef, uint32 protocol) + { + // ToDo: implement this + // pretend that everything is fine + return true; + } + + bool DeviceWindowsHID::SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) + { + sint32 retryCount = 0; + while (true) + { + BOOL r = HidD_SetOutputReport(this->m_hFile, reportData, length); + if (r != FALSE) + break; + Sleep(20); // retry + retryCount++; + if (retryCount >= 50) + { + cemuLog_log(LogType::Force, "nsyshid::DeviceWindowsHID::SetReport(): HID SetReport failed"); + return false; + } + } + return true; + } + + HANDLE OpenDevice(wchar_t* devicePath) + { + return CreateFile(devicePath, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + } + + void _debugPrintHex(std::string prefix, uint8* data, size_t len) + { + char debugOutput[1024] = {0}; + len = std::min(len, (size_t)100); + for (sint32 i = 0; i < len; i++) + { + sprintf(debugOutput + i * 3, "%02x ", data[i]); + } + fmt::print("{} Data: {}\n", prefix, debugOutput); + cemuLog_logDebug(LogType::Force, "[{}] Data: {}", prefix, debugOutput); + } +} // namespace nsyshid::backend::windows + +#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID diff --git a/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h b/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h new file mode 100644 index 00000000..049b33e4 --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h @@ -0,0 +1,66 @@ +#ifndef CEMU_NSYSHID_BACKEND_WINDOWS_HID_H +#define CEMU_NSYSHID_BACKEND_WINDOWS_HID_H + +#include "nsyshid.h" + +#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID + +#include "Backend.h" + +namespace nsyshid::backend::windows +{ + class BackendWindowsHID : public nsyshid::Backend { + public: + BackendWindowsHID(); + + ~BackendWindowsHID(); + + bool IsInitialisedOk() override; + + protected: + void AttachVisibleDevices() override; + + private: + std::shared_ptr CheckAndCreateDevice(wchar_t* devicePath, HANDLE hDevice); + }; + + class DeviceWindowsHID : public nsyshid::Device { + public: + DeviceWindowsHID(uint16 vendorId, + uint16 productId, + uint8 interfaceIndex, + uint8 interfaceSubClass, + uint8 protocol, + wchar_t* devicePath); + + ~DeviceWindowsHID(); + + bool Open() override; + + void Close() override; + + bool IsOpened() override; + + ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) override; + + WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) override; + + bool GetDescriptor(uint8 descType, uint8 descIndex, uint8 lang, uint8* output, uint32 outputMaxLength) override; + + bool SetProtocol(uint32 ifIndef, uint32 protocol) override; + + bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override; + + private: + wchar_t* m_devicePath; + HANDLE m_hFile; + }; + + HANDLE OpenDevice(wchar_t* devicePath); + + void _debugPrintHex(std::string prefix, uint8* data, size_t len); +} // namespace nsyshid::backend::windows + +#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID + +#endif // CEMU_NSYSHID_BACKEND_WINDOWS_HID_H diff --git a/src/Cafe/OS/libs/nsyshid/Whitelist.cpp b/src/Cafe/OS/libs/nsyshid/Whitelist.cpp new file mode 100644 index 00000000..f20e4c45 --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/Whitelist.cpp @@ -0,0 +1,53 @@ +#include "Whitelist.h" + +namespace nsyshid +{ + Whitelist& Whitelist::GetInstance() + { + static Whitelist whitelist; + return whitelist; + } + + Whitelist::Whitelist() + { + // add known devices + { + // lego dimensions portal + m_devices.emplace_back(0x0e6f, 0x0241); + // skylanders portal + m_devices.emplace_back(0x1430, 0x0150); + // disney infinity base + m_devices.emplace_back(0x0e6f, 0x0129); + } + } + + bool Whitelist::IsDeviceWhitelisted(uint16 vendorId, uint16 productId) + { + auto it = std::find(m_devices.begin(), m_devices.end(), + std::tuple(vendorId, productId)); + return it != m_devices.end(); + } + + void Whitelist::AddDevice(uint16 vendorId, uint16 productId) + { + if (!IsDeviceWhitelisted(vendorId, productId)) + { + m_devices.emplace_back(vendorId, productId); + } + } + + void Whitelist::RemoveDevice(uint16 vendorId, uint16 productId) + { + m_devices.remove(std::tuple(vendorId, productId)); + } + + std::list> Whitelist::GetDevices() + { + return m_devices; + } + + void Whitelist::RemoveAllDevices() + { + m_devices.clear(); + } +} // namespace nsyshid diff --git a/src/Cafe/OS/libs/nsyshid/Whitelist.h b/src/Cafe/OS/libs/nsyshid/Whitelist.h new file mode 100644 index 00000000..73b7742b --- /dev/null +++ b/src/Cafe/OS/libs/nsyshid/Whitelist.h @@ -0,0 +1,32 @@ +#ifndef CEMU_NSYSHID_WHITELIST_H +#define CEMU_NSYSHID_WHITELIST_H + +namespace nsyshid +{ + class Whitelist { + public: + static Whitelist& GetInstance(); + + Whitelist(const Whitelist&) = delete; + + Whitelist& operator=(const Whitelist&) = delete; + + bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId); + + void AddDevice(uint16 vendorId, uint16 productId); + + void RemoveDevice(uint16 vendorId, uint16 productId); + + std::list> GetDevices(); + + void RemoveAllDevices(); + + private: + Whitelist(); + + // vendorId, productId + std::list> m_devices; + }; +} // namespace nsyshid + +#endif // CEMU_NSYSHID_WHITELIST_H diff --git a/src/Cafe/OS/libs/nsyshid/nsyshid.cpp b/src/Cafe/OS/libs/nsyshid/nsyshid.cpp index 9b9d2d61..b21e2a43 100644 --- a/src/Cafe/OS/libs/nsyshid/nsyshid.cpp +++ b/src/Cafe/OS/libs/nsyshid/nsyshid.cpp @@ -1,289 +1,259 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/HW/Espresso/PPCCallback.h" #include +#include #include "nsyshid.h" - -#if BOOST_OS_WINDOWS - -#include -#include -#include - #include "Cafe/OS/libs/coreinit/coreinit_Thread.h" - -#pragma comment(lib,"Setupapi.lib") -#pragma comment(lib,"hid.lib") - -DEFINE_GUID(GUID_DEVINTERFACE_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30); +#include "Backend.h" +#include "Whitelist.h" namespace nsyshid { - typedef struct - { - /* +0x00 */ uint32be handle; - /* +0x04 */ uint32 ukn04; - /* +0x08 */ uint16 vendorId; // little-endian ? - /* +0x0A */ uint16 productId; // little-endian ? - /* +0x0C */ uint8 ifIndex; - /* +0x0D */ uint8 subClass; - /* +0x0E */ uint8 protocol; - /* +0x0F */ uint8 paddingGuessed0F; - /* +0x10 */ uint16be maxPacketSizeRX; - /* +0x12 */ uint16be maxPacketSizeTX; - }HIDDevice_t; - - static_assert(offsetof(HIDDevice_t, vendorId) == 0x8, ""); - static_assert(offsetof(HIDDevice_t, productId) == 0xA, ""); - static_assert(offsetof(HIDDevice_t, ifIndex) == 0xC, ""); - static_assert(offsetof(HIDDevice_t, protocol) == 0xE, ""); - - typedef struct _HIDDeviceInfo_t - { - uint32 handle; - uint32 physicalDeviceInstance; - uint16 vendorId; - uint16 productId; - uint8 interfaceIndex; - uint8 interfaceSubClass; - uint8 protocol; - HIDDevice_t* hidDevice; // this info is passed to applications and must remain intact - wchar_t* devicePath; - _HIDDeviceInfo_t* next; - // host - HANDLE hFile; - }HIDDeviceInfo_t; - - HIDDeviceInfo_t* firstDevice = nullptr; + std::list> backendList; + std::list> deviceList; typedef struct _HIDClient_t { - MEMPTR<_HIDClient_t> next; uint32be callbackFunc; // attach/detach callback - }HIDClient_t; + } HIDClient_t; - HIDClient_t* firstHIDClient = nullptr; + std::list HIDClientList; - HANDLE openDevice(wchar_t* devicePath) - { - return CreateFile(devicePath, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | - FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - NULL); - } + std::recursive_mutex hidMutex; - void attachClientToList(HIDClient_t* hidClient) + void AttachClientToList(HIDClient_t* hidClient) { + std::lock_guard lock(hidMutex); // todo - append at the beginning or end of the list? List order matters because it also controls the order in which attach callbacks are called - if (firstHIDClient) - { - hidClient->next = firstHIDClient; - firstHIDClient = hidClient; - } - else - { - hidClient->next = nullptr; - firstHIDClient = hidClient; - } + HIDClientList.push_front(hidClient); } - void attachDeviceToList(HIDDeviceInfo_t* hidDeviceInfo) + void DetachClientFromList(HIDClient_t* hidClient) { - if (firstDevice) - { - hidDeviceInfo->next = firstDevice; - firstDevice = hidDeviceInfo; - } - else - { - hidDeviceInfo->next = nullptr; - firstDevice = hidDeviceInfo; - } + std::lock_guard lock(hidMutex); + HIDClientList.remove(hidClient); } - HIDDeviceInfo_t* getHIDDeviceInfoByHandle(uint32 handle, bool openFileHandle = false) + std::shared_ptr GetDeviceByHandle(uint32 handle, bool openIfClosed = false) { - HIDDeviceInfo_t* deviceItr = firstDevice; - while (deviceItr) + std::shared_ptr device; { - if (deviceItr->handle == handle) + std::lock_guard lock(hidMutex); + for (const auto& d : deviceList) { - if (openFileHandle && deviceItr->hFile == INVALID_HANDLE_VALUE) + if (d->m_hid->handle == handle) { - deviceItr->hFile = openDevice(deviceItr->devicePath); - if (deviceItr->hFile == INVALID_HANDLE_VALUE) - { - cemuLog_log(LogType::Force, "HID: Failed to open device \"{}\"", boost::nowide::narrow(std::wstring(deviceItr->devicePath))); - return nullptr; - } - HidD_SetNumInputBuffers(deviceItr->hFile, 2); // dont cache too many reports + device = d; + break; } - return deviceItr; } - deviceItr = deviceItr->next; + } + if (device != nullptr) + { + if (openIfClosed && !device->IsOpened()) + { + if (!device->Open()) + { + return nullptr; + } + } + return device; } return nullptr; } uint32 _lastGeneratedHidHandle = 1; - uint32 generateHIDHandle() + uint32 GenerateHIDHandle() { + std::lock_guard lock(hidMutex); _lastGeneratedHidHandle++; return _lastGeneratedHidHandle; } const int HID_MAX_NUM_DEVICES = 128; - SysAllocator _devicePool; - std::bitset _devicePoolMask; + SysAllocator HIDPool; + std::queue HIDPoolIndexQueue; - HIDDevice_t* getFreeDevice() + void InitHIDPoolIndexQueue() { - for (sint32 i = 0; i < HID_MAX_NUM_DEVICES; i++) + static bool HIDPoolIndexQueueInitialized = false; + std::lock_guard lock(hidMutex); + if (HIDPoolIndexQueueInitialized) { - if (_devicePoolMask.test(i) == false) - { - _devicePoolMask.set(i); - return _devicePool.GetPtr() + i; - } + return; + } + HIDPoolIndexQueueInitialized = true; + for (size_t i = 0; i < HID_MAX_NUM_DEVICES; i++) + { + HIDPoolIndexQueue.push(i); } - return nullptr; } - void checkAndAddDevice(wchar_t* devicePath, HANDLE hDevice) + HID_t* GetFreeHID() { - HIDD_ATTRIBUTES hidAttr; - hidAttr.Size = sizeof(HIDD_ATTRIBUTES); - if (HidD_GetAttributes(hDevice, &hidAttr) == FALSE) - return; - HIDDevice_t* hidDevice = getFreeDevice(); - if (hidDevice == nullptr) + std::lock_guard lock(hidMutex); + InitHIDPoolIndexQueue(); + if (HIDPoolIndexQueue.empty()) { - cemuLog_log(LogType::Force, "HID: Maximum number of supported devices exceeded"); - return; + return nullptr; } - - HIDDeviceInfo_t* deviceInfo = (HIDDeviceInfo_t*)malloc(sizeof(HIDDeviceInfo_t)); - memset(deviceInfo, 0, sizeof(HIDDeviceInfo_t)); - deviceInfo->devicePath = _wcsdup(devicePath); - deviceInfo->vendorId = hidAttr.VendorID; - deviceInfo->productId = hidAttr.ProductID; - deviceInfo->hFile = INVALID_HANDLE_VALUE; - // generate handle - deviceInfo->handle = generateHIDHandle(); - // get additional device info - sint32 maxPacketInputLength = -1; - sint32 maxPacketOutputLength = -1; - PHIDP_PREPARSED_DATA ppData = nullptr; - if (HidD_GetPreparsedData(hDevice, &ppData)) - { - HIDP_CAPS caps; - if (HidP_GetCaps(ppData, &caps) == HIDP_STATUS_SUCCESS) - { - // length includes the report id byte - maxPacketInputLength = caps.InputReportByteLength - 1; - maxPacketOutputLength = caps.OutputReportByteLength - 1; - } - HidD_FreePreparsedData(ppData); - } - if (maxPacketInputLength <= 0 || maxPacketInputLength >= 0xF000) - { - cemuLog_log(LogType::Force, "HID: Input packet length not available or out of range (length = {})", maxPacketInputLength); - maxPacketInputLength = 0x20; - } - if (maxPacketOutputLength <= 0 || maxPacketOutputLength >= 0xF000) - { - cemuLog_log(LogType::Force, "HID: Output packet length not available or out of range (length = {})", maxPacketOutputLength); - maxPacketOutputLength = 0x20; - } - // setup HIDDevice struct - deviceInfo->hidDevice = hidDevice; - memset(hidDevice, 0, sizeof(HIDDevice_t)); - hidDevice->handle = deviceInfo->handle; - hidDevice->vendorId = deviceInfo->vendorId; - hidDevice->productId = deviceInfo->productId; - hidDevice->maxPacketSizeRX = maxPacketInputLength; - hidDevice->maxPacketSizeTX = maxPacketOutputLength; - - hidDevice->ukn04 = 0x11223344; - - hidDevice->ifIndex = 1; - hidDevice->protocol = 0; - hidDevice->subClass = 2; - - // todo - other values - //hidDevice->ifIndex = 1; - - - attachDeviceToList(deviceInfo); - + size_t index = HIDPoolIndexQueue.front(); + HIDPoolIndexQueue.pop(); + return HIDPool.GetPtr() + index; } - void initDeviceList() + void ReleaseHID(HID_t* device) { - if (firstDevice) - return; - HDEVINFO hDevInfo; - SP_DEVICE_INTERFACE_DATA DevIntfData; - PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData; - SP_DEVINFO_DATA DevData; - - DWORD dwSize, dwMemberIdx; - - hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - - if (hDevInfo != INVALID_HANDLE_VALUE) + // this should never happen, but having a safeguard can't hurt + if (device == nullptr) { - DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - dwMemberIdx = 0; - - SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_HID, - dwMemberIdx, &DevIntfData); - - while (GetLastError() != ERROR_NO_MORE_ITEMS) - { - DevData.cbSize = sizeof(DevData); - SetupDiGetDeviceInterfaceDetail( - hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL); - - DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize); - DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, - DevIntfDetailData, dwSize, &dwSize, &DevData)) - { - HANDLE hHIDDevice = openDevice(DevIntfDetailData->DevicePath); - if (hHIDDevice != INVALID_HANDLE_VALUE) - { - checkAndAddDevice(DevIntfDetailData->DevicePath, hHIDDevice); - CloseHandle(hHIDDevice); - } - } - HeapFree(GetProcessHeap(), 0, DevIntfDetailData); - // next - SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_HID, ++dwMemberIdx, &DevIntfData); - } - SetupDiDestroyDeviceInfoList(hDevInfo); + cemu_assert_error(); } + std::lock_guard lock(hidMutex); + InitHIDPoolIndexQueue(); + size_t index = device - HIDPool.GetPtr(); + HIDPoolIndexQueue.push(index); } const int HID_CALLBACK_DETACH = 0; const int HID_CALLBACK_ATTACH = 1; - uint32 doAttachCallback(HIDClient_t* hidClient, HIDDeviceInfo_t* deviceInfo) + uint32 DoAttachCallback(HIDClient_t* hidClient, const std::shared_ptr& device) { - return PPCCoreCallback(hidClient->callbackFunc, memory_getVirtualOffsetFromPointer(hidClient), memory_getVirtualOffsetFromPointer(deviceInfo->hidDevice), HID_CALLBACK_ATTACH); + return PPCCoreCallback(hidClient->callbackFunc, memory_getVirtualOffsetFromPointer(hidClient), + memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_ATTACH); } - void doDetachCallback(HIDClient_t* hidClient, HIDDeviceInfo_t* deviceInfo) + void DoAttachCallbackAsync(HIDClient_t* hidClient, const std::shared_ptr& device) { - PPCCoreCallback(hidClient->callbackFunc, memory_getVirtualOffsetFromPointer(hidClient), memory_getVirtualOffsetFromPointer(deviceInfo->hidDevice), HID_CALLBACK_DETACH); + coreinitAsyncCallback_add(hidClient->callbackFunc, 3, memory_getVirtualOffsetFromPointer(hidClient), + memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_ATTACH); + } + + void DoDetachCallback(HIDClient_t* hidClient, const std::shared_ptr& device) + { + PPCCoreCallback(hidClient->callbackFunc, memory_getVirtualOffsetFromPointer(hidClient), + memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_DETACH); + } + + void DoDetachCallbackAsync(HIDClient_t* hidClient, const std::shared_ptr& device) + { + coreinitAsyncCallback_add(hidClient->callbackFunc, 3, memory_getVirtualOffsetFromPointer(hidClient), + memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_DETACH); + } + + void AttachBackend(const std::shared_ptr& backend) + { + { + std::lock_guard lock(hidMutex); + backendList.push_back(backend); + } + backend->OnAttach(); + } + + void DetachBackend(const std::shared_ptr& backend) + { + { + std::lock_guard lock(hidMutex); + backendList.remove(backend); + } + backend->OnDetach(); + } + + void DetachAllBackends() + { + std::list> backendListCopy; + { + std::lock_guard lock(hidMutex); + backendListCopy = backendList; + backendList.clear(); + } + for (const auto& backend : backendListCopy) + { + backend->OnDetach(); + } + } + + void AttachDefaultBackends() + { + backend::AttachDefaultBackends(); + } + + bool AttachDevice(const std::shared_ptr& device) + { + std::lock_guard lock(hidMutex); + + // is the device already attached? + { + auto it = std::find(deviceList.begin(), deviceList.end(), device); + if (it != deviceList.end()) + { + cemuLog_logDebug(LogType::Force, + "nsyshid.AttachDevice(): failed to attach device: {:04x}:{:04x}: already attached", + device->m_vendorId, + device->m_productId); + return false; + } + } + + HID_t* hidDevice = GetFreeHID(); + if (hidDevice == nullptr) + { + cemuLog_logDebug(LogType::Force, + "nsyshid.AttachDevice(): failed to attach device: {:04x}:{:04x}: no free device slots left", + device->m_vendorId, + device->m_productId); + return false; + } + hidDevice->handle = GenerateHIDHandle(); + device->AssignHID(hidDevice); + deviceList.push_back(device); + + // do attach callbacks + for (auto client : HIDClientList) + { + DoAttachCallbackAsync(client, device); + } + + cemuLog_logDebug(LogType::Force, "nsyshid.AttachDevice(): device attached: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + return true; + } + + void DetachDevice(const std::shared_ptr& device) + { + { + std::lock_guard lock(hidMutex); + + // remove from list + auto it = std::find(deviceList.begin(), deviceList.end(), device); + if (it == deviceList.end()) + { + cemuLog_logDebug(LogType::Force, "nsyshid.DetachDevice(): device not found: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); + return; + } + deviceList.erase(it); + + // do detach callbacks + for (auto client : HIDClientList) + { + DoDetachCallbackAsync(client, device); + } + ReleaseHID(device->m_hid); + } + + device->Close(); + + cemuLog_logDebug(LogType::Force, "nsyshid.DetachDevice(): device removed: {:04x}:{:04x}", + device->m_vendorId, + device->m_productId); } void export_HIDAddClient(PPCInterpreter_t* hCPU) @@ -292,15 +262,14 @@ namespace nsyshid ppcDefineParamMPTR(callbackFuncMPTR, 1); cemuLog_logDebug(LogType::Force, "nsyshid.HIDAddClient(0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4]); hidClient->callbackFunc = callbackFuncMPTR; - attachClientToList(hidClient); - initDeviceList(); + + std::lock_guard lock(hidMutex); + AttachClientToList(hidClient); + // do attach callbacks - HIDDeviceInfo_t* deviceItr = firstDevice; - while (deviceItr) + for (const auto& device : deviceList) { - if (doAttachCallback(hidClient, deviceItr) != 0) - break; - deviceItr = deviceItr->next; + DoAttachCallback(hidClient, device); } osLib_returnFromFunction(hCPU, 0); @@ -310,14 +279,14 @@ namespace nsyshid { ppcDefineParamTypePtr(hidClient, HIDClient_t, 0); cemuLog_logDebug(LogType::Force, "nsyshid.HIDDelClient(0x{:08x})", hCPU->gpr[3]); - - // todo + + std::lock_guard lock(hidMutex); + DetachClientFromList(hidClient); + // do detach callbacks - HIDDeviceInfo_t* deviceItr = firstDevice; - while (deviceItr) + for (const auto& device : deviceList) { - doDetachCallback(hidClient, deviceItr); - deviceItr = deviceItr->next; + DoDetachCallback(hidClient, device); } osLib_returnFromFunction(hCPU, 0); @@ -325,127 +294,68 @@ namespace nsyshid void export_HIDGetDescriptor(PPCInterpreter_t* hCPU) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU8(descType, 1); // r4 - ppcDefineParamU8(descIndex, 2); // r5 - ppcDefineParamU8(lang, 3); // r6 - ppcDefineParamUStr(output, 4); // r7 + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamU8(descType, 1); // r4 + ppcDefineParamU8(descIndex, 2); // r5 + ppcDefineParamU8(lang, 3); // r6 + ppcDefineParamUStr(output, 4); // r7 ppcDefineParamU32(outputMaxLength, 5); // r8 - ppcDefineParamMPTR(cbFuncMPTR, 6); // r9 - ppcDefineParamMPTR(cbParamMPTR, 7); // r10 + ppcDefineParamMPTR(cbFuncMPTR, 6); // r9 + ppcDefineParamMPTR(cbParamMPTR, 7); // r10 - HIDDeviceInfo_t* hidDeviceInfo = getHIDDeviceInfoByHandle(hidHandle); - if (hidDeviceInfo) + int returnValue = -1; + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + if (device) { - HANDLE hHIDDevice = openDevice(hidDeviceInfo->devicePath); - if (hHIDDevice != INVALID_HANDLE_VALUE) + memset(output, 0, outputMaxLength); + if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength)) { - if (descType == 0x02) - { - uint8 configurationDescriptor[0x29]; - - uint8* currentWritePtr; - - // configuration descriptor - currentWritePtr = configurationDescriptor + 0; - *(uint8*)(currentWritePtr + 0) = 9; // bLength - *(uint8*)(currentWritePtr + 1) = 2; // bDescriptorType - *(uint16be*)(currentWritePtr + 2) = 0x0029; // wTotalLength - *(uint8*)(currentWritePtr + 4) = 1; // bNumInterfaces - *(uint8*)(currentWritePtr + 5) = 1; // bConfigurationValue - *(uint8*)(currentWritePtr + 6) = 0; // iConfiguration - *(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes - *(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower - currentWritePtr = currentWritePtr + 9; - // configuration descriptor - *(uint8*)(currentWritePtr + 0) = 9; // bLength - *(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType - *(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber - *(uint8*)(currentWritePtr + 3) = 0; // bAlternateSetting - *(uint8*)(currentWritePtr + 4) = 2; // bNumEndpoints - *(uint8*)(currentWritePtr + 5) = 3; // bInterfaceClass - *(uint8*)(currentWritePtr + 6) = 0; // bInterfaceSubClass - *(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol - *(uint8*)(currentWritePtr + 8) = 0; // iInterface - currentWritePtr = currentWritePtr + 9; - // configuration descriptor - *(uint8*)(currentWritePtr + 0) = 9; // bLength - *(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType - *(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID - *(uint8*)(currentWritePtr + 4) = 0x00; // bCountryCode - *(uint8*)(currentWritePtr + 5) = 0x01; // bNumDescriptors - *(uint8*)(currentWritePtr + 6) = 0x22; // bDescriptorType - *(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength - currentWritePtr = currentWritePtr + 9; - // endpoint descriptor 1 - *(uint8*)(currentWritePtr + 0) = 7; // bLength - *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType - *(uint8*)(currentWritePtr + 1) = 0x81; // bEndpointAddress - *(uint8*)(currentWritePtr + 2) = 0x03; // bmAttributes - *(uint16be*)(currentWritePtr + 3) = 0x40; // wMaxPacketSize - *(uint8*)(currentWritePtr + 5) = 0x01; // bInterval - currentWritePtr = currentWritePtr + 7; - // endpoint descriptor 2 - *(uint8*)(currentWritePtr + 0) = 7; // bLength - *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType - *(uint8*)(currentWritePtr + 1) = 0x02; // bEndpointAddress - *(uint8*)(currentWritePtr + 2) = 0x03; // bmAttributes - *(uint16be*)(currentWritePtr + 3) = 0x40; // wMaxPacketSize - *(uint8*)(currentWritePtr + 5) = 0x01; // bInterval - currentWritePtr = currentWritePtr + 7; - - cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29); - - memcpy(output, configurationDescriptor, std::min(outputMaxLength, sizeof(configurationDescriptor))); - } - else - { - cemu_assert_unimplemented(); - } - CloseHandle(hHIDDevice); + returnValue = 0; } else { - cemu_assert_unimplemented(); + returnValue = -1; } } else { cemu_assert_suspicious(); } - osLib_returnFromFunction(hCPU, 0); + osLib_returnFromFunction(hCPU, returnValue); } void _debugPrintHex(std::string prefix, uint8* data, size_t len) { - char debugOutput[1024] = { 0 }; + char debugOutput[1024] = {0}; len = std::min(len, (size_t)100); for (sint32 i = 0; i < len; i++) { sprintf(debugOutput + i * 3, "%02x ", data[i]); } + fmt::print("{} Data: {}\n", prefix, debugOutput); cemuLog_logDebug(LogType::Force, "[{}] Data: {}", prefix, debugOutput); } - void doHIDTransferCallback(MPTR callbackFuncMPTR, MPTR callbackParamMPTR, uint32 hidHandle, uint32 errorCode, MPTR buffer, sint32 length) + void DoHIDTransferCallback(MPTR callbackFuncMPTR, MPTR callbackParamMPTR, uint32 hidHandle, uint32 errorCode, + MPTR buffer, sint32 length) { coreinitAsyncCallback_add(callbackFuncMPTR, 5, hidHandle, errorCode, buffer, length, callbackParamMPTR); } void export_HIDSetIdle(PPCInterpreter_t* hCPU) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU32(ifIndex, 1); // r4 - ppcDefineParamU32(ukn, 2); // r5 - ppcDefineParamU32(duration, 3); // r6 - ppcDefineParamMPTR(callbackFuncMPTR, 4); // r7 + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamU32(ifIndex, 1); // r4 + ppcDefineParamU32(ukn, 2); // r5 + ppcDefineParamU32(duration, 3); // r6 + ppcDefineParamMPTR(callbackFuncMPTR, 4); // r7 ppcDefineParamMPTR(callbackParamMPTR, 5); // r8 cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetIdle(...)"); // todo if (callbackFuncMPTR) { - doHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0); + DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0); } else { @@ -456,67 +366,78 @@ namespace nsyshid void export_HIDSetProtocol(PPCInterpreter_t* hCPU) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU32(ifIndex, 1); // r4 - ppcDefineParamU32(protocol, 2); // r5 - ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamU32(ifIndex, 1); // r4 + ppcDefineParamU32(protocol, 2); // r5 + ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 ppcDefineParamMPTR(callbackParamMPTR, 4); // r7 cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(...)"); - - if (callbackFuncMPTR) + + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + sint32 returnCode = -1; + if (device) { - doHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0); + if (!device->IsOpened()) + { + cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(): error: device is not opened"); + } + else + { + if (device->SetProtocol(ifIndex, protocol)) + { + returnCode = 0; + } + } } else { - cemu_assert_unimplemented(); + cemu_assert_suspicious(); } - osLib_returnFromFunction(hCPU, 0); // for non-async version, return number of bytes transferred + + if (callbackFuncMPTR) + { + DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0); + } + osLib_returnFromFunction(hCPU, returnCode); } // handler for async HIDSetReport transfers - void _hidSetReportAsync(HIDDeviceInfo_t* hidDeviceInfo, uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) + void _hidSetReportAsync(std::shared_ptr device, uint8* reportData, sint32 length, + uint8* originalData, + sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) { - sint32 retryCount = 0; - while (true) + cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin"); + if (device->SetReport(reportData, length, originalData, originalLength)) { - BOOL r = HidD_SetOutputReport(hidDeviceInfo->hFile, reportData, length); - if (r != FALSE) - break; - Sleep(20); // retry - retryCount++; - if (retryCount >= 40) - { - cemuLog_log(LogType::Force, "HID async SetReport failed"); - sint32 errorCode = -1; - doHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidDeviceInfo->handle, errorCode, memory_getVirtualOffsetFromPointer(originalData), 0); - free(reportData); - return; - } + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + 0, + memory_getVirtualOffsetFromPointer(originalData), + originalLength); + } + else + { + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + -1, + memory_getVirtualOffsetFromPointer(originalData), + 0); } - doHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidDeviceInfo->handle, 0, memory_getVirtualOffsetFromPointer(originalData), originalLength); free(reportData); } // handler for synchronous HIDSetReport transfers - sint32 _hidSetReportSync(HIDDeviceInfo_t* hidDeviceInfo, uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength, OSThread_t* osThread) + sint32 _hidSetReportSync(std::shared_ptr device, uint8* reportData, sint32 length, + uint8* originalData, + sint32 originalLength, OSThread_t* osThread) { - //cemuLog_logDebug(LogType::Force, "_hidSetReportSync begin"); _debugPrintHex("_hidSetReportSync Begin", reportData, length); - sint32 retryCount = 0; sint32 returnCode = 0; - while (true) + if (device->SetReport(reportData, length, originalData, originalLength)) { - BOOL r = HidD_SetOutputReport(hidDeviceInfo->hFile, reportData, length); - if (r != FALSE) - { - returnCode = originalLength; - break; - } - Sleep(100); // retry - retryCount++; - if (retryCount >= 10) - assert_dbg(); + returnCode = originalLength; } free(reportData); cemuLog_logDebug(LogType::Force, "_hidSetReportSync end. returnCode: {}", returnCode); @@ -526,14 +447,15 @@ namespace nsyshid void export_HIDSetReport(PPCInterpreter_t* hCPU) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU32(reportRelatedUkn, 1); // r4 - ppcDefineParamU32(reportId, 2); // r5 - ppcDefineParamUStr(data, 3); // r6 - ppcDefineParamU32(dataLength, 4); // r7 - ppcDefineParamMPTR(callbackFuncMPTR, 5); // r8 + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamU32(reportRelatedUkn, 1); // r4 + ppcDefineParamU32(reportId, 2); // r5 + ppcDefineParamUStr(data, 3); // r6 + ppcDefineParamU32(dataLength, 4); // r7 + ppcDefineParamMPTR(callbackFuncMPTR, 5); // r8 ppcDefineParamMPTR(callbackParamMPTR, 6); // r9 - cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetReport({},0x{:02x},0x{:02x},...)", hidHandle, reportRelatedUkn, reportId); + cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetReport({},0x{:02x},0x{:02x},...)", hidHandle, reportRelatedUkn, + reportId); _debugPrintHex("HIDSetReport", data, dataLength); @@ -542,8 +464,8 @@ namespace nsyshid assert_dbg(); #endif - HIDDeviceInfo_t* hidDeviceInfo = getHIDDeviceInfoByHandle(hidHandle, true); - if (hidDeviceInfo == nullptr) + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + if (device == nullptr) { cemuLog_log(LogType::Force, "nsyshid.HIDSetReport(): Unable to find device with hid handle {}", hidHandle); osLib_returnFromFunction(hCPU, -1); @@ -552,19 +474,20 @@ namespace nsyshid // prepare report data // note: Currently we need to pad the data to 0x20 bytes for it to work (plus one extra byte for HidD_SetOutputReport) - // Does IOSU pad data to 0x20 byte? Also check if this is specific to Skylanders portal - sint32 paddedLength = (dataLength +0x1F)&~0x1F; - uint8* reportData = (uint8*)malloc(paddedLength+1); - memset(reportData, 0, paddedLength+1); + // Does IOSU pad data to 0x20 byte? Also check if this is specific to Skylanders portal + sint32 paddedLength = (dataLength + 0x1F) & ~0x1F; + uint8* reportData = (uint8*)malloc(paddedLength + 1); + memset(reportData, 0, paddedLength + 1); reportData[0] = 0; memcpy(reportData + 1, data, dataLength); - // issue request (synchronous or asynchronous) sint32 returnCode = 0; if (callbackFuncMPTR == MPTR_NULL) { - std::future res = std::async(std::launch::async, &_hidSetReportSync, hidDeviceInfo, reportData, paddedLength + 1, data, dataLength, coreinitThread_getCurrentThreadDepr(hCPU)); + std::future res = std::async(std::launch::async, &_hidSetReportSync, device, reportData, + paddedLength + 1, data, dataLength, + coreinitThread_getCurrentThreadDepr(hCPU)); coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(hCPU), 1000); PPCCore_switchToScheduler(); returnCode = res.get(); @@ -572,110 +495,88 @@ namespace nsyshid else { // asynchronous - std::thread(&_hidSetReportAsync, hidDeviceInfo, reportData, paddedLength+1, data, dataLength, callbackFuncMPTR, callbackParamMPTR).detach(); + std::thread(&_hidSetReportAsync, device, reportData, paddedLength + 1, data, dataLength, + callbackFuncMPTR, callbackParamMPTR) + .detach(); returnCode = 0; } osLib_returnFromFunction(hCPU, returnCode); } - sint32 _hidReadInternalSync(HIDDeviceInfo_t* hidDeviceInfo, uint8* data, sint32 maxLength) + sint32 _hidReadInternalSync(std::shared_ptr device, uint8* data, sint32 maxLength) { - DWORD bt; - OVERLAPPED ovlp = { 0 }; - ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - uint8* tempBuffer = (uint8*)malloc(maxLength + 1); - sint32 transferLength = 0; // minus report byte - - _debugPrintHex("HID_READ_BEFORE", data, maxLength); - cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", maxLength); - BOOL readResult = ReadFile(hidDeviceInfo->hFile, tempBuffer, maxLength + 1, &bt, &ovlp); - if (readResult != FALSE) + if (!device->IsOpened()) { - // sometimes we get the result immediately - if (bt == 0) - transferLength = 0; - else - transferLength = bt - 1; - cemuLog_logDebug(LogType::Force, "HidRead Result received immediately (error 0x{:08x}) Length 0x{:08x}", GetLastError(), transferLength); + cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): cannot read from a non-opened device"); + return -1; } - else + memset(data, 0, maxLength); + + sint32 bytesRead = 0; + Device::ReadResult readResult = device->Read(data, maxLength, bytesRead); + switch (readResult) { - // wait for result - cemuLog_logDebug(LogType::Force, "HidRead WaitForResult (error 0x{:08x})", GetLastError()); - // async hid read is never supposed to return unless there is an response? Lego Dimensions stops HIDRead calls as soon as one of them fails with a non-zero error (which includes time out) - DWORD r = WaitForSingleObject(ovlp.hEvent, 2000*100); - if (r == WAIT_TIMEOUT) - { - cemuLog_logDebug(LogType::Force, "HidRead internal timeout (error 0x{:08x})", GetLastError()); - // return -108 in case of timeout - free(tempBuffer); - CloseHandle(ovlp.hEvent); - return -108; - } - - - cemuLog_logDebug(LogType::Force, "HidRead WaitHalfComplete"); - GetOverlappedResult(hidDeviceInfo->hFile, &ovlp, &bt, false); - if (bt == 0) - transferLength = 0; - else - transferLength = bt - 1; - cemuLog_logDebug(LogType::Force, "HidRead WaitComplete Length: 0x{:08x}", transferLength); - } - sint32 returnCode = 0; - if (bt != 0) + case Device::ReadResult::Success: { - memcpy(data, tempBuffer + 1, transferLength); - sint32 hidReadLength = transferLength; - - char debugOutput[1024] = { 0 }; - for (sint32 i = 0; i < transferLength; i++) - { - sprintf(debugOutput + i * 3, "%02x ", tempBuffer[1 + i]); - } - cemuLog_logDebug(LogType::Force, "HIDRead data: {}", debugOutput); - - returnCode = transferLength; + cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read {} of {} bytes", + bytesRead, + maxLength); + return bytesRead; } - else + break; + case Device::ReadResult::Error: { - cemuLog_log(LogType::Force, "Failed HID read"); - returnCode = -1; + cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read error"); + return -1; } - free(tempBuffer); - CloseHandle(ovlp.hEvent); - return returnCode; + break; + case Device::ReadResult::ErrorTimeout: + { + cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read error: timeout"); + return -108; + } + break; + } + cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read error: unknown"); + return -1; } - void _hidReadAsync(HIDDeviceInfo_t* hidDeviceInfo, uint8* data, sint32 maxLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) + void _hidReadAsync(std::shared_ptr device, + uint8* data, sint32 maxLength, + MPTR callbackFuncMPTR, + MPTR callbackParamMPTR) { - sint32 returnCode = _hidReadInternalSync(hidDeviceInfo, data, maxLength); + sint32 returnCode = _hidReadInternalSync(device, data, maxLength); sint32 errorCode = 0; if (returnCode < 0) - errorCode = returnCode; // dont return number of bytes in error code - doHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidDeviceInfo->handle, errorCode, memory_getVirtualOffsetFromPointer(data), (returnCode>0)?returnCode:0); + errorCode = returnCode; // don't return number of bytes in error code + DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, device->m_hid->handle, errorCode, + memory_getVirtualOffsetFromPointer(data), (returnCode > 0) ? returnCode : 0); } - sint32 _hidReadSync(HIDDeviceInfo_t* hidDeviceInfo, uint8* data, sint32 maxLength, OSThread_t* osThread) + sint32 _hidReadSync(std::shared_ptr device, + uint8* data, + sint32 maxLength, + OSThread_t* osThread) { - sint32 returnCode = _hidReadInternalSync(hidDeviceInfo, data, maxLength); + sint32 returnCode = _hidReadInternalSync(device, data, maxLength); coreinit_resumeThread(osThread, 1000); return returnCode; } void export_HIDRead(PPCInterpreter_t* hCPU) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamUStr(data, 1); // r4 - ppcDefineParamU32(maxLength, 2); // r5 - ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamUStr(data, 1); // r4 + ppcDefineParamU32(maxLength, 2); // r5 + ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 ppcDefineParamMPTR(callbackParamMPTR, 4); // r7 - cemuLog_logDebug(LogType::Force, "nsyshid.HIDRead(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); + cemuLog_logDebug(LogType::Force, "nsyshid.HIDRead(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); - HIDDeviceInfo_t* hidDeviceInfo = getHIDDeviceInfoByHandle(hidHandle, true); - if (hidDeviceInfo == nullptr) + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + if (device == nullptr) { cemuLog_log(LogType::Force, "nsyshid.HIDRead(): Unable to find device with hid handle {}", hidHandle); osLib_returnFromFunction(hCPU, -1); @@ -685,13 +586,14 @@ namespace nsyshid if (callbackFuncMPTR != MPTR_NULL) { // asynchronous transfer - std::thread(&_hidReadAsync, hidDeviceInfo, data, maxLength, callbackFuncMPTR, callbackParamMPTR).detach(); + std::thread(&_hidReadAsync, device, data, maxLength, callbackFuncMPTR, callbackParamMPTR).detach(); returnCode = 0; } else { // synchronous transfer - std::future res = std::async(std::launch::async, &_hidReadSync, hidDeviceInfo, data, maxLength, coreinitThread_getCurrentThreadDepr(hCPU)); + std::future res = std::async(std::launch::async, &_hidReadSync, device, data, maxLength, + coreinitThread_getCurrentThreadDepr(hCPU)); coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(hCPU), 1000); PPCCore_switchToScheduler(); returnCode = res.get(); @@ -700,81 +602,78 @@ namespace nsyshid osLib_returnFromFunction(hCPU, returnCode); } - sint32 _hidWriteInternalSync(HIDDeviceInfo_t* hidDeviceInfo, uint8* data, sint32 maxLength) + sint32 _hidWriteInternalSync(std::shared_ptr device, uint8* data, sint32 maxLength) { - DWORD bt; - OVERLAPPED ovlp = { 0 }; - ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - uint8* tempBuffer = (uint8*)malloc(maxLength + 1); - memcpy(tempBuffer + 1, data, maxLength); - tempBuffer[0] = 0; // report byte? - cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", maxLength); - BOOL WriteResult = WriteFile(hidDeviceInfo->hFile, tempBuffer, maxLength + 1, &bt, &ovlp); - if (WriteResult != FALSE) + if (!device->IsOpened()) { - // sometimes we get the result immediately - cemuLog_logDebug(LogType::Force, "HidWrite Result received immediately (error 0x{:08x}) Length 0x{:08x}", GetLastError()); + cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): cannot write to a non-opened device"); + return -1; } - else + sint32 bytesWritten = 0; + Device::WriteResult writeResult = device->Write(data, maxLength, bytesWritten); + switch (writeResult) { - // wait for result - cemuLog_logDebug(LogType::Force, "HidWrite WaitForResult (error 0x{:08x})", GetLastError()); - // todo - check for error type - DWORD r = WaitForSingleObject(ovlp.hEvent, 2000); - if (r == WAIT_TIMEOUT) - { - cemuLog_logDebug(LogType::Force, "HidWrite internal timeout"); - // return -108 in case of timeout - free(tempBuffer); - CloseHandle(ovlp.hEvent); - return -108; - } - - - cemuLog_logDebug(LogType::Force, "HidWrite WaitHalfComplete"); - GetOverlappedResult(hidDeviceInfo->hFile, &ovlp, &bt, false); - cemuLog_logDebug(LogType::Force, "HidWrite WaitComplete"); + case Device::WriteResult::Success: + { + cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): wrote {} of {} bytes", bytesWritten, + maxLength); + return bytesWritten; } - sint32 returnCode = 0; - if (bt != 0) - returnCode = maxLength; - else - returnCode = -1; - - free(tempBuffer); - CloseHandle(ovlp.hEvent); - return returnCode; + break; + case Device::WriteResult::Error: + { + cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): write error"); + return -1; + } + break; + case Device::WriteResult::ErrorTimeout: + { + cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): write error: timeout"); + return -108; + } + break; + } + cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): write error: unknown"); + return -1; } - void _hidWriteAsync(HIDDeviceInfo_t* hidDeviceInfo, uint8* data, sint32 maxLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) + void _hidWriteAsync(std::shared_ptr device, + uint8* data, + sint32 maxLength, + MPTR callbackFuncMPTR, + MPTR callbackParamMPTR) { - sint32 returnCode = _hidWriteInternalSync(hidDeviceInfo, data, maxLength); + sint32 returnCode = _hidWriteInternalSync(device, data, maxLength); sint32 errorCode = 0; if (returnCode < 0) - errorCode = returnCode; // dont return number of bytes in error code - doHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidDeviceInfo->handle, errorCode, memory_getVirtualOffsetFromPointer(data), (returnCode > 0) ? returnCode : 0); + errorCode = returnCode; // don't return number of bytes in error code + DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, device->m_hid->handle, errorCode, + memory_getVirtualOffsetFromPointer(data), (returnCode > 0) ? returnCode : 0); } - sint32 _hidWriteSync(HIDDeviceInfo_t* hidDeviceInfo, uint8* data, sint32 maxLength, OSThread_t* osThread) + sint32 _hidWriteSync(std::shared_ptr device, + uint8* data, + sint32 maxLength, + OSThread_t* osThread) { - sint32 returnCode = _hidWriteInternalSync(hidDeviceInfo, data, maxLength); + sint32 returnCode = _hidWriteInternalSync(device, data, maxLength); coreinit_resumeThread(osThread, 1000); return returnCode; } void export_HIDWrite(PPCInterpreter_t* hCPU) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamUStr(data, 1); // r4 - ppcDefineParamU32(maxLength, 2); // r5 - ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamUStr(data, 1); // r4 + ppcDefineParamU32(maxLength, 2); // r5 + ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 ppcDefineParamMPTR(callbackParamMPTR, 4); // r7 - cemuLog_logDebug(LogType::Force, "nsyshid.HIDWrite(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); + cemuLog_logDebug(LogType::Force, "nsyshid.HIDWrite(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); - HIDDeviceInfo_t* hidDeviceInfo = getHIDDeviceInfoByHandle(hidHandle, true); - if (hidDeviceInfo == nullptr) + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + if (device == nullptr) { cemuLog_log(LogType::Force, "nsyshid.HIDWrite(): Unable to find device with hid handle {}", hidHandle); osLib_returnFromFunction(hCPU, -1); @@ -784,13 +683,14 @@ namespace nsyshid if (callbackFuncMPTR != MPTR_NULL) { // asynchronous transfer - std::thread(&_hidWriteAsync, hidDeviceInfo, data, maxLength, callbackFuncMPTR, callbackParamMPTR).detach(); + std::thread(&_hidWriteAsync, device, data, maxLength, callbackFuncMPTR, callbackParamMPTR).detach(); returnCode = 0; } else { // synchronous transfer - std::future res = std::async(std::launch::async, &_hidWriteSync, hidDeviceInfo, data, maxLength, coreinitThread_getCurrentThreadDepr(hCPU)); + std::future res = std::async(std::launch::async, &_hidWriteSync, device, data, maxLength, + coreinitThread_getCurrentThreadDepr(hCPU)); coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(hCPU), 1000); PPCCore_switchToScheduler(); returnCode = res.get(); @@ -804,7 +704,8 @@ namespace nsyshid ppcDefineParamU32(errorCode, 0); ppcDefineParamTypePtr(ukn0, uint32be, 1); ppcDefineParamTypePtr(ukn1, uint32be, 2); - cemuLog_logDebug(LogType::Force, "nsyshid.HIDDecodeError(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); + cemuLog_logDebug(LogType::Force, "nsyshid.HIDDecodeError(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], + hCPU->gpr[4], hCPU->gpr[5]); // todo *ukn0 = 0x3FF; @@ -813,6 +714,114 @@ namespace nsyshid osLib_returnFromFunction(hCPU, 0); } + void Backend::DetachAllDevices() + { + std::lock_guard lock(this->m_devicesMutex); + if (m_isAttached) + { + for (const auto& device : this->m_devices) + { + nsyshid::DetachDevice(device); + } + this->m_devices.clear(); + } + } + + bool Backend::AttachDevice(const std::shared_ptr& device) + { + std::lock_guard lock(this->m_devicesMutex); + if (m_isAttached && nsyshid::AttachDevice(device)) + { + this->m_devices.push_back(device); + return true; + } + return false; + } + + void Backend::DetachDevice(const std::shared_ptr& device) + { + std::lock_guard lock(this->m_devicesMutex); + if (m_isAttached) + { + nsyshid::DetachDevice(device); + this->m_devices.remove(device); + } + } + + std::shared_ptr Backend::FindDevice(std::function&)> isWantedDevice) + { + std::lock_guard lock(this->m_devicesMutex); + auto it = std::find_if(this->m_devices.begin(), this->m_devices.end(), std::move(isWantedDevice)); + if (it != this->m_devices.end()) + { + return *it; + } + return nullptr; + } + + bool Backend::IsDeviceWhitelisted(uint16 vendorId, uint16 productId) + { + return Whitelist::GetInstance().IsDeviceWhitelisted(vendorId, productId); + } + + Backend::Backend() + : m_isAttached(false) + { + } + + void Backend::OnAttach() + { + std::lock_guard lock(this->m_devicesMutex); + m_isAttached = true; + AttachVisibleDevices(); + } + + void Backend::OnDetach() + { + std::lock_guard lock(this->m_devicesMutex); + DetachAllDevices(); + m_isAttached = false; + } + + bool Backend::IsBackendAttached() + { + std::lock_guard lock(this->m_devicesMutex); + return m_isAttached; + } + + Device::Device(uint16 vendorId, + uint16 productId, + uint8 interfaceIndex, + uint8 interfaceSubClass, + uint8 protocol) + : m_hid(nullptr), + m_vendorId(vendorId), + m_productId(productId), + m_interfaceIndex(interfaceIndex), + m_interfaceSubClass(interfaceSubClass), + m_protocol(protocol), + m_maxPacketSizeRX(0x20), + m_maxPacketSizeTX(0x20) + { + } + + void Device::AssignHID(HID_t* hid) + { + if (hid != nullptr) + { + hid->vendorId = this->m_vendorId; + hid->productId = this->m_productId; + hid->ifIndex = this->m_interfaceIndex; + hid->subClass = this->m_interfaceSubClass; + hid->protocol = this->m_protocol; + hid->ukn04 = 0x11223344; + hid->paddingGuessed0F = 0; + hid->maxPacketSizeRX = this->m_maxPacketSizeRX; + hid->maxPacketSizeTX = this->m_maxPacketSizeTX; + } + this->m_hid = hid; + } + void load() { osLib_addFunction("nsyshid", "HIDAddClient", export_HIDAddClient); @@ -826,19 +835,10 @@ namespace nsyshid osLib_addFunction("nsyshid", "HIDWrite", export_HIDWrite); osLib_addFunction("nsyshid", "HIDDecodeError", export_HIDDecodeError); - firstHIDClient = nullptr; + + // initialise whitelist + Whitelist::GetInstance(); + + AttachDefaultBackends(); } -} - -#else - -namespace nsyshid -{ - void load() - { - // unimplemented - }; -}; - - -#endif +} // namespace nsyshid diff --git a/src/Cafe/OS/libs/nsyshid/nsyshid.h b/src/Cafe/OS/libs/nsyshid/nsyshid.h index 051b4e7c..1478adf9 100644 --- a/src/Cafe/OS/libs/nsyshid/nsyshid.h +++ b/src/Cafe/OS/libs/nsyshid/nsyshid.h @@ -1,5 +1,12 @@ #pragma once + namespace nsyshid { + class Backend; + + void AttachBackend(const std::shared_ptr& backend); + + void DetachBackend(const std::shared_ptr& backend); + void load(); -} \ No newline at end of file +} // namespace nsyshid diff --git a/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp b/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp index 546a9615..09d73013 100644 --- a/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp +++ b/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp @@ -3,6 +3,9 @@ #include #include +#pragma comment(lib, "Setupapi.lib") +#pragma comment(lib, "hid.lib") + WinWiimoteDevice::WinWiimoteDevice(HANDLE handle, std::vector identifier) : m_handle(handle), m_identifier(std::move(identifier)) { diff --git a/vcpkg.json b/vcpkg.json index 940ed748..7ea8058e 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -46,6 +46,7 @@ "name": "curl", "default-features": false, "features": [ "openssl" ] - } + }, + "libusb" ] } From 323bdfa18382986763450e55a6117e78873e6cfd Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Tue, 19 Sep 2023 16:54:38 +0100 Subject: [PATCH 032/101] More changes to finding wiimotes (#961) --- .../settings/WiimoteControllerSettings.cpp | 1 - .../api/Wiimote/WiimoteControllerProvider.cpp | 46 +++++++++++++------ .../api/Wiimote/hidapi/HidapiWiimote.cpp | 13 ++---- src/input/api/Wiimote/hidapi/HidapiWiimote.h | 3 +- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/gui/input/settings/WiimoteControllerSettings.cpp b/src/gui/input/settings/WiimoteControllerSettings.cpp index 5bc20269..9830b666 100644 --- a/src/gui/input/settings/WiimoteControllerSettings.cpp +++ b/src/gui/input/settings/WiimoteControllerSettings.cpp @@ -56,7 +56,6 @@ WiimoteControllerSettings::WiimoteControllerSettings(wxWindow* parent, const wxP // Motion m_use_motion = new wxCheckBox(box, wxID_ANY, _("Use motion")); m_use_motion->SetValue(m_settings.motion); - m_use_motion->SetValue(m_settings.motion); m_use_motion->Enable(m_controller->has_motion()); row_sizer->Add(m_use_motion, 0, wxALL, 5); diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.cpp b/src/input/api/Wiimote/WiimoteControllerProvider.cpp index 0ebf88aa..0ca00a1a 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.cpp +++ b/src/input/api/Wiimote/WiimoteControllerProvider.cpp @@ -9,6 +9,7 @@ #endif #include +#include WiimoteControllerProvider::WiimoteControllerProvider() : m_running(true) @@ -30,20 +31,39 @@ WiimoteControllerProvider::~WiimoteControllerProvider() std::vector> WiimoteControllerProvider::get_controllers() { std::scoped_lock lock(m_device_mutex); - for (const auto& device : WiimoteDevice_t::get_devices()) + + std::queue disconnected_wiimote_indices; + for (auto i{0u}; i < m_wiimotes.size(); ++i){ + if (!(m_wiimotes[i].connected = m_wiimotes[i].device->write_data({kStatusRequest, 0x00}))){ + disconnected_wiimote_indices.push(i); + } + } + + const auto valid_new_device = [&](std::shared_ptr & device) { + const auto writeable = device->write_data({kStatusRequest, 0x00}); + const auto not_already_connected = + std::none_of(m_wiimotes.cbegin(), m_wiimotes.cend(), + [device](const auto& it) { + return (*it.device == *device) && it.connected; + }); + return writeable && not_already_connected; + }; + + for (auto& device : WiimoteDevice_t::get_devices()) { - // test connection of all devices as they might have been changed - const bool is_connected = device->write_data({kStatusRequest, 0x00}); - if (is_connected) - { - // only add unknown, connected devices to our list - const bool is_new_device = std::none_of(m_wiimotes.cbegin(), m_wiimotes.cend(), - [device](const auto& it) { return *it.device == *device; }); - if (is_new_device) - { - m_wiimotes.push_back(std::make_unique(device)); - } - } + if (!valid_new_device(device)) + continue; + // Replace disconnected wiimotes + if (!disconnected_wiimote_indices.empty()){ + const auto idx = disconnected_wiimote_indices.front(); + disconnected_wiimote_indices.pop(); + + m_wiimotes.replace(idx, std::make_unique(device)); + } + // Otherwise add them + else { + m_wiimotes.push_back(std::make_unique(device)); + } } std::vector> result; diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp index 898e6cf4..a5701f56 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -5,8 +5,8 @@ static constexpr uint16 WIIMOTE_PRODUCT_ID = 0x0306; static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330; static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22; -HidapiWiimote::HidapiWiimote(hid_device* dev, uint64_t identifier, std::string_view path) - : m_handle(dev), m_identifier(identifier), m_path(path) { +HidapiWiimote::HidapiWiimote(hid_device* dev, std::string_view path) + : m_handle(dev), m_path(path) { } @@ -36,11 +36,7 @@ std::vector HidapiWiimote::get_devices() { } else { hid_set_nonblocking(dev, true); - // Enough to have a unique id for each device within a session - uint64_t id = (static_cast(it->interface_number) << 32) | - (static_cast(it->usage_page) << 16) | - (it->usage); - wiimote_devices.push_back(std::make_shared(dev, id, it->path)); + wiimote_devices.push_back(std::make_shared(dev, it->path)); } } hid_free_enumeration(device_enumeration); @@ -48,8 +44,7 @@ std::vector HidapiWiimote::get_devices() { } bool HidapiWiimote::operator==(WiimoteDevice& o) const { - auto const& other_mote = static_cast(o); - return m_identifier == other_mote.m_identifier && other_mote.m_path == m_path; + return static_cast(o).m_path == m_path; } HidapiWiimote::~HidapiWiimote() { diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.h b/src/input/api/Wiimote/hidapi/HidapiWiimote.h index 7b91dbbe..858cb1f3 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.h +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.h @@ -5,7 +5,7 @@ class HidapiWiimote : public WiimoteDevice { public: - HidapiWiimote(hid_device* dev, uint64_t identifier, std::string_view path); + HidapiWiimote(hid_device* dev, std::string_view path); ~HidapiWiimote() override; bool write_data(const std::vector &data) override; @@ -16,7 +16,6 @@ public: private: hid_device* m_handle; - const uint64_t m_identifier; const std::string m_path; }; From 90c56b773147d412e311f8e27430efdf2c94c0fd Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:17:21 +0200 Subject: [PATCH 033/101] Latte: Optimizations and tweaks (#706) --- .../HW/Latte/Core/LatteCommandProcessor.cpp | 1217 ++++++++++------- src/Cafe/HW/Latte/Core/LatteOverlay.cpp | 6 +- src/Cafe/HW/Latte/Core/LatteOverlay.h | 2 +- .../HW/Latte/Core/LattePerformanceMonitor.cpp | 8 +- .../HW/Latte/Core/LattePerformanceMonitor.h | 1 + src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp | 28 + .../HW/Latte/Core/LatteTextureReadback.cpp | 37 +- .../HW/Latte/Core/LatteTextureReadbackInfo.h | 1 + src/Cafe/HW/Latte/ISA/LatteReg.h | 2 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 2 +- 10 files changed, 822 insertions(+), 482 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp index 37ce8ff9..60e5935c 100644 --- a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp +++ b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp @@ -16,9 +16,17 @@ #include "Cafe/CafeSystem.h" +#include + +void LatteCP_DebugPrintCmdBuffer(uint32be* bufferPtr, uint32 size); + #define CP_TIMER_RECHECK 1024 -//#define FAST_DRAW_LOGGING +//#define LATTE_CP_LOGGING + +typedef uint32be* LatteCMDPtr; +#define LatteReadCMD() ((uint32)*(cmd++)) +#define LatteSkipCMD(_nWords) cmd += (_nWords) uint8* gxRingBufferReadPtr; // currently active read pointer (gx2 ring buffer or display list) uint8* gx2CPParserDisplayListPtr; @@ -31,6 +39,14 @@ void LatteThread_Exit(); class DrawPassContext { + struct CmdQueuePos + { + CmdQueuePos(LatteCMDPtr current, LatteCMDPtr start, LatteCMDPtr end) : current(current), start(start), end(end) {}; + + LatteCMDPtr current; + LatteCMDPtr start; + LatteCMDPtr end; + }; public: bool isWithinDrawPass() const { @@ -54,6 +70,13 @@ public: if (numInstances == 0) return; + /* + if (GetAsyncKeyState('B')) + { + cemuLog_force("[executeDraw] {} Count {} BaseVertex {} BaseInstance {}", m_isFirstDraw?"Init":"Fast", count, baseVertex, baseInstance); + } + */ + if (!isAutoIndex) { cemu_assert_debug(physIndices != MPTR_NULL); @@ -66,6 +89,9 @@ public: { g_renderer->draw_execute(baseVertex, baseInstance, numInstances, count, MPTR_NULL, Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE::AUTO, m_isFirstDraw); } + performanceMonitor.cycle[performanceMonitor.cycleIndex].drawCallCounter++; + if (!m_isFirstDraw) + performanceMonitor.cycle[performanceMonitor.cycleIndex].fastDrawCallCounter++; m_isFirstDraw = false; m_vertexBufferChanged = false; m_uniformBufferChanged = false; @@ -87,14 +113,33 @@ public: m_uniformBufferChanged = true; } + // command buffer processing position + void PushCurrentCommandQueuePos(LatteCMDPtr current, LatteCMDPtr start, LatteCMDPtr end) + { + m_queuePosStack.emplace_back(current, start, end); + } + + bool PopCurrentCommandQueuePos(LatteCMDPtr& current, LatteCMDPtr& start, LatteCMDPtr& end) + { + if (m_queuePosStack.empty()) + return false; + const auto& it = m_queuePosStack.back(); + current = it.current; + start = it.start; + end = it.end; + m_queuePosStack.pop_back(); + return true; + } + private: bool m_drawPassActive{ false }; bool m_isFirstDraw{false}; bool m_vertexBufferChanged{ false }; bool m_uniformBufferChanged{ false }; + boost::container::small_vector m_queuePosStack; }; -void LatteCP_processCommandBuffer(uint8* cmdBuffer, sint32 cmdSize, DrawPassContext& drawPassCtx); +void LatteCP_processCommandBuffer(DrawPassContext& drawPassCtx); /* * Read a U32 from the command buffer @@ -193,10 +238,6 @@ void LatteCP_skipWords(uint32 wordsToSkip) } } -typedef uint32be* LatteCMDPtr; -#define LatteReadCMD() ((uint32)*(cmd++)) -#define LatteSkipCMD(_nWords) cmd += (_nWords) - LatteCMDPtr LatteCP_itSurfaceSync(LatteCMDPtr cmd) { uint32 invalidationFlags = LatteReadCMD(); @@ -215,22 +256,31 @@ LatteCMDPtr LatteCP_itSurfaceSync(LatteCMDPtr cmd) return cmd; } -template -void LatteCP_itIndirectBufferDepr(uint32 nWords) +// called from TCL command queue. Executes a memory command buffer +void LatteCP_itIndirectBufferDepr(LatteCMDPtr cmd, uint32 nWords) { cemu_assert_debug(nWords == 3); - - uint32 physicalAddress = readU32(); - uint32 physicalAddressHigh = readU32(); // unused - uint32 sizeInDWords = readU32(); + uint32 physicalAddress = LatteReadCMD(); + uint32 physicalAddressHigh = LatteReadCMD(); // unused + uint32 sizeInDWords = LatteReadCMD(); uint32 displayListSize = sizeInDWords * 4; DrawPassContext drawPassCtx; - LatteCP_processCommandBuffer(memory_getPointerFromPhysicalOffset(physicalAddress), displayListSize, drawPassCtx); + +#ifdef LATTE_CP_LOGGING + if (GetAsyncKeyState('A')) + LatteCP_DebugPrintCmdBuffer(MEMPTR(physicalAddress), displayListSize); +#endif + + uint32be* buf = MEMPTR(physicalAddress).GetPtr(); + drawPassCtx.PushCurrentCommandQueuePos(buf, buf, buf + sizeInDWords); + + LatteCP_processCommandBuffer(drawPassCtx); if (drawPassCtx.isWithinDrawPass()) drawPassCtx.endDrawPass(); } -LatteCMDPtr LatteCP_itIndirectBuffer(LatteCMDPtr cmd, uint32 nWords, DrawPassContext& drawPassCtx) +// pushes the command buffer to the stack +void LatteCP_itIndirectBuffer(LatteCMDPtr cmd, uint32 nWords, DrawPassContext& drawPassCtx) { cemu_assert_debug(nWords == 3); uint32 physicalAddress = LatteReadCMD(); @@ -239,8 +289,8 @@ LatteCMDPtr LatteCP_itIndirectBuffer(LatteCMDPtr cmd, uint32 nWords, DrawPassCon uint32 displayListSize = sizeInDWords * 4; cemu_assert_debug(displayListSize >= 4); - LatteCP_processCommandBuffer(memory_getPointerFromPhysicalOffset(physicalAddress), displayListSize, drawPassCtx); - return cmd; + uint32be* buf = MEMPTR(physicalAddress).GetPtr(); + drawPassCtx.PushCurrentCommandQueuePos(buf, buf, buf + sizeInDWords); } LatteCMDPtr LatteCP_itStreamoutBufferUpdate(LatteCMDPtr cmd, uint32 nWords) @@ -615,8 +665,6 @@ LatteCMDPtr LatteCP_itDrawIndex2(LatteCMDPtr cmd, uint32 nWords, DrawPassContext uint32 count = LatteReadCMD(); uint32 ukn3 = LatteReadCMD(); - performanceMonitor.cycle[performanceMonitor.cycleIndex].drawCallCounter++; - LatteGPUState.currentDrawCallTick = GetTickCount(); drawPassCtx.executeDraw(count, false, physIndices); return cmd; @@ -628,8 +676,6 @@ LatteCMDPtr LatteCP_itDrawIndexAuto(LatteCMDPtr cmd, uint32 nWords, DrawPassCont uint32 count = LatteReadCMD(); uint32 ukn = LatteReadCMD(); - performanceMonitor.cycle[performanceMonitor.cycleIndex].drawCallCounter++; - if (LatteGPUState.drawContext.numInstances == 0) return cmd; LatteGPUState.currentDrawCallTick = GetTickCount(); @@ -692,7 +738,6 @@ LatteCMDPtr LatteCP_itDrawImmediate(LatteCMDPtr cmd, uint32 nWords, DrawPassCont // verify packet size if (nWords != (2 + numIndexU32s)) debugBreakpoint(); - performanceMonitor.cycle[performanceMonitor.cycleIndex].drawCallCounter++; uint32 baseVertex = LatteGPUState.contextRegister[mmSQ_VTX_BASE_VTX_LOC]; uint32 baseInstance = LatteGPUState.contextRegister[mmSQ_VTX_START_INST_LOC]; @@ -930,431 +975,412 @@ void LatteCP_dumpCommandBufferError(LatteCMDPtr cmdStart, LatteCMDPtr cmdEnd, La } // any drawcalls issued without changing textures, framebuffers, shader or other complex states can be done quickly without having to reinitialize the entire pipeline state -// we implement this optimization by having an optimized version of LatteCP_processCommandBuffer, called right after drawcalls, which only implements commands that dont interfere with fast drawing. Other commands will cause this function to return to the complex parser -LatteCMDPtr LatteCP_processCommandBuffer_continuousDrawPass(LatteCMDPtr cmd, LatteCMDPtr cmdStart, LatteCMDPtr cmdEnd, DrawPassContext& drawPassCtx) +// we implement this optimization by having a specialized version of LatteCP_processCommandBuffer, called right after drawcalls, which only implements commands that dont interfere with fast drawing. Other commands will cause this function to return to the complex and generic parser +void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCtx) { cemu_assert_debug(drawPassCtx.isWithinDrawPass()); // quit early if there are parameters set which are generally incompatible with fast drawing if (LatteGPUState.contextRegister[mmVGT_STRMOUT_EN] != 0) { drawPassCtx.endDrawPass(); - return cmd; + return; } // check for other special states? - while (cmd < cmdEnd) + while (true) { - LatteCMDPtr cmdBeforeCommand = cmd; - uint32 itHeader = LatteReadCMD(); - uint32 itHeaderType = (itHeader >> 30) & 3; - if (itHeaderType == 3) + LatteCMDPtr cmd, cmdStart, cmdEnd; + if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) { - uint32 itCode = (itHeader >> 8) & 0xFF; - uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1; - switch (itCode) - { - case IT_SET_RESOURCE: // attribute buffers, uniform buffers or texture units - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords, [&drawPassCtx](uint32 registerStart, uint32 registerEnd) - { - if (registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD_FIRST && registerStart <= Latte::REGADDR::SQ_TEX_RESOURCE_WORD_LAST) - drawPassCtx.endDrawPass(); // texture updates end the current draw sequence - else if (registerStart >= mmSQ_VTX_ATTRIBUTE_BLOCK_START && registerEnd <= mmSQ_VTX_ATTRIBUTE_BLOCK_END) - drawPassCtx.notifyModifiedVertexBuffer(); - else - drawPassCtx.notifyModifiedUniformBuffer(); - }); - if (!drawPassCtx.isWithinDrawPass()) - return cmd; - break; - } - case IT_SET_ALU_CONST: // uniform register - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - break; - } - case IT_SET_CTL_CONST: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - break; - } - case IT_SET_CONFIG_REG: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - break; - } - case IT_INDEX_TYPE: - { - cmd = LatteCP_itIndexType(cmd, nWords); - break; - } - case IT_NUM_INSTANCES: - { - cmd = LatteCP_itNumInstances(cmd, nWords); - break; - } - case IT_DRAW_INDEX_2: - { -#ifdef FAST_DRAW_LOGGING - if(GetAsyncKeyState('A')) - forceLogRemoveMe_printf("Minimal draw"); -#endif - cmd = LatteCP_itDrawIndex2(cmd, nWords, drawPassCtx); - break; - } - case IT_SET_CONTEXT_REG: - { -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] Quit due to command IT_SET_CONTEXT_REG Reg: %04x", (uint32)cmd[0] + 0xA000); -#endif - drawPassCtx.endDrawPass(); - return cmdBeforeCommand; - } - case IT_INDIRECT_BUFFER_PRIV: - { - cmd = LatteCP_itIndirectBuffer(cmd, nWords, drawPassCtx); - if (!drawPassCtx.isWithinDrawPass()) - return cmd; - break; - } - default: -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] Quit due to command itCode 0x%02x", itCode); -#endif - drawPassCtx.endDrawPass(); - return cmdBeforeCommand; - } - } - else if (itHeaderType == 2) - { - // filler packet - } - else - { -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] Quit due to unsupported headerType 0x%02x", itHeaderType); -#endif drawPassCtx.endDrawPass(); - return cmdBeforeCommand; - } - } - cemu_assert_debug(drawPassCtx.isWithinDrawPass()); - return cmd; -} - -void LatteCP_processCommandBuffer(uint8* cmdBuffer, sint32 cmdSize, DrawPassContext& drawPassCtx) -{ - LatteCMDPtr cmd = (LatteCMDPtr)cmdBuffer; - LatteCMDPtr cmdStart = (LatteCMDPtr)cmdBuffer; - LatteCMDPtr cmdEnd = (LatteCMDPtr)(cmdBuffer + cmdSize); - - if (drawPassCtx.isWithinDrawPass()) - { - cmd = LatteCP_processCommandBuffer_continuousDrawPass(cmd, cmdStart, cmdEnd, drawPassCtx); - cemu_assert_debug(cmd <= cmdEnd); - if (cmd == cmdEnd) return; - cemu_assert_debug(!drawPassCtx.isWithinDrawPass()); - } + } - while (cmd < cmdEnd) - { - uint32 itHeader = LatteReadCMD(); - uint32 itHeaderType = (itHeader >> 30) & 3; - if (itHeaderType == 3) + while (cmd < cmdEnd) { - uint32 itCode = (itHeader >> 8) & 0xFF; - uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1; -#ifdef CEMU_DEBUG_ASSERT - LatteCMDPtr expectedPostCmd = cmd + nWords; -#endif - switch (itCode) + LatteCMDPtr cmdBeforeCommand = cmd; + uint32 itHeader = LatteReadCMD(); + uint32 itHeaderType = (itHeader >> 30) & 3; + if (itHeaderType == 3) { - case IT_SET_CONTEXT_REG: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - } - break; - case IT_SET_RESOURCE: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - } - break; - case IT_SET_ALU_CONST: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - } - break; - case IT_SET_CTL_CONST: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - } - break; - case IT_SET_SAMPLER: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - } - break; - case IT_SET_CONFIG_REG: - { - cmd = LatteCP_itSetRegistersGeneric(cmd, nWords); - } - break; - case IT_SET_LOOP_CONST: - { - LatteSkipCMD(nWords); - // todo - } - break; - case IT_SURFACE_SYNC: - { - cmd = LatteCP_itSurfaceSync(cmd); - } - break; - case IT_INDIRECT_BUFFER_PRIV: - { - cmd = LatteCP_itIndirectBuffer(cmd, nWords, drawPassCtx); - if (drawPassCtx.isWithinDrawPass()) + uint32 itCode = (itHeader >> 8) & 0xFF; + uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1; + LatteCMDPtr cmdData = cmd; + cmd += nWords; + switch (itCode) { - cmd = LatteCP_processCommandBuffer_continuousDrawPass(cmd, cmdStart, cmdEnd, drawPassCtx); - cemu_assert_debug(cmd <= cmdEnd); - if (cmd == cmdEnd) + case IT_SET_RESOURCE: // attribute buffers, uniform buffers or texture units + { + LatteCP_itSetRegistersGeneric(cmdData, nWords, [&drawPassCtx](uint32 registerStart, uint32 registerEnd) + { + if ((registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)) || + (registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)) || + (registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7))) + drawPassCtx.endDrawPass(); // texture updates end the current draw sequence + else if (registerStart >= mmSQ_VTX_ATTRIBUTE_BLOCK_START && registerEnd <= mmSQ_VTX_ATTRIBUTE_BLOCK_END) + drawPassCtx.notifyModifiedVertexBuffer(); + else + drawPassCtx.notifyModifiedUniformBuffer(); + }); + if (!drawPassCtx.isWithinDrawPass()) + { + drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); return; - cemu_assert_debug(!drawPassCtx.isWithinDrawPass()); + } + break; + } + case IT_SET_ALU_CONST: // uniform register + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + break; + } + case IT_SET_CTL_CONST: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + break; + } + case IT_SET_CONFIG_REG: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + break; + } + case IT_INDEX_TYPE: + { + LatteCP_itIndexType(cmdData, nWords); + break; + } + case IT_NUM_INSTANCES: + { + LatteCP_itNumInstances(cmdData, nWords); + break; + } + case IT_DRAW_INDEX_2: + { + LatteCP_itDrawIndex2(cmdData, nWords, drawPassCtx); + break; + } + case IT_SET_CONTEXT_REG: + { + drawPassCtx.endDrawPass(); + drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); + return; + } + case IT_INDIRECT_BUFFER_PRIV: + { + drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); + LatteCP_itIndirectBuffer(cmdData, nWords, drawPassCtx); + if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) // switch to sub buffer + cemu_assert_debug(false); + + //if (!drawPassCtx.isWithinDrawPass()) + // return cmdData; + break; + } + default: + // unsupported command for fast draw + drawPassCtx.endDrawPass(); + drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); + return; } -#ifdef CEMU_DEBUG_ASSERT - expectedPostCmd = cmd; -#endif } - break; - case IT_STRMOUT_BUFFER_UPDATE: + else if (itHeaderType == 2) { - cmd = LatteCP_itStreamoutBufferUpdate(cmd, nWords); - } - break; - case IT_INDEX_TYPE: - { - cmd = LatteCP_itIndexType(cmd, nWords); - } - break; - case IT_NUM_INSTANCES: - { - cmd = LatteCP_itNumInstances(cmd, nWords); - } - break; - case IT_DRAW_INDEX_2: - { - drawPassCtx.beginDrawPass(); -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] Starting"); -#endif - cmd = LatteCP_itDrawIndex2(cmd, nWords, drawPassCtx); - cmd = LatteCP_processCommandBuffer_continuousDrawPass(cmd, cmdStart, cmdEnd, drawPassCtx); - cemu_assert_debug(cmd == cmdEnd || drawPassCtx.isWithinDrawPass() == false); // draw sequence should have ended if we didn't reach the end of the command buffer -#ifdef CEMU_DEBUG_ASSERT - expectedPostCmd = cmd; -#endif - } - break; - case IT_DRAW_INDEX_AUTO: - { - drawPassCtx.beginDrawPass(); - cmd = LatteCP_itDrawIndexAuto(cmd, nWords, drawPassCtx); - cmd = LatteCP_processCommandBuffer_continuousDrawPass(cmd, cmdStart, cmdEnd, drawPassCtx); - cemu_assert_debug(cmd == cmdEnd || drawPassCtx.isWithinDrawPass() == false); // draw sequence should have ended if we didn't reach the end of the command buffer -#ifdef CEMU_DEBUG_ASSERT - expectedPostCmd = cmd; -#endif -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] Auto-draw"); -#endif - } - break; - case IT_DRAW_INDEX_IMMD: - { - DrawPassContext drawPassCtx; - drawPassCtx.beginDrawPass(); - cmd = LatteCP_itDrawImmediate(cmd, nWords, drawPassCtx); - drawPassCtx.endDrawPass(); - break; - } - case IT_WAIT_REG_MEM: - { - cmd = LatteCP_itWaitRegMem(cmd, nWords); - LatteTiming_HandleTimedVsync(); - LatteAsyncCommands_checkAndExecute(); - } - break; - case IT_MEM_WRITE: - { - cmd = LatteCP_itMemWrite(cmd, nWords); - } - break; - case IT_CONTEXT_CONTROL: - { - cmd = LatteCP_itContextControl(cmd, nWords); - } - break; - case IT_MEM_SEMAPHORE: - { - cmd = LatteCP_itMemSemaphore(cmd, nWords); - } - break; - case IT_LOAD_CONFIG_REG: - { - cmd = LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_CONFIG); - } - break; - case IT_LOAD_CONTEXT_REG: - { - cmd = LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_CONTEXT); - } - break; - case IT_LOAD_ALU_CONST: - { - cmd = LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_ALU_CONST); - } - break; - case IT_LOAD_LOOP_CONST: - { - cmd = LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_LOOP_CONST); - } - break; - case IT_LOAD_RESOURCE: - { - cmd = LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_RESOURCE); - } - break; - case IT_LOAD_SAMPLER: - { - cmd = LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_SAMPLER); - } - break; - case IT_SET_PREDICATION: - { - cmd = LatteCP_itSetPredication(cmd, nWords); - } - break; - case IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER: - { - cmd = LatteCP_itHLECopyColorBufferToScanBuffer(cmd, nWords); - } - break; - case IT_HLE_TRIGGER_SCANBUFFER_SWAP: - { - cmd = LatteCP_itHLESwapScanBuffer(cmd, nWords); - } - break; - case IT_HLE_WAIT_FOR_FLIP: - { - cmd = LatteCP_itHLEWaitForFlip(cmd, nWords); - } - break; - case IT_HLE_REQUEST_SWAP_BUFFERS: - { - cmd = LatteCP_itHLERequestSwapBuffers(cmd, nWords); - } - break; - case IT_HLE_CLEAR_COLOR_DEPTH_STENCIL: - { - cmd = LatteCP_itHLEClearColorDepthStencil(cmd, nWords); - } - break; - case IT_HLE_COPY_SURFACE_NEW: - { - cmd = LatteCP_itHLECopySurfaceNew(cmd, nWords); - } - break; - case IT_HLE_SAMPLE_TIMER: - { - cmd = LatteCP_itHLESampleTimer(cmd, nWords); - } - break; - case IT_HLE_SPECIAL_STATE: - { - cmd = LatteCP_itHLESpecialState(cmd, nWords); - } - break; - case IT_HLE_BEGIN_OCCLUSION_QUERY: - { - cmd = LatteCP_itHLEBeginOcclusionQuery(cmd, nWords); - } - break; - case IT_HLE_END_OCCLUSION_QUERY: - { - cmd = LatteCP_itHLEEndOcclusionQuery(cmd, nWords); - } - break; - case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP: - { - cmd = LatteCP_itHLESetRetirementTimestamp(cmd, nWords); - } - break; - case IT_HLE_BOTTOM_OF_PIPE_CB: - { - cmd = LatteCP_itHLEBottomOfPipeCB(cmd, nWords); - } - break; - case IT_HLE_SYNC_ASYNC_OPERATIONS: - { - LatteSkipCMD(nWords); - LatteTextureReadback_UpdateFinishedTransfers(true); - LatteQuery_UpdateFinishedQueriesForceFinishAll(); - } - break; - default: - debug_printf("Unhandled IT %02x\n", itCode); - cemu_assert_debug(false); - LatteSkipCMD(nWords); - } -#ifdef CEMU_DEBUG_ASSERT - if(cmd != expectedPostCmd) - debug_printf("cmd %016p expectedPostCmd %016p\n", cmd, expectedPostCmd); - cemu_assert_debug(cmd == expectedPostCmd); -#endif - } - else if (itHeaderType == 2) - { - // filler packet - // has no body - } - else if (itHeaderType == 0) - { - uint32 registerBase = (itHeader & 0xFFFF); - uint32 registerCount = ((itHeader >> 16) & 0x3FFF) + 1; - if (registerBase == 0x304A) - { - GX2::__GX2NotifyEvent(GX2::GX2CallbackEventType::TIMESTAMP_TOP); - LatteSkipCMD(registerCount); - } - else if (registerBase == 0x304B) - { - LatteSkipCMD(registerCount); + // filler packet } else { + // unsupported command for fast draw + drawPassCtx.endDrawPass(); + drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); + return; + } + } + } + if (drawPassCtx.isWithinDrawPass()) + drawPassCtx.endDrawPass(); +} + +void LatteCP_processCommandBuffer(DrawPassContext& drawPassCtx) +{ + while (true) + { + LatteCMDPtr cmd, cmdStart, cmdEnd; + if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) + break; + while (cmd < cmdEnd) + { + uint32 itHeader = LatteReadCMD(); + uint32 itHeaderType = (itHeader >> 30) & 3; + if (itHeaderType == 3) + { + uint32 itCode = (itHeader >> 8) & 0xFF; + uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1; + LatteCMDPtr cmdData = cmd; + cmd += nWords; + switch (itCode) + { + case IT_SET_CONTEXT_REG: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + } + break; + case IT_SET_RESOURCE: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + } + break; + case IT_SET_ALU_CONST: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + } + break; + case IT_SET_CTL_CONST: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + } + break; + case IT_SET_SAMPLER: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + } + break; + case IT_SET_CONFIG_REG: + { + LatteCP_itSetRegistersGeneric(cmdData, nWords); + } + break; + case IT_SET_LOOP_CONST: + { + // todo + } + break; + case IT_SURFACE_SYNC: + { + LatteCP_itSurfaceSync(cmdData); + } + break; + case IT_INDIRECT_BUFFER_PRIV: + { + drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); + LatteCP_itIndirectBuffer(cmdData, nWords, drawPassCtx); + if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) // switch to sub buffer + cemu_assert_debug(false); + } + break; + case IT_STRMOUT_BUFFER_UPDATE: + { + LatteCP_itStreamoutBufferUpdate(cmdData, nWords); + } + break; + case IT_INDEX_TYPE: + { + LatteCP_itIndexType(cmdData, nWords); + } + break; + case IT_NUM_INSTANCES: + { + LatteCP_itNumInstances(cmdData, nWords); + } + break; + case IT_DRAW_INDEX_2: + { + drawPassCtx.beginDrawPass(); + LatteCP_itDrawIndex2(cmdData, nWords, drawPassCtx); + // enter fast draw mode + drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); + LatteCP_processCommandBuffer_continuousDrawPass(drawPassCtx); + cemu_assert_debug(!drawPassCtx.isWithinDrawPass()); + if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) + return; + } + break; + case IT_DRAW_INDEX_AUTO: + { + drawPassCtx.beginDrawPass(); + LatteCP_itDrawIndexAuto(cmdData, nWords, drawPassCtx); + // enter fast draw mode + drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); + LatteCP_processCommandBuffer_continuousDrawPass(drawPassCtx); + cemu_assert_debug(!drawPassCtx.isWithinDrawPass()); + if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) + return; + } + break; + case IT_DRAW_INDEX_IMMD: + { + DrawPassContext drawPassCtx; + drawPassCtx.beginDrawPass(); + LatteCP_itDrawImmediate(cmdData, nWords, drawPassCtx); + drawPassCtx.endDrawPass(); + break; + } + case IT_WAIT_REG_MEM: + { + LatteCP_itWaitRegMem(cmdData, nWords); + LatteTiming_HandleTimedVsync(); + LatteAsyncCommands_checkAndExecute(); + break; + } + case IT_MEM_WRITE: + { + LatteCP_itMemWrite(cmdData, nWords); + break; + } + case IT_CONTEXT_CONTROL: + { + LatteCP_itContextControl(cmdData, nWords); + break; + } + case IT_MEM_SEMAPHORE: + { + LatteCP_itMemSemaphore(cmdData, nWords); + break; + } + case IT_LOAD_CONFIG_REG: + { + LatteCP_itLoadReg(cmdData, nWords, LATTE_REG_BASE_CONFIG); + break; + } + case IT_LOAD_CONTEXT_REG: + { + LatteCP_itLoadReg(cmdData, nWords, LATTE_REG_BASE_CONTEXT); + break; + } + case IT_LOAD_ALU_CONST: + { + LatteCP_itLoadReg(cmdData, nWords, LATTE_REG_BASE_ALU_CONST); + break; + } + case IT_LOAD_LOOP_CONST: + { + LatteCP_itLoadReg(cmdData, nWords, LATTE_REG_BASE_LOOP_CONST); + break; + } + case IT_LOAD_RESOURCE: + { + LatteCP_itLoadReg(cmdData, nWords, LATTE_REG_BASE_RESOURCE); + break; + } + case IT_LOAD_SAMPLER: + { + LatteCP_itLoadReg(cmdData, nWords, LATTE_REG_BASE_SAMPLER); + break; + } + case IT_SET_PREDICATION: + { + LatteCP_itSetPredication(cmdData, nWords); + break; + } + case IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER: + { + LatteCP_itHLECopyColorBufferToScanBuffer(cmdData, nWords); + break; + } + case IT_HLE_TRIGGER_SCANBUFFER_SWAP: + { + LatteCP_itHLESwapScanBuffer(cmdData, nWords); + break; + } + case IT_HLE_WAIT_FOR_FLIP: + { + LatteCP_itHLEWaitForFlip(cmdData, nWords); + break; + } + case IT_HLE_REQUEST_SWAP_BUFFERS: + { + LatteCP_itHLERequestSwapBuffers(cmdData, nWords); + break; + } + case IT_HLE_CLEAR_COLOR_DEPTH_STENCIL: + { + LatteCP_itHLEClearColorDepthStencil(cmdData, nWords); + break; + } + case IT_HLE_COPY_SURFACE_NEW: + { + LatteCP_itHLECopySurfaceNew(cmdData, nWords); + break; + } + case IT_HLE_SAMPLE_TIMER: + { + LatteCP_itHLESampleTimer(cmdData, nWords); + break; + } + case IT_HLE_SPECIAL_STATE: + { + LatteCP_itHLESpecialState(cmdData, nWords); + break; + } + case IT_HLE_BEGIN_OCCLUSION_QUERY: + { + LatteCP_itHLEBeginOcclusionQuery(cmdData, nWords); + break; + } + case IT_HLE_END_OCCLUSION_QUERY: + { + LatteCP_itHLEEndOcclusionQuery(cmdData, nWords); + break; + } + case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP: + { + LatteCP_itHLESetRetirementTimestamp(cmdData, nWords); + break; + } + case IT_HLE_BOTTOM_OF_PIPE_CB: + { + LatteCP_itHLEBottomOfPipeCB(cmdData, nWords); + break; + } + case IT_HLE_SYNC_ASYNC_OPERATIONS: + { + LatteTextureReadback_UpdateFinishedTransfers(true); + LatteQuery_UpdateFinishedQueriesForceFinishAll(); + break; + } + default: + debug_printf("Unhandled IT %02x\n", itCode); + cemu_assert_debug(false); + break; + } + } + else if (itHeaderType == 2) + { + // filler packet + // has no body + } + else if (itHeaderType == 0) + { + uint32 registerBase = (itHeader & 0xFFFF); + uint32 registerCount = ((itHeader >> 16) & 0x3FFF) + 1; + if (registerBase == 0x304A) + { + GX2::__GX2NotifyEvent(GX2::GX2CallbackEventType::TIMESTAMP_TOP); + LatteSkipCMD(registerCount); + } + else if (registerBase == 0x304B) + { + LatteSkipCMD(registerCount); + } + else + { + LatteCP_dumpCommandBufferError(cmdStart, cmdEnd, cmd); + cemu_assert_debug(false); + } + } + else + { + debug_printf("invalid itHeaderType %08x\n", itHeaderType); LatteCP_dumpCommandBufferError(cmdStart, cmdEnd, cmd); cemu_assert_debug(false); } } - else - { - debug_printf("invalid itHeaderType %08x\n", itHeaderType); - LatteCP_dumpCommandBufferError(cmdStart, cmdEnd, cmd); - cemu_assert_debug(false); - } + cemu_assert_debug(cmd == cmdEnd); } - cemu_assert_debug(cmd == cmdEnd); } void LatteCP_ProcessRingbuffer() { - sint32 timerRecheck = 0; // estimates how much CP processing time passed based on the executed commands, if the value exceeds CP_TIMER_RECHECK then _handleTimers() is called + sint32 timerRecheck = 0; // estimates how much CP processing time has elapsed based on the executed commands, if the value exceeds CP_TIMER_RECHECK then _handleTimers() is called while (true) { uint32 itHeader = LatteCP_readU32Deprc(); @@ -1365,80 +1391,73 @@ void LatteCP_ProcessRingbuffer() uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1; LatteCP_waitForNWords(nWords); LatteCMDPtr cmd = (LatteCMDPtr)gxRingBufferReadPtr; - uint8* expectedGxRingBufferReadPtr = gxRingBufferReadPtr + nWords*4; + uint8* cmdEnd = gxRingBufferReadPtr + nWords * 4; + gxRingBufferReadPtr = cmdEnd; switch (itCode) { case IT_SURFACE_SYNC: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSurfaceSync(cmd); + LatteCP_itSurfaceSync(cmd); timerRecheck += CP_TIMER_RECHECK / 512; } break; case IT_SET_CONTEXT_REG: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetRegistersGeneric(cmd, nWords); + LatteCP_itSetRegistersGeneric(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; } break; case IT_SET_RESOURCE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetRegistersGeneric(cmd, nWords); + LatteCP_itSetRegistersGeneric(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; } break; case IT_SET_ALU_CONST: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetRegistersGeneric(cmd, nWords); + LatteCP_itSetRegistersGeneric(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_SET_CTL_CONST: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetRegistersGeneric(cmd, nWords); + LatteCP_itSetRegistersGeneric(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_SET_SAMPLER: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetRegistersGeneric(cmd, nWords); + LatteCP_itSetRegistersGeneric(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_SET_CONFIG_REG: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetRegistersGeneric(cmd, nWords); + LatteCP_itSetRegistersGeneric(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_INDIRECT_BUFFER_PRIV: { -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] BEGIN CMD BUFFER"); -#endif - LatteCP_itIndirectBufferDepr(nWords); + LatteCP_itIndirectBufferDepr(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; -#ifdef FAST_DRAW_LOGGING - if (GetAsyncKeyState('A')) - forceLogRemoveMe_printf("[FAST-DRAW] END CMD BUFFER"); -#endif break; } case IT_STRMOUT_BUFFER_UPDATE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itStreamoutBufferUpdate(cmd, nWords); + LatteCP_itStreamoutBufferUpdate(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_INDEX_TYPE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itIndexType(cmd, nWords); + LatteCP_itIndexType(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 1024; break; } case IT_NUM_INSTANCES: { - gxRingBufferReadPtr = (uint8*)LatteCP_itNumInstances(cmd, nWords); + LatteCP_itNumInstances(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 1024; break; } @@ -1446,7 +1465,7 @@ void LatteCP_ProcessRingbuffer() { DrawPassContext drawPassCtx; drawPassCtx.beginDrawPass(); - gxRingBufferReadPtr = (uint8*)LatteCP_itDrawIndex2(cmd, nWords, drawPassCtx); + LatteCP_itDrawIndex2(cmd, nWords, drawPassCtx); drawPassCtx.endDrawPass(); timerRecheck += CP_TIMER_RECHECK / 64; break; @@ -1455,7 +1474,7 @@ void LatteCP_ProcessRingbuffer() { DrawPassContext drawPassCtx; drawPassCtx.beginDrawPass(); - gxRingBufferReadPtr = (uint8*)LatteCP_itDrawIndexAuto(cmd, nWords, drawPassCtx); + LatteCP_itDrawIndexAuto(cmd, nWords, drawPassCtx); drawPassCtx.endDrawPass(); timerRecheck += CP_TIMER_RECHECK / 512; break; @@ -1464,165 +1483,162 @@ void LatteCP_ProcessRingbuffer() { DrawPassContext drawPassCtx; drawPassCtx.beginDrawPass(); - gxRingBufferReadPtr = (uint8*)LatteCP_itDrawImmediate(cmd, nWords, drawPassCtx); + LatteCP_itDrawImmediate(cmd, nWords, drawPassCtx); drawPassCtx.endDrawPass(); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_WAIT_REG_MEM: { - gxRingBufferReadPtr = (uint8*)LatteCP_itWaitRegMem(cmd, nWords); + LatteCP_itWaitRegMem(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 16; break; } case IT_MEM_WRITE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itMemWrite(cmd, nWords); + LatteCP_itMemWrite(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 128; break; } case IT_CONTEXT_CONTROL: { - gxRingBufferReadPtr = (uint8*)LatteCP_itContextControl(cmd, nWords); + LatteCP_itContextControl(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 128; break; } case IT_MEM_SEMAPHORE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itMemSemaphore(cmd, nWords); + LatteCP_itMemSemaphore(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 128; break; } case IT_LOAD_CONFIG_REG: { - gxRingBufferReadPtr = (uint8*)LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_CONFIG); + LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_CONFIG); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_LOAD_CONTEXT_REG: { - gxRingBufferReadPtr = (uint8*)LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_CONTEXT); + LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_CONTEXT); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_LOAD_ALU_CONST: { - gxRingBufferReadPtr = (uint8*)LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_ALU_CONST); + LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_ALU_CONST); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_LOAD_LOOP_CONST: { - gxRingBufferReadPtr = (uint8*)LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_LOOP_CONST); + LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_LOOP_CONST); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_LOAD_RESOURCE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_RESOURCE); + LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_RESOURCE); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_LOAD_SAMPLER: { - gxRingBufferReadPtr = (uint8*)LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_SAMPLER); + LatteCP_itLoadReg(cmd, nWords, LATTE_REG_BASE_SAMPLER); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_SET_LOOP_CONST: { - LatteSkipCMD(nWords); - gxRingBufferReadPtr = (uint8*)cmd; // todo break; } case IT_SET_PREDICATION: { - gxRingBufferReadPtr = (uint8*)LatteCP_itSetPredication(cmd, nWords); + LatteCP_itSetPredication(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLECopyColorBufferToScanBuffer(cmd, nWords); + LatteCP_itHLECopyColorBufferToScanBuffer(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_HLE_TRIGGER_SCANBUFFER_SWAP: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLESwapScanBuffer(cmd, nWords); + LatteCP_itHLESwapScanBuffer(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 64; break; } case IT_HLE_WAIT_FOR_FLIP: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLEWaitForFlip(cmd, nWords); + LatteCP_itHLEWaitForFlip(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 1; break; } case IT_HLE_REQUEST_SWAP_BUFFERS: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLERequestSwapBuffers(cmd, nWords); + LatteCP_itHLERequestSwapBuffers(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 32; break; } case IT_HLE_CLEAR_COLOR_DEPTH_STENCIL: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLEClearColorDepthStencil(cmd, nWords); + LatteCP_itHLEClearColorDepthStencil(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 128; break; } case IT_HLE_COPY_SURFACE_NEW: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLECopySurfaceNew(cmd, nWords); + LatteCP_itHLECopySurfaceNew(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 128; break; } case IT_HLE_FIFO_WRAP_AROUND: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLEFifoWrapAround(cmd, nWords); - expectedGxRingBufferReadPtr = gxRingBufferReadPtr; + LatteCP_itHLEFifoWrapAround(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_SAMPLE_TIMER: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLESampleTimer(cmd, nWords); + LatteCP_itHLESampleTimer(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_SPECIAL_STATE: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLESpecialState(cmd, nWords); + LatteCP_itHLESpecialState(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_BEGIN_OCCLUSION_QUERY: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLEBeginOcclusionQuery(cmd, nWords); + LatteCP_itHLEBeginOcclusionQuery(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_END_OCCLUSION_QUERY: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLEEndOcclusionQuery(cmd, nWords); + LatteCP_itHLEEndOcclusionQuery(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLESetRetirementTimestamp(cmd, nWords); + LatteCP_itHLESetRetirementTimestamp(cmd, nWords); timerRecheck += CP_TIMER_RECHECK / 512; break; } case IT_HLE_BOTTOM_OF_PIPE_CB: { - gxRingBufferReadPtr = (uint8*)LatteCP_itHLEBottomOfPipeCB(cmd, nWords); + LatteCP_itHLEBottomOfPipeCB(cmd, nWords); break; } case IT_HLE_SYNC_ASYNC_OPERATIONS: { - LatteCP_skipWords(nWords); + //LatteCP_skipWords(nWords); LatteTextureReadback_UpdateFinishedTransfers(true); LatteQuery_UpdateFinishedQueriesForceFinishAll(); break; @@ -1630,7 +1646,6 @@ void LatteCP_ProcessRingbuffer() default: cemu_assert_debug(false); } - cemu_assert_debug(expectedGxRingBufferReadPtr == gxRingBufferReadPtr); } else if (itHeaderType == 2) { @@ -1668,3 +1683,275 @@ void LatteCP_ProcessRingbuffer() } } } + +#ifdef LATTE_CP_LOGGING +void LatteCP_DebugPrintCmdBuffer(uint32be* bufferPtr, uint32 size) +{ + uint32be* bufferPtrInitial = bufferPtr; + uint32be* bufferPtrEnd = bufferPtr + (size/4); + while (bufferPtr < bufferPtrEnd) + { + std::string strPrefix = fmt::format("[PM4 Buf {:08x} Offs {:04x}]", MEMPTR(bufferPtr).GetMPTR(), (bufferPtr - bufferPtrInitial) * 4); + uint32 itHeader = *bufferPtr; + bufferPtr++; + uint32 itHeaderType = (itHeader >> 30) & 3; + if (itHeaderType == 3) + { + uint32 itCode = (itHeader >> 8) & 0xFF; + uint32 nWords = ((itHeader >> 16) & 0x3FFF) + 1; + uint32be* cmdData = bufferPtr; + bufferPtr += nWords; + switch (itCode) + { + case IT_SURFACE_SYNC: + { + cemuLog_log(LogType::Force, "{} IT_SURFACE_SYNC", strPrefix); + break; + } + case IT_SET_CONTEXT_REG: + { + std::string regVals; + for (uint32 i = 0; i < std::min(nWords - 1, 8); i++) + regVals.append(fmt::format("{:08x} ", cmdData[1 + i].value())); + cemuLog_log(LogType::Force, "{} IT_SET_CONTEXT_REG Reg {:04x} RegValues {}", strPrefix, cmdData[0].value(), regVals); + } + case IT_SET_RESOURCE: + { + std::string regVals; + for (uint32 i = 0; i < std::min(nWords - 1, 8); i++) + regVals.append(fmt::format("{:08x} ", cmdData[1+i].value())); + cemuLog_log(LogType::Force, "{} IT_SET_RESOURCE Reg {:04x} RegValues {}", strPrefix, cmdData[0].value(), regVals); + break; + } + case IT_SET_ALU_CONST: + { + cemuLog_log(LogType::Force, "{} IT_SET_ALU_CONST", strPrefix); + break; + } + case IT_SET_CTL_CONST: + { + cemuLog_log(LogType::Force, "{} IT_SET_CTL_CONST", strPrefix); + break; + } + case IT_SET_SAMPLER: + { + cemuLog_log(LogType::Force, "{} IT_SET_SAMPLER", strPrefix); + break; + } + case IT_SET_CONFIG_REG: + { + cemuLog_log(LogType::Force, "{} IT_SET_CONFIG_REG", strPrefix); + break; + } + case IT_INDIRECT_BUFFER_PRIV: + { + if (nWords != 3) + { + cemuLog_log(LogType::Force, "{} IT_INDIRECT_BUFFER_PRIV (malformed!)", strPrefix); + } + else + { + uint32 physicalAddress = cmdData[0]; + uint32 physicalAddressHigh = cmdData[1]; + uint32 sizeInDWords = cmdData[2]; + cemuLog_log(LogType::Force, "{} IT_INDIRECT_BUFFER_PRIV Addr {:08x} Size {:08x}", strPrefix, physicalAddress, sizeInDWords*4); + LatteCP_DebugPrintCmdBuffer(MEMPTR(physicalAddress), sizeInDWords * 4); + } + break; + } + case IT_STRMOUT_BUFFER_UPDATE: + { + cemuLog_log(LogType::Force, "{} IT_STRMOUT_BUFFER_UPDATE", strPrefix); + break; + } + case IT_INDEX_TYPE: + { + cemuLog_log(LogType::Force, "{} IT_INDEX_TYPE", strPrefix); + break; + } + case IT_NUM_INSTANCES: + { + cemuLog_log(LogType::Force, "{} IT_NUM_INSTANCES", strPrefix); + break; + } + case IT_DRAW_INDEX_2: + { + if (nWords != 5) + { + cemuLog_log(LogType::Force, "{} IT_DRAW_INDEX_2 (malformed!)", strPrefix); + } + else + { + uint32 ukn1 = cmdData[0]; + MPTR physIndices = cmdData[1]; + uint32 ukn2 = cmdData[2]; + uint32 count = cmdData[3]; + uint32 ukn3 = cmdData[4]; + cemuLog_log(LogType::Force, "{} IT_DRAW_INDEX_2 | Count {}", strPrefix, count); + } + break; + } + case IT_DRAW_INDEX_AUTO: + { + cemuLog_log(LogType::Force, "{} IT_DRAW_INDEX_AUTO", strPrefix); + break; + } + case IT_DRAW_INDEX_IMMD: + { + cemuLog_log(LogType::Force, "{} IT_DRAW_INDEX_IMMD", strPrefix); + break; + } + case IT_WAIT_REG_MEM: + { + cemuLog_log(LogType::Force, "{} IT_WAIT_REG_MEM", strPrefix); + break; + } + case IT_MEM_WRITE: + { + cemuLog_log(LogType::Force, "{} IT_MEM_WRITE", strPrefix); + break; + } + case IT_CONTEXT_CONTROL: + { + cemuLog_log(LogType::Force, "{} IT_CONTEXT_CONTROL", strPrefix); + break; + } + case IT_MEM_SEMAPHORE: + { + cemuLog_log(LogType::Force, "{} IT_MEM_SEMAPHORE", strPrefix); + break; + } + case IT_LOAD_CONFIG_REG: + { + cemuLog_log(LogType::Force, "{} IT_LOAD_CONFIG_REG", strPrefix); + break; + } + case IT_LOAD_CONTEXT_REG: + { + cemuLog_log(LogType::Force, "{} IT_LOAD_CONTEXT_REG", strPrefix); + break; + } + case IT_LOAD_ALU_CONST: + { + cemuLog_log(LogType::Force, "{} IT_LOAD_ALU_CONST", strPrefix); + break; + } + case IT_LOAD_LOOP_CONST: + { + cemuLog_log(LogType::Force, "{} IT_LOAD_LOOP_CONST", strPrefix); + break; + } + case IT_LOAD_RESOURCE: + { + cemuLog_log(LogType::Force, "{} IT_LOAD_RESOURCE", strPrefix); + break; + } + case IT_LOAD_SAMPLER: + { + cemuLog_log(LogType::Force, "{} IT_LOAD_SAMPLER", strPrefix); + break; + } + case IT_SET_LOOP_CONST: + { + cemuLog_log(LogType::Force, "{} IT_SET_LOOP_CONST", strPrefix); + break; + } + case IT_SET_PREDICATION: + { + cemuLog_log(LogType::Force, "{} IT_SET_PREDICATION", strPrefix); + break; + } + case IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER: + { + cemuLog_log(LogType::Force, "{} IT_HLE_COPY_COLORBUFFER_TO_SCANBUFFER", strPrefix); + break; + } + case IT_HLE_TRIGGER_SCANBUFFER_SWAP: + { + cemuLog_log(LogType::Force, "{} IT_HLE_TRIGGER_SCANBUFFER_SWAP", strPrefix); + break; + } + case IT_HLE_WAIT_FOR_FLIP: + { + cemuLog_log(LogType::Force, "{} IT_HLE_WAIT_FOR_FLIP", strPrefix); + break; + } + case IT_HLE_REQUEST_SWAP_BUFFERS: + { + cemuLog_log(LogType::Force, "{} IT_HLE_REQUEST_SWAP_BUFFERS", strPrefix); + break; + } + case IT_HLE_CLEAR_COLOR_DEPTH_STENCIL: + { + cemuLog_log(LogType::Force, "{} IT_HLE_CLEAR_COLOR_DEPTH_STENCIL", strPrefix); + break; + } + case IT_HLE_COPY_SURFACE_NEW: + { + cemuLog_log(LogType::Force, "{} IT_HLE_COPY_SURFACE_NEW", strPrefix); + break; + } + case IT_HLE_FIFO_WRAP_AROUND: + { + cemuLog_log(LogType::Force, "{} IT_HLE_FIFO_WRAP_AROUND", strPrefix); + break; + } + case IT_HLE_SAMPLE_TIMER: + { + cemuLog_log(LogType::Force, "{} IT_HLE_SAMPLE_TIMER", strPrefix); + break; + } + case IT_HLE_SPECIAL_STATE: + { + cemuLog_log(LogType::Force, "{} IT_HLE_SPECIAL_STATE", strPrefix); + break; + } + case IT_HLE_BEGIN_OCCLUSION_QUERY: + { + cemuLog_log(LogType::Force, "{} IT_HLE_BEGIN_OCCLUSION_QUERY", strPrefix); + break; + } + case IT_HLE_END_OCCLUSION_QUERY: + { + cemuLog_log(LogType::Force, "{} IT_HLE_END_OCCLUSION_QUERY", strPrefix); + break; + } + case IT_HLE_SET_CB_RETIREMENT_TIMESTAMP: + { + cemuLog_log(LogType::Force, "{} IT_HLE_SET_CB_RETIREMENT_TIMESTAMP", strPrefix); + break; + } + case IT_HLE_BOTTOM_OF_PIPE_CB: + { + cemuLog_log(LogType::Force, "{} IT_HLE_BOTTOM_OF_PIPE_CB", strPrefix); + break; + } + case IT_HLE_SYNC_ASYNC_OPERATIONS: + { + cemuLog_log(LogType::Force, "{} IT_HLE_SYNC_ASYNC_OPERATIONS", strPrefix); + break; + } + default: + cemuLog_log(LogType::Force, "{} Unsupported operation code", strPrefix); + return; + } + } + else if (itHeaderType == 2) + { + // filler packet + } + else if (itHeaderType == 0) + { + uint32 registerBase = (itHeader & 0xFFFF); + uint32 registerCount = ((itHeader >> 16) & 0x3FFF) + 1; + LatteCP_skipWords(registerCount); + cemuLog_log(LogType::Force, "[LatteCP] itType=0 registerBase={:04x}", registerBase); + } + else + { + cemuLog_log(LogType::Force, "Invalid itHeaderType %08x\n", itHeaderType); + return; + } + } +} +#endif \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Core/LatteOverlay.cpp b/src/Cafe/HW/Latte/Core/LatteOverlay.cpp index ff5238d5..238f85e8 100644 --- a/src/Cafe/HW/Latte/Core/LatteOverlay.cpp +++ b/src/Cafe/HW/Latte/Core/LatteOverlay.cpp @@ -26,6 +26,7 @@ struct OverlayStats double fps{}; uint32 draw_calls_per_frame{}; + uint32 fast_draw_calls_per_frame{}; float cpu_usage{}; // cemu cpu usage in % std::vector cpu_per_core; // global cpu usage in % per core uint32 ram_usage{}; // ram usage in MB @@ -86,7 +87,7 @@ void LatteOverlay_renderOverlay(ImVec2& position, ImVec2& pivot, sint32 directio ImGui::Text("FPS: %.2lf", g_state.fps); if (config.overlay.drawcalls) - ImGui::Text("Draws/f: %d", g_state.draw_calls_per_frame); + ImGui::Text("Draws/f: %d (fast: %d)", g_state.draw_calls_per_frame, g_state.fast_draw_calls_per_frame); if (config.overlay.cpu_usage) ImGui::Text("CPU: %.2lf%%", g_state.cpu_usage); @@ -588,13 +589,14 @@ static void UpdateStats_CpuPerCore() } } -void LatteOverlay_updateStats(double fps, sint32 drawcalls) +void LatteOverlay_updateStats(double fps, sint32 drawcalls, sint32 fastDrawcalls) { if (GetConfig().overlay.position == ScreenPosition::kDisabled) return; g_state.fps = fps; g_state.draw_calls_per_frame = drawcalls; + g_state.fast_draw_calls_per_frame = fastDrawcalls; UpdateStats_CemuCpu(); UpdateStats_CpuPerCore(); diff --git a/src/Cafe/HW/Latte/Core/LatteOverlay.h b/src/Cafe/HW/Latte/Core/LatteOverlay.h index e497abb0..824c68b2 100644 --- a/src/Cafe/HW/Latte/Core/LatteOverlay.h +++ b/src/Cafe/HW/Latte/Core/LatteOverlay.h @@ -2,6 +2,6 @@ void LatteOverlay_init(); void LatteOverlay_render(bool pad_view); -void LatteOverlay_updateStats(double fps, sint32 drawcalls); +void LatteOverlay_updateStats(double fps, sint32 drawcalls, sint32 fastDrawcalls); void LatteOverlay_pushNotification(const std::string& text, sint32 duration); \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp b/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp index 6bbc7ea4..f2767446 100644 --- a/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp +++ b/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp @@ -38,6 +38,7 @@ void LattePerformanceMonitor_frameEnd() uint64 indexDataCached = 0; uint32 frameCounter = 0; uint32 drawCallCounter = 0; + uint32 fastDrawCallCounter = 0; uint32 shaderBindCounter = 0; uint32 recompilerLeaveCount = 0; uint32 threadLeaveCount = 0; @@ -53,6 +54,7 @@ void LattePerformanceMonitor_frameEnd() indexDataCached += performanceMonitor.cycle[i].indexDataCached; frameCounter += performanceMonitor.cycle[i].frameCounter; drawCallCounter += performanceMonitor.cycle[i].drawCallCounter; + fastDrawCallCounter += performanceMonitor.cycle[i].fastDrawCallCounter; shaderBindCounter += performanceMonitor.cycle[i].shaderBindCount; recompilerLeaveCount += performanceMonitor.cycle[i].recompilerLeaveCount; threadLeaveCount += performanceMonitor.cycle[i].threadLeaveCount; @@ -75,7 +77,6 @@ void LattePerformanceMonitor_frameEnd() indexDataUploadPerFrame /= 1024ULL; double fps = (double)elapsedFrames2S * 1000.0 / (double)totalElapsedTimeFPS; - uint32 drawCallsPerFrame = drawCallCounter / elapsedFrames; uint32 shaderBindsPerFrame = shaderBindCounter / elapsedFrames; passedCycles = passedCycles * 1000ULL / totalElapsedTime; uint32 rlps = (uint32)((uint64)recompilerLeaveCount * 1000ULL / (uint64)totalElapsedTime); @@ -85,6 +86,7 @@ void LattePerformanceMonitor_frameEnd() // next counter cycle sint32 nextCycleIndex = (performanceMonitor.cycleIndex + 1) % PERFORMANCE_MONITOR_TRACK_CYCLES; performanceMonitor.cycle[nextCycleIndex].drawCallCounter = 0; + performanceMonitor.cycle[nextCycleIndex].fastDrawCallCounter = 0; performanceMonitor.cycle[nextCycleIndex].frameCounter = 0; performanceMonitor.cycle[nextCycleIndex].shaderBindCount = 0; performanceMonitor.cycle[nextCycleIndex].lastCycleCount = PPCInterpreter_getMainCoreCycleCounter(); @@ -104,12 +106,12 @@ void LattePerformanceMonitor_frameEnd() if (isFirstUpdate) { - LatteOverlay_updateStats(0.0, 0); + LatteOverlay_updateStats(0.0, 0, 0); gui_updateWindowTitles(false, false, 0.0); } else { - LatteOverlay_updateStats(fps, drawCallCounter / elapsedFrames); + LatteOverlay_updateStats(fps, drawCallCounter / elapsedFrames, fastDrawCallCounter / elapsedFrames); gui_updateWindowTitles(false, false, fps); } } diff --git a/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.h b/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.h index 77554e80..713e094e 100644 --- a/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.h +++ b/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.h @@ -84,6 +84,7 @@ typedef struct uint32 lastUpdate; uint32 frameCounter; uint32 drawCallCounter; + uint32 fastDrawCallCounter; uint32 shaderBindCount; uint64 vertexDataUploaded; // amount of vertex data uploaded to GPU (bytes) uint64 vertexDataCached; // amount of vertex data reused from GPU cache (bytes) diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index 3a52f641..06015949 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -295,6 +295,34 @@ LatteTextureView* LatteMRT::GetColorAttachmentTexture(uint32 index, bool createN uint32 colorBufferHeight = pitchHeight / colorBufferPitch; uint32 colorBufferWidth = colorBufferPitch; + // colorbuffer width/height has to be padded to 8/32 alignment but the actual resolution might be smaller + // use the scissor box as a clue to figure out the original resolution if possible +#if 0 + uint32 scissorBoxWidth = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_X(); + uint32 scissorBoxHeight = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_Y(); + if (((scissorBoxWidth + 7) & ~7) == colorBufferWidth) + colorBufferWidth = scissorBoxWidth; + if (((colorBufferHeight + 31) & ~31) == colorBufferHeight) + colorBufferHeight = scissorBoxHeight; +#endif + + // log resolution changes if the above heuristic takes effect + // this is useful to find resolutions which need to be updated in gfx pack texture rules +#if 0 + uint32 colorBufferHeight2 = pitchHeight / colorBufferPitch; + static std::unordered_set s_foundColorBufferResMappings; + if (colorBufferPitch != colorBufferWidth || colorBufferHeight != colorBufferHeight2) + { + // only log unique, source and dest resolution. Encode into a key with 16 bits per component + uint64 resHash = (uint64)colorBufferWidth | ((uint64)colorBufferHeight << 16) | ((uint64)colorBufferPitch << 32) | ((uint64)colorBufferHeight2 << 48); + if( !s_foundColorBufferResMappings.contains(resHash) ) + { + s_foundColorBufferResMappings.insert(resHash); + cemuLog_log(LogType::Force, "[COLORBUFFER-DBG] Using res {}x{} instead of {}x{}", colorBufferWidth, colorBufferHeight, colorBufferPitch, colorBufferHeight2); + } + } +#endif + bool colorBufferWasFound = false; sint32 viewFirstMip = 0; // todo diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp index 0483e8ee..a6e865d8 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp @@ -8,10 +8,11 @@ #include "Cafe/HW/Latte/Core/LatteTexture.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" -// #define LOG_READBACK_TIME +//#define LOG_READBACK_TIME struct LatteTextureReadbackQueueEntry { + HRTick initiateTime; uint32 lastUpdateDrawcallIndex; LatteTextureView* textureView; }; @@ -22,12 +23,12 @@ std::queue sTextureActiveReadbackQueue; // readbacks void LatteTextureReadback_StartTransfer(LatteTextureView* textureView) { cemuLog_log(LogType::TextureReadback, "[TextureReadback-Start] PhysAddr {:08x} Res {}x{} Fmt {} Slice {} Mip {}", textureView->baseTexture->physAddress, textureView->baseTexture->width, textureView->baseTexture->height, textureView->baseTexture->format, textureView->firstSlice, textureView->firstMip); + HRTick currentTick = HighResolutionTimer().now().getTick(); // create info entry and store in ordered linked list LatteTextureReadbackInfo* readbackInfo = g_renderer->texture_createReadback(textureView); sTextureActiveReadbackQueue.push(readbackInfo); readbackInfo->StartTransfer(); - //debug_printf("[Tex-Readback] %08x %dx%d TM %d FMT %04x\n", textureView->baseTexture->physAddress, textureView->baseTexture->width, textureView->baseTexture->height, textureView->baseTexture->tileMode, textureView->baseTexture->format); - readbackInfo->transferStartTime = HighResolutionTimer().now().getTick(); + readbackInfo->transferStartTime = currentTick; } /* @@ -41,9 +42,15 @@ bool LatteTextureReadback_Update(bool forceStart) for (size_t i = 0; i < sTextureScheduledReadbacks.size(); i++) { LatteTextureReadbackQueueEntry& entry = sTextureScheduledReadbacks[i]; - uint32 numPassedDrawcalls = LatteGPUState.drawCallCounter - entry.lastUpdateDrawcallIndex; - if (forceStart || numPassedDrawcalls >= 5) + uint32 numElapsedDrawcalls = LatteGPUState.drawCallCounter - entry.lastUpdateDrawcallIndex; + if (forceStart || numElapsedDrawcalls >= 5) { +#ifdef LOG_READBACK_TIME + double elapsedSecondsSinceInitiate = HighResolutionTimer::getTimeDiff(entry.initiateTime, HighResolutionTimer().now().getTick()); + char initiateElapsedTimeStr[32]; + sprintf(initiateElapsedTimeStr, "%.4lfms", elapsedSecondsSinceInitiate); + cemuLog_log(LogType::TextureReadback, "[TextureReadback-Update] Starting transfer for {:08x} after {} elapsed drawcalls. Time since initiate: {} Force-start: {}", entry.textureView->baseTexture->physAddress, numElapsedDrawcalls, initiateElapsedTimeStr, forceStart?"yes":"no"); +#endif LatteTextureReadback_StartTransfer(entry.textureView); // remove element vectorRemoveByIndex(sTextureScheduledReadbacks, i); @@ -91,6 +98,7 @@ void LatteTextureReadback_Initate(LatteTextureView* textureView) } // queue LatteTextureReadbackQueueEntry queueEntry; + queueEntry.initiateTime = HighResolutionTimer().now().getTick(); queueEntry.textureView = textureView; queueEntry.lastUpdateDrawcallIndex = LatteGPUState.drawCallCounter; sTextureScheduledReadbacks.emplace_back(queueEntry); @@ -112,6 +120,14 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) if (!readbackInfo->IsFinished()) { readbackInfo->waitStartTime = HighResolutionTimer().now().getTick(); +#ifdef LOG_READBACK_TIME + if (cemuLog_isLoggingEnabled(LogType::TextureReadback)) + { + double elapsedSecondsTransfer = HighResolutionTimer::getTimeDiff(readbackInfo->transferStartTime, HighResolutionTimer().now().getTick()); + forceLog_printf("[Texture-Readback] Force-finish: %08x Res %4d/%4d TM %d FMT %04x Transfer time so far: %.4lfms", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0); + } +#endif + readbackInfo->forceFinish = true; readbackInfo->ForceFinish(); // rerun logic since ->ForceFinish() can recurively call this function and thus modify the queue continue; @@ -125,10 +141,13 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) } // performance testing #ifdef LOG_READBACK_TIME - HRTick currentTick = HighResolutionTimer().now().getTick(); - double elapsedSecondsTransfer = HighResolutionTimer::getTimeDiff(readbackInfo->transferStartTime, currentTick); - double elapsedSecondsWaiting = HighResolutionTimer::getTimeDiff(readbackInfo->waitStartTime, currentTick); - cemuLog_log(LogType::Force, "[Texture-Readback] {:08x} Res {:4}/{:4} TM {} FMT {:04x} ReadbackLatency: {:6.3}ms WaitTime: {:6.3}ms ForcedWait {}", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0, elapsedSecondsWaiting * 1000.0, forceFinish?"yes":"no"); + if (cemuLog_isLoggingEnabled(LogType::TextureReadback)) + { + HRTick currentTick = HighResolutionTimer().now().getTick(); + double elapsedSecondsTransfer = HighResolutionTimer::getTimeDiff(readbackInfo->transferStartTime, currentTick); + double elapsedSecondsWaiting = HighResolutionTimer::getTimeDiff(readbackInfo->waitStartTime, currentTick); + forceLog_printf("[Texture-Readback] %08x Res %4d/%4d TM %d FMT %04x ReadbackLatency: %6.3lfms WaitTime: %6.3lfms ForcedWait %s", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0, elapsedSecondsWaiting * 1000.0, readbackInfo->forceFinish ? "yes" : "no"); + } #endif uint8* pixelData = readbackInfo->GetData(); LatteTextureLoader_writeReadbackTextureToMemory(&readbackInfo->hostTextureCopy, 0, 0, pixelData); diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h b/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h index 4f3a3069..535e9442 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h @@ -21,6 +21,7 @@ public: HRTick transferStartTime; HRTick waitStartTime; + bool forceFinish{ false }; // set to true if not finished in time for dependent operation // texture info LatteTextureDefinition hostTextureCopy{}; diff --git a/src/Cafe/HW/Latte/ISA/LatteReg.h b/src/Cafe/HW/Latte/ISA/LatteReg.h index 7f0cf7c9..d571dc6e 100644 --- a/src/Cafe/HW/Latte/ISA/LatteReg.h +++ b/src/Cafe/HW/Latte/ISA/LatteReg.h @@ -484,7 +484,7 @@ namespace Latte SQ_TEX_RESOURCE_WORD0_N_GS = 0xE930, SQ_TEX_RESOURCE_WORD_FIRST = SQ_TEX_RESOURCE_WORD0_N_PS, SQ_TEX_RESOURCE_WORD_LAST = (SQ_TEX_RESOURCE_WORD0_N_GS + GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7 - 1), - // there are 54 samplers with 3 registers each. 18 per stage. For stage indices see SAMPLER_BASE_INDEX_* + // there are 54 samplers with 3 registers each. 18 (actually only 16?) per stage. For stage indices see SAMPLER_BASE_INDEX_* SQ_TEX_SAMPLER_WORD0_0 = 0xF000, SQ_TEX_SAMPLER_WORD1_0 = 0xF001, SQ_TEX_SAMPLER_WORD2_0 = 0xF002, diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 7987b20e..c2e0a4f8 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2002,7 +2002,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor occlusionQuery_notifyBeginCommandBuffer(); m_recordedDrawcalls = 0; - m_submitThreshold = 500; // this used to be 750 before 1.25.5, but more frequent submission is actually better for latency + m_submitThreshold = 300; m_submitOnIdle = false; } From 1d398551e25a35054ef51780a60756c3015ec312 Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Tue, 19 Sep 2023 20:43:54 +0100 Subject: [PATCH 034/101] Add DS_Store to gitignore (#969) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e7f104d2..9e9ff7df 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,6 @@ bin/controllerProfiles/* bin/gameProfiles/* bin/graphicPacks/* + +# Ignore Finder view option files created by OS X +.DS_Store \ No newline at end of file From b4aa10bee4759518e05253d57d3029847bfc39b8 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Wed, 20 Sep 2023 19:01:56 +0200 Subject: [PATCH 035/101] Vulkan: Only create imgui renderpass once (#972) --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 61 +++++++++++-------- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 2 +- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index c2e0a4f8..2b8376d5 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1508,34 +1508,37 @@ void VulkanRenderer::DeleteNullObjects() void VulkanRenderer::ImguiInit() { - // TODO: renderpass swapchain format may change between srgb and rgb -> need reinit - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = m_mainSwapchainInfo->m_surfaceFormat.format; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + if (m_imguiRenderPass == VK_NULL_HANDLE) + { + // TODO: renderpass swapchain format may change between srgb and rgb -> need reinit + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = m_mainSwapchainInfo->m_surfaceFormat.format; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - const auto result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_imguiRenderPass); - if (result != VK_SUCCESS) - throw VkException(result, "can't create imgui renderpass"); + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + const auto result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_imguiRenderPass); + if (result != VK_SUCCESS) + throw VkException(result, "can't create imgui renderpass"); + } ImGui_ImplVulkan_InitInfo info{}; info.Instance = m_instance; @@ -1564,6 +1567,12 @@ void VulkanRenderer::Shutdown() Renderer::Shutdown(); SubmitCommandBuffer(); WaitDeviceIdle(); + + if (m_imguiRenderPass != VK_NULL_HANDLE) + { + vkDestroyRenderPass(m_logicalDevice, m_imguiRenderPass, nullptr); + m_imguiRenderPass = VK_NULL_HANDLE; + } } void VulkanRenderer::UnrecoverableError(const char* errMsg) const diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 147b6c15..24008ee3 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -441,7 +441,7 @@ private: bool m_destroyPadSwapchainNextAcquire = false; bool IsSwapchainInfoValid(bool mainWindow) const; - VkRenderPass m_imguiRenderPass = nullptr; + VkRenderPass m_imguiRenderPass = VK_NULL_HANDLE; VkDescriptorPool m_descriptorPool; From 638c4014a1da1e2cff4000f4c6b64c7b75314055 Mon Sep 17 00:00:00 2001 From: Squall Leonhart Date: Sat, 23 Sep 2023 03:20:22 +1000 Subject: [PATCH 036/101] nn_olv: Handle nullptr key in SetSearchKey (#974) --- src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h index e6078a7a..62dad755 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h @@ -531,6 +531,11 @@ namespace nn // SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCwUc static nnResult SetSearchKey(DownloadPostDataListParam* _this, const uint16be* searchKey, uint8 searchKeyIndex) { + if( !searchKey ) + { + memset(&_this->searchKeyArray[searchKeyIndex], 0, sizeof(SearchKey)); + return OLV_RESULT_SUCCESS; + } if (searchKeyIndex >= MAX_NUM_SEARCH_KEY) return OLV_RESULT_INVALID_PARAMETER; memset(&_this->searchKeyArray[searchKeyIndex], 0, sizeof(SearchKey)); @@ -546,6 +551,11 @@ namespace nn // SetSearchKey__Q3_2nn3olv25DownloadPostDataListParamFPCw static nnResult SetSearchKeySingle(DownloadPostDataListParam* _this, const uint16be* searchKey) { + if (searchKey == nullptr) + { + cemuLog_logDebug(LogType::NN_OLV, "DownloadPostDataListParam::SetSearchKeySingle: searchKeySingle is Null\n"); + return OLV_RESULT_INVALID_PARAMETER; + } return SetSearchKey(_this, searchKey, 0); } From 65e5e20afcb015670585699d21518ddd654d0b00 Mon Sep 17 00:00:00 2001 From: Leif Liddy Date: Wed, 27 Sep 2023 00:29:23 +0200 Subject: [PATCH 037/101] BUILD.md: Require libtool and libusb1-devel for Fedora (#979) --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index da6c03ce..35aaffb7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -36,7 +36,7 @@ To compile Cemu, a recent enough compiler and STL with C++20 support is required `sudo pacman -S --needed base-devel clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip` #### For Fedora and derivatives: -`sudo dnf install clang cmake cubeb-devel freeglut-devel git glm-devel gtk3-devel kernel-headers libgcrypt-devel libsecret-devel nasm ninja-build perl-core systemd-devel zlib-devel` +`sudo dnf install clang cmake cubeb-devel freeglut-devel git glm-devel gtk3-devel kernel-headers libgcrypt-devel libsecret-devel libtool libusb1-devel nasm ninja-build perl-core systemd-devel zlib-devel` ### Build Cemu using cmake and clang 1. `git clone --recursive https://github.com/cemu-project/Cemu` From 4d6b72b353595d2a009884e62902d4f2663d5969 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 20 Sep 2023 04:54:36 +0200 Subject: [PATCH 038/101] Latte: Very minor refactor + optimization --- .../Renderer/Vulkan/VulkanRendererCore.cpp | 33 +++++++++---------- src/Common/precompiled.h | 3 ++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index c68c664f..9b47a14b 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -370,8 +370,12 @@ void VulkanRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size) // does nothing since the index buffer memory is coherent } +float s_vkUniformData[512 * 4]; + void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader) { + auto GET_UNIFORM_DATA_PTR = [&](size_t index) { return s_vkUniformData + (index / 4); }; + sint32 shaderAluConst; sint32 shaderUniformRegisterOffset; @@ -390,27 +394,23 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START; break; default: - cemu_assert_debug(false); + UNREACHABLE; } if (shader->resourceMapping.uniformVarsBufferBindingPoint >= 0) { - float uniformData[512 * 4]; - if (shader->uniform.list_ufTexRescale.empty() == false) { for (auto& entry : shader->uniform.list_ufTexRescale) { float* xyScale = LatteTexture_getEffectiveTextureScale(shader->shaderType, entry.texUnit); - float* v = uniformData + (entry.uniformLocation / 4); memcpy(entry.currentValue, xyScale, sizeof(float) * 2); - memcpy(v, xyScale, sizeof(float) * 2); + memcpy(GET_UNIFORM_DATA_PTR(entry.uniformLocation), xyScale, sizeof(float) * 2); } } if (shader->uniform.loc_alphaTestRef >= 0) { - float* v = uniformData + (shader->uniform.loc_alphaTestRef / 4); - v[0] = LatteGPUState.contextNew.SX_ALPHA_REF.get_ALPHA_TEST_REF(); + *GET_UNIFORM_DATA_PTR(shader->uniform.loc_alphaTestRef) = LatteGPUState.contextNew.SX_ALPHA_REF.get_ALPHA_TEST_REF(); } if (shader->uniform.loc_pointSize >= 0) { @@ -418,41 +418,38 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt float pointWidth = (float)pointSizeReg.get_WIDTH() / 8.0f; if (pointWidth == 0.0f) pointWidth = 1.0f / 8.0f; // minimum size - float* v = uniformData + (shader->uniform.loc_pointSize / 4); - v[0] = pointWidth; + *GET_UNIFORM_DATA_PTR(shader->uniform.loc_pointSize) = pointWidth; } if (shader->uniform.loc_remapped >= 0) { - LatteBufferCache_LoadRemappedUniforms(shader, uniformData + (shader->uniform.loc_remapped / 4)); + LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped)); } if (shader->uniform.loc_uniformRegister >= 0) { uint32* uniformRegData = (uint32*)(LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst); - float* v = uniformData + (shader->uniform.loc_uniformRegister / 4); - memcpy(v, uniformRegData, shader->uniform.count_uniformRegister * 16); + memcpy(GET_UNIFORM_DATA_PTR(shader->uniform.loc_uniformRegister), uniformRegData, shader->uniform.count_uniformRegister * 16); } if (shader->uniform.loc_windowSpaceToClipSpaceTransform >= 0) { sint32 viewportWidth; sint32 viewportHeight; LatteRenderTarget_GetCurrentVirtualViewportSize(&viewportWidth, &viewportHeight); // always call after _updateViewport() - float* v = uniformData + (shader->uniform.loc_windowSpaceToClipSpaceTransform / 4); + float* v = GET_UNIFORM_DATA_PTR(shader->uniform.loc_windowSpaceToClipSpaceTransform); v[0] = 2.0f / (float)viewportWidth; v[1] = 2.0f / (float)viewportHeight; } if (shader->uniform.loc_fragCoordScale >= 0) { - float* coordScale = uniformData + (shader->uniform.loc_fragCoordScale / 4); - LatteMRT::GetCurrentFragCoordScale(coordScale); + LatteMRT::GetCurrentFragCoordScale(GET_UNIFORM_DATA_PTR(shader->uniform.loc_fragCoordScale)); } if (shader->uniform.loc_verticesPerInstance >= 0) { - *(int*)(uniformData + (shader->uniform.loc_verticesPerInstance / 4)) = m_streamoutState.verticesPerInstance; + *(int*)(s_vkUniformData + ((size_t)shader->uniform.loc_verticesPerInstance / 4)) = m_streamoutState.verticesPerInstance; for (sint32 b = 0; b < LATTE_NUM_STREAMOUT_BUFFER; b++) { if (shader->uniform.loc_streamoutBufferBase[b] >= 0) { - *(int*)(uniformData + (shader->uniform.loc_streamoutBufferBase[b] / 4)) = m_streamoutState.buffer[b].ringBufferOffset; + *(uint32*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_streamoutBufferBase[b]) = m_streamoutState.buffer[b].ringBufferOffset; } } } @@ -463,7 +460,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt } uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1; const uint32 uniformOffset = m_uniformVarBufferWriteIndex; - memcpy(m_uniformVarBufferPtr + uniformOffset, uniformData, shader->uniform.uniformRangeSize); + memcpy(m_uniformVarBufferPtr + uniformOffset, s_vkUniformData, shader->uniform.uniformRangeSize); m_uniformVarBufferWriteIndex += shader->uniform.uniformRangeSize; m_uniformVarBufferWriteIndex = (m_uniformVarBufferWriteIndex + bufferAlignmentM1) & ~bufferAlignmentM1; // update dynamic offset diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 7152f2c1..580aeb23 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -235,10 +235,13 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor, #if defined(_MSC_VER) #define UNREACHABLE __assume(false) + #define ASSUME(__cond) __assume(__cond) #elif defined(__GNUC__) #define UNREACHABLE __builtin_unreachable() + #define ASSUME(__cond) __attribute__((assume(__cond))) #else #define UNREACHABLE + #define ASSUME(__cond) #endif #if defined(_MSC_VER) From 3e925b77074ec50fe5f6c825d12c56cb5b9d44f5 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 23 Sep 2023 22:53:57 +0200 Subject: [PATCH 039/101] Latte: Bound uniform buffers based on access patterns within the shader --- src/Cafe/HW/Latte/Core/LatteBufferData.cpp | 12 +-- .../LegacyShaderDecompiler/LatteDecompiler.h | 13 ++-- .../LatteDecompilerAnalyzer.cpp | 74 +++++++++---------- .../LatteDecompilerEmitGLSLHeader.hpp | 30 +------- .../LatteDecompilerInternal.h | 70 ++++++++++++++---- .../Renderer/Vulkan/VulkanRendererCore.cpp | 8 +- 6 files changed, 114 insertions(+), 93 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteBufferData.cpp b/src/Cafe/HW/Latte/Core/LatteBufferData.cpp index d31a8651..85d4cdf7 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferData.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferData.cpp @@ -132,22 +132,18 @@ void LatteBufferCache_syncGPUUniformBuffers(LatteDecompilerShader* shader, const { if (shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK) { - // use full uniform buffers - for (sint32 t = 0; t < shader->uniformBufferListCount; t++) + for(const auto& buf : shader->list_quickBufferList) { - sint32 i = shader->uniformBufferList[t]; + sint32 i = buf.index; MPTR physicalAddr = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 0]; uint32 uniformSize = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 1] + 1; - - if (physicalAddr == MPTR_NULL) + if (physicalAddr == MPTR_NULL) [[unlikely]] { - // no data g_renderer->buffer_bindUniformBuffer(shaderType, i, 0, 0); continue; } - + uniformSize = std::min(uniformSize, buf.size); uint32 bindOffset = LatteBufferCache_retrieveDataInCache(physicalAddr, uniformSize); - g_renderer->buffer_bindUniformBuffer(shaderType, i, bindOffset, uniformSize); } } diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h index f7a0ea5f..92777844 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h @@ -1,6 +1,7 @@ #pragma once #include "Cafe/HW/Latte/Core/LatteConst.h" #include "Cafe/HW/Latte/Renderer/RendererShader.h" +#include namespace LatteDecompiler { @@ -158,11 +159,13 @@ struct LatteDecompilerShader struct LatteFetchShader* compatibleFetchShader{}; // error tracking bool hasError{false}; // if set, the shader cannot be used - // optimized access / iteration - // list of uniform buffers used - uint8 uniformBufferList[LATTE_NUM_MAX_UNIFORM_BUFFERS]; - uint8 uniformBufferListCount{ 0 }; - // list of used texture units (faster access than iterating textureUnitMask) + // compact resource lists for optimized access + struct QuickBufferEntry + { + uint8 index; + uint16 size; + }; + boost::container::static_vector list_quickBufferList; uint8 textureUnitList[LATTE_NUM_MAX_TEX_UNITS]; uint8 textureUnitListCount{ 0 }; // input diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp index e482be2c..7285d312 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp @@ -230,47 +230,39 @@ void LatteDecompiler_analyzeALUClause(LatteDecompilerShaderContext* shaderContex // check input for uniform access if( aluInstruction.sourceOperand[f].sel == 0xFFFFFFFF ) continue; // source operand not set/used + // about uniform register and buffer access tracking: + // for absolute indices we can determine a maximum size that is accessed + // relative accesses are tricky because the upper bound of accessed indices is unknown + // worst case we have to load the full file (256 * 16 byte entries) or for buffers an arbitrary upper bound (64KB in our case) if( GPU7_ALU_SRC_IS_CFILE(aluInstruction.sourceOperand[f].sel) ) { - // uniform register access - - // relative register file accesses are tricky because the range of possible indices is unknown - // worst case we have to load the full file (256 * 16 byte entries) - // by tracking the accessed base indices the shader analyzer can determine bounds for the potentially accessed ranges - - shaderContext->analyzer.uniformRegisterAccess = true; if (aluInstruction.sourceOperand[f].rel) { - shaderContext->analyzer.uniformRegisterDynamicAccess = true; - shaderContext->analyzer.uniformRegisterAccessIndices.emplace_back(GPU7_ALU_SRC_GET_CFILE_INDEX(aluInstruction.sourceOperand[f].sel), true); + shaderContext->analyzer.uniformRegisterAccessTracker.TrackAccess(GPU7_ALU_SRC_GET_CFILE_INDEX(aluInstruction.sourceOperand[f].sel), true); } else { _remapUniformAccess(shaderContext, true, 0, GPU7_ALU_SRC_GET_CFILE_INDEX(aluInstruction.sourceOperand[f].sel)); - shaderContext->analyzer.uniformRegisterAccessIndices.emplace_back(GPU7_ALU_SRC_GET_CFILE_INDEX(aluInstruction.sourceOperand[f].sel), false); + shaderContext->analyzer.uniformRegisterAccessTracker.TrackAccess(GPU7_ALU_SRC_GET_CFILE_INDEX(aluInstruction.sourceOperand[f].sel), false); } } else if( GPU7_ALU_SRC_IS_CBANK0(aluInstruction.sourceOperand[f].sel) ) { // uniform bank 0 (uniform buffer with index cfInstruction->cBank0Index) uint32 uniformBufferIndex = cfInstruction->cBank0Index; - if( uniformBufferIndex >= LATTE_NUM_MAX_UNIFORM_BUFFERS) - debugBreakpoint(); - shaderContext->analyzer.uniformBufferAccessMask |= (1<analyzer.uniformBufferDynamicAccessMask |= (1<cBank0AddrBase); + cemu_assert(uniformBufferIndex < LATTE_NUM_MAX_UNIFORM_BUFFERS); + uint32 offset = GPU7_ALU_SRC_GET_CBANK0_INDEX(aluInstruction.sourceOperand[f].sel)+cfInstruction->cBank0AddrBase; + _remapUniformAccess(shaderContext, false, uniformBufferIndex, offset); + shaderContext->analyzer.uniformBufferAccessTracker[uniformBufferIndex].TrackAccess(offset, aluInstruction.sourceOperand[f].rel); } else if( GPU7_ALU_SRC_IS_CBANK1(aluInstruction.sourceOperand[f].sel) ) { // uniform bank 1 (uniform buffer with index cfInstruction->cBank1Index) uint32 uniformBufferIndex = cfInstruction->cBank1Index; - if( uniformBufferIndex >= LATTE_NUM_MAX_UNIFORM_BUFFERS) - debugBreakpoint(); - shaderContext->analyzer.uniformBufferAccessMask |= (1<analyzer.uniformBufferDynamicAccessMask |= (1<cBank1AddrBase); + cemu_assert(uniformBufferIndex < LATTE_NUM_MAX_UNIFORM_BUFFERS); + uint32 offset = GPU7_ALU_SRC_GET_CBANK1_INDEX(aluInstruction.sourceOperand[f].sel)+cfInstruction->cBank1AddrBase; + _remapUniformAccess(shaderContext, false, uniformBufferIndex, offset); + shaderContext->analyzer.uniformBufferAccessTracker[uniformBufferIndex].TrackAccess(offset, aluInstruction.sourceOperand[f].rel); } else if( GPU7_ALU_SRC_IS_GPR(aluInstruction.sourceOperand[f].sel) ) { @@ -360,8 +352,7 @@ void LatteDecompiler_analyzeTEXClause(LatteDecompilerShaderContext* shaderContex if( texInstruction.textureFetch.textureIndex >= 0x80 && texInstruction.textureFetch.textureIndex <= 0x8F ) { uint32 uniformBufferIndex = texInstruction.textureFetch.textureIndex - 0x80; - shaderContext->analyzer.uniformBufferAccessMask |= (1<analyzer.uniformBufferDynamicAccessMask |= (1<analyzer.uniformBufferAccessTracker[uniformBufferIndex].TrackAccess(0, true); } else if( texInstruction.textureFetch.textureIndex == 0x9F && shader->shaderType == LatteConst::ShaderType::Geometry ) { @@ -576,7 +567,7 @@ namespace LatteDecompiler // for Vulkan we use consecutive indices for (uint32 i = 0; i < LATTE_NUM_MAX_UNIFORM_BUFFERS; i++) { - if ((decompilerContext->analyzer.uniformBufferAccessMask&(1 << i)) == 0) + if (!decompilerContext->analyzer.uniformBufferAccessTracker[i].HasAccess()) continue; sint32 uniformBindingPoint = i; if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry) @@ -592,7 +583,7 @@ namespace LatteDecompiler // for OpenGL we use the relative buffer index for (uint32 i = 0; i < LATTE_NUM_MAX_UNIFORM_BUFFERS; i++) { - if ((decompilerContext->analyzer.uniformBufferAccessMask&(1 << i)) == 0) + if (!decompilerContext->analyzer.uniformBufferAccessTracker[i].HasAccess()) continue; sint32 uniformBindingPoint = i; if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry) @@ -765,17 +756,24 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD LatteDecompiler_analyzeSubroutine(shaderContext, subroutineAddr); } // decide which uniform mode to use - if(shaderContext->analyzer.uniformBufferAccessMask != 0 && shaderContext->analyzer.uniformRegisterAccess ) - debugBreakpoint(); // not allowed - if(shaderContext->analyzer.uniformBufferDynamicAccessMask != 0 ) + bool hasAnyDynamicBufferAccess = false; + bool hasAnyBufferAccess = false; + for(auto& it : shaderContext->analyzer.uniformBufferAccessTracker) + { + if( it.HasRelativeAccess() ) + hasAnyDynamicBufferAccess = true; + if( it.HasAccess() ) + hasAnyBufferAccess = true; + } + if (hasAnyDynamicBufferAccess) { shader->uniformMode = LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK; } - else if(shaderContext->analyzer.uniformRegisterDynamicAccess ) + else if(shaderContext->analyzer.uniformRegisterAccessTracker.HasRelativeAccess() ) { shader->uniformMode = LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE; } - else if(shaderContext->analyzer.uniformBufferAccessMask != 0 || shaderContext->analyzer.uniformRegisterAccess != 0 ) + else if(hasAnyBufferAccess || shaderContext->analyzer.uniformRegisterAccessTracker.HasAccess() ) { shader->uniformMode = LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED; } @@ -783,16 +781,18 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD { shader->uniformMode = LATTE_DECOMPILER_UNIFORM_MODE_NONE; } - // generate list of uniform buffers based on uniformBufferAccessMask (for faster access) - shader->uniformBufferListCount = 0; + // generate compact list of uniform buffers (for faster access) + cemu_assert_debug(shader->list_quickBufferList.empty()); for (uint32 i = 0; i < LATTE_NUM_MAX_UNIFORM_BUFFERS; i++) { - if( !HAS_FLAG(shaderContext->analyzer.uniformBufferAccessMask, (1<analyzer.uniformBufferAccessTracker[i].HasAccess() ) continue; - shader->uniformBufferList[shader->uniformBufferListCount] = i; - shader->uniformBufferListCount++; + LatteDecompilerShader::QuickBufferEntry entry; + entry.index = i; + entry.size = shaderContext->analyzer.uniformBufferAccessTracker[i].DetermineSize(LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE) * 16; + shader->list_quickBufferList.push_back(entry); } - // get dimension of each used textures + // get dimension of each used texture _LatteRegisterSetTextureUnit* texRegs = nullptr; if( shader->shaderType == LatteConst::ShaderType::Vertex ) texRegs = shaderContext->contextRegistersNew->SQ_TEX_START_VS; diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp index 0bd4eb6f..21cae093 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp @@ -37,36 +37,14 @@ namespace LatteDecompiler } else if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE) { - // here we try to predict the accessed range so we dont have to upload the whole register file - // we assume that if there is a fixed-index access on an index higher than a relative access, it bounds the prior relative access - sint16 highestAccessIndex = -1; - bool highestAccessIndexIsRel = false; - for(auto& accessItr : decompilerContext->analyzer.uniformRegisterAccessIndices) - { - if (accessItr.index > highestAccessIndex || (accessItr.index == highestAccessIndex && accessItr.isRelative && !highestAccessIndexIsRel)) - { - highestAccessIndex = accessItr.index; - highestAccessIndexIsRel = accessItr.isRelative; - } - } - if (highestAccessIndex < 0) - highestAccessIndex = 0; - - uint32 cfileSize; - if (highestAccessIndexIsRel) - cfileSize = 256; - else - cfileSize = highestAccessIndex + 1; - - // full uniform register file has to be present + uint32 cfileSize = decompilerContext->analyzer.uniformRegisterAccessTracker.DetermineSize(256); + // full or partial uniform register file has to be present if (shaderType == LatteConst::ShaderType::Vertex) shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterVS[{}];" _CRLF, cfileSize); else if (shaderType == LatteConst::ShaderType::Pixel) shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterPS[{}];" _CRLF, cfileSize); else if (shaderType == LatteConst::ShaderType::Geometry) shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterGS[{}];" _CRLF, cfileSize); - else - debugBreakpoint(); uniformOffsets.offset_uniformRegister = uniformCurrentOffset; uniformOffsets.count_uniformRegister = cfileSize; uniformCurrentOffset += 16 * cfileSize; @@ -168,7 +146,7 @@ namespace LatteDecompiler { for (uint32 i = 0; i < LATTE_NUM_MAX_UNIFORM_BUFFERS; i++) { - if ((decompilerContext->analyzer.uniformBufferAccessMask&(1 << i)) == 0) + if (!decompilerContext->analyzer.uniformBufferAccessTracker[i].HasAccess()) continue; cemu_assert_debug(decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i] >= 0); @@ -178,7 +156,7 @@ namespace LatteDecompiler shaderSrc->addFmt("uniform {}{}" _CRLF, _getShaderUniformBlockInterfaceName(decompilerContext->shaderType), i); shaderSrc->add("{" _CRLF); - shaderSrc->addFmt("vec4 {}{}[{}];" _CRLF, _getShaderUniformBlockVariableName(decompilerContext->shaderType), i, LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE); + shaderSrc->addFmt("vec4 {}{}[{}];" _CRLF, _getShaderUniformBlockVariableName(decompilerContext->shaderType), i, decompilerContext->analyzer.uniformBufferAccessTracker[i].DetermineSize(LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE)); shaderSrc->add("};" _CRLF _CRLF); shaderSrc->add(_CRLF); } diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h index 53fb61ef..54112ddf 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h @@ -125,19 +125,66 @@ struct LatteDecompilerCFInstruction LatteDecompilerCFInstruction& operator=(LatteDecompilerCFInstruction&& mE) = default; }; -struct LatteDecompilerCFileAccess -{ - LatteDecompilerCFileAccess(uint8 index, bool isRelative) : index(index), isRelative(isRelative) {}; - uint8 index; - bool isRelative; -}; - struct LatteDecompilerSubroutineInfo { uint32 cfAddr; std::vector instructions; }; +// helper struct to track the highest accessed offset within a buffer +struct LatteDecompilerBufferAccessTracker +{ + bool hasStaticIndexAccess{false}; + bool hasDynamicIndexAccess{false}; + sint32 highestAccessDynamicIndex{0}; + sint32 highestAccessStaticIndex{0}; + + // track access, index is the array index and not a byte offset + void TrackAccess(sint32 index, bool isDynamicIndex) + { + if (isDynamicIndex) + { + hasDynamicIndexAccess = true; + if (index > highestAccessDynamicIndex) + highestAccessDynamicIndex = index; + } + else + { + hasStaticIndexAccess = true; + if (index > highestAccessStaticIndex) + highestAccessStaticIndex = index; + } + } + + sint32 DetermineSize(sint32 maximumSize) const + { + // here we try to predict the accessed range so we dont have to upload the whole buffer + // potential risky optimization: assume that if there is a fixed-index access on an index higher than any other non-zero relative accesses, it bounds the prior relative access + sint32 highestAccessIndex = -1; + if(hasStaticIndexAccess) + { + highestAccessIndex = highestAccessStaticIndex; + } + if(hasDynamicIndexAccess) + { + return maximumSize; // dynamic index exists and no bound can be determined + } + if (highestAccessIndex < 0) + return 1; // no access at all? But avoid zero as a size + return highestAccessIndex + 1; + } + + bool HasAccess() const + { + return hasStaticIndexAccess || hasDynamicIndexAccess; + } + + bool HasRelativeAccess() const + { + return hasDynamicIndexAccess; + } +}; + struct LatteDecompilerShaderContext { LatteDecompilerOutput_t* output; @@ -174,12 +221,9 @@ struct LatteDecompilerShaderContext bool isPointsPrimitive{}; // set if current render primitive is points bool outputPointSize{}; // set if the current shader should output the point size std::bitset<256> inputAttributSemanticMask; // one set bit for every used semanticId - todo: there are only 128 bit available semantic locations? The MSB has special meaning? - // uniform - bool uniformRegisterAccess; // set to true if cfile (uniform register) is accessed - bool uniformRegisterDynamicAccess; // set to true if cfile (uniform register) is accessed with a dynamic index - uint32 uniformBufferAccessMask; // 1 bit per buffer, set if the uniform buffer is accessed - uint32 uniformBufferDynamicAccessMask; // 1 bit per buffer, set if the uniform buffer is accessed by dynamic index - std::vector uniformRegisterAccessIndices; + // uniforms + LatteDecompilerBufferAccessTracker uniformRegisterAccessTracker; + LatteDecompilerBufferAccessTracker uniformBufferAccessTracker[LATTE_NUM_MAX_UNIFORM_BUFFERS]; // ssbo bool hasSSBORead; // shader has instructions that read from SSBO bool hasSSBOWrite; // shader has instructions that write to SSBO diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 9b47a14b..5bffcc68 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -1591,10 +1591,9 @@ void VulkanRenderer::draw_updateUniformBuffersDirectAccess(LatteDecompilerShader { if (shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK) { - // use full uniform buffers - for (sint32 t = 0; t < shader->uniformBufferListCount; t++) + for(const auto& buf : shader->list_quickBufferList) { - sint32 i = shader->uniformBufferList[t]; + sint32 i = buf.index; MPTR physicalAddr = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 0]; uint32 uniformSize = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 1] + 1; @@ -1603,6 +1602,7 @@ void VulkanRenderer::draw_updateUniformBuffersDirectAccess(LatteDecompilerShader cemu_assert_unimplemented(); continue; } + uniformSize = std::min(uniformSize, buf.size); cemu_assert_debug(physicalAddr < 0x50000000); @@ -1621,7 +1621,7 @@ void VulkanRenderer::draw_updateUniformBuffersDirectAccess(LatteDecompilerShader dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; break; default: - cemu_assert_debug(false); + UNREACHABLE; } } } From f9f62069298f201448291a6037396618294cfe62 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:11:57 +0200 Subject: [PATCH 040/101] Vulkan: Add profiler for Vulkan API CPU cost Disabled by default. Set VULKAN_API_CPU_BENCHMARK to 1 to enable --- .../HW/Latte/Core/LatteTextureReadback.cpp | 12 ++- .../HW/Latte/Renderer/Vulkan/VulkanAPI.cpp | 82 ++++++++++++++++++- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h | 9 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 5 ++ .../Renderer/Vulkan/VulkanRendererCore.cpp | 4 +- 5 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp index a6e865d8..8df5dcea 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp @@ -8,7 +8,7 @@ #include "Cafe/HW/Latte/Core/LatteTexture.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" -//#define LOG_READBACK_TIME +#define LOG_READBACK_TIME struct LatteTextureReadbackQueueEntry { @@ -47,9 +47,7 @@ bool LatteTextureReadback_Update(bool forceStart) { #ifdef LOG_READBACK_TIME double elapsedSecondsSinceInitiate = HighResolutionTimer::getTimeDiff(entry.initiateTime, HighResolutionTimer().now().getTick()); - char initiateElapsedTimeStr[32]; - sprintf(initiateElapsedTimeStr, "%.4lfms", elapsedSecondsSinceInitiate); - cemuLog_log(LogType::TextureReadback, "[TextureReadback-Update] Starting transfer for {:08x} after {} elapsed drawcalls. Time since initiate: {} Force-start: {}", entry.textureView->baseTexture->physAddress, numElapsedDrawcalls, initiateElapsedTimeStr, forceStart?"yes":"no"); + cemuLog_log(LogType::TextureReadback, "[TextureReadback-Update] Starting transfer for {:08x} after {} elapsed drawcalls. Time since initiate: {:.4} Force-start: {}", entry.textureView->baseTexture->physAddress, numElapsedDrawcalls, elapsedSecondsSinceInitiate, forceStart?"yes":"no"); #endif LatteTextureReadback_StartTransfer(entry.textureView); // remove element @@ -83,7 +81,7 @@ void LatteTextureReadback_Initate(LatteTextureView* textureView) // currently we don't support readback for resized textures if (textureView->baseTexture->overwriteInfo.hasResolutionOverwrite) { - cemuLog_log(LogType::Force, "_initate(): Readback is not supported for textures with modified resolution"); + cemuLog_log(LogType::Force, "Texture readback is not supported for textures with modified resolution. Texture: {:08x} {}x{}", textureView->baseTexture->physAddress, textureView->baseTexture->width, textureView->baseTexture->height); return; } // check if texture isn't already queued for transfer @@ -124,7 +122,7 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) if (cemuLog_isLoggingEnabled(LogType::TextureReadback)) { double elapsedSecondsTransfer = HighResolutionTimer::getTimeDiff(readbackInfo->transferStartTime, HighResolutionTimer().now().getTick()); - forceLog_printf("[Texture-Readback] Force-finish: %08x Res %4d/%4d TM %d FMT %04x Transfer time so far: %.4lfms", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0); + cemuLog_log(LogType::TextureReadback, "[Texture-Readback] Force-finish: {:08x} Res {:}/{:} TM {:} FMT {:04x} Transfer time so far: {:.4}ms", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0); } #endif readbackInfo->forceFinish = true; @@ -146,7 +144,7 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) HRTick currentTick = HighResolutionTimer().now().getTick(); double elapsedSecondsTransfer = HighResolutionTimer::getTimeDiff(readbackInfo->transferStartTime, currentTick); double elapsedSecondsWaiting = HighResolutionTimer::getTimeDiff(readbackInfo->waitStartTime, currentTick); - forceLog_printf("[Texture-Readback] %08x Res %4d/%4d TM %d FMT %04x ReadbackLatency: %6.3lfms WaitTime: %6.3lfms ForcedWait %s", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0, elapsedSecondsWaiting * 1000.0, readbackInfo->forceFinish ? "yes" : "no"); + cemuLog_log(LogType::TextureReadback, "[Texture-Readback] {:08x} Res {}/{} TM {} FMT {:04x} ReadbackLatency: {:6.3}ms WaitTime: {:6.3}ms ForcedWait {}", readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.tileMode, (uint32)readbackInfo->hostTextureCopy.format, elapsedSecondsTransfer * 1000.0, elapsedSecondsWaiting * 1000.0, readbackInfo->forceFinish ? "yes" : "no"); } #endif uint8* pixelData = readbackInfo->GetData(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp index d7d139be..ad32b541 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp @@ -1,13 +1,81 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" #define VKFUNC_DEFINE #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" +#include // for std::iota #if BOOST_OS_LINUX || BOOST_OS_MACOS #include #endif +#define VULKAN_API_CPU_BENCHMARK 0 // if 1, Cemu will log the CPU time spent per Vulkan API function + bool g_vulkan_available = false; +#if VULKAN_API_CPU_BENCHMARK != 0 +uint64 s_vulkanBenchmarkLastResultsTime = 0; + +struct VulkanBenchmarkFuncInfo +{ + std::string funcName; + uint64 cycles; + uint32 numCalls; +}; + +std::vector s_vulkanBenchmarkFuncs; + +template +auto VkWrapperFuncGenTest(TRet (*func)(Args...), const char* name) +{ + static VulkanBenchmarkFuncInfo _FuncInfo; + static auto _FuncPtrCopy = func; + TRet (*newFunc)(Args...); + if constexpr(std::is_void_v) + { + newFunc = +[](Args... args) { uint64 t = __rdtsc(); _mm_mfence(); _FuncPtrCopy(args...); _mm_mfence(); _FuncInfo.cycles += (__rdtsc() - t); _FuncInfo.numCalls++; }; + } + else + newFunc = +[](Args... args) -> TRet { uint64 t = __rdtsc(); _mm_mfence(); TRet r = _FuncPtrCopy(args...); _mm_mfence(); _FuncInfo.cycles += (__rdtsc() - t); _FuncInfo.numCalls++; return r; }; + if(func && func != newFunc) + _FuncPtrCopy = func; + if(_FuncInfo.funcName.empty()) + { + _FuncInfo = {.funcName = name, .cycles = 0, .numCalls = 0}; + s_vulkanBenchmarkFuncs.emplace_back(&_FuncInfo); + } + return newFunc; +}; +#endif + +// called when a TV SwapBuffers is called +void VulkanBenchmarkPrintResults() +{ +#if VULKAN_API_CPU_BENCHMARK != 0 + // note: This could be done by hooking vk present functions + uint64 currentCycle = __rdtsc(); + uint64 elapsedCycles = currentCycle - s_vulkanBenchmarkLastResultsTime; + s_vulkanBenchmarkLastResultsTime = currentCycle; + double elapsedCyclesDbl = (double)elapsedCycles; + cemuLog_log(LogType::Force, "--- Vulkan API CPU benchmark ---"); + cemuLog_log(LogType::Force, "Elapsed cycles this frame: {:} | Current cycle {:} | NumFunc {:}", elapsedCycles, currentCycle, s_vulkanBenchmarkFuncs.size()); + + std::vector sortedIndices(s_vulkanBenchmarkFuncs.size()); + std::iota(sortedIndices.begin(), sortedIndices.end(), 0); + std::sort(sortedIndices.begin(), sortedIndices.end(), + [](int32_t a, int32_t b) { + return s_vulkanBenchmarkFuncs[a]->cycles > s_vulkanBenchmarkFuncs[b]->cycles; + }); + for (sint32 idx : sortedIndices) + { + auto& func = s_vulkanBenchmarkFuncs[idx]; + if(func->cycles == 0) + return; + cemuLog_log(LogType::Force, "{}: {} cycles ({:.4}%) {} calls", func->funcName.c_str(), func->cycles, ((double)func->cycles / elapsedCyclesDbl) * 100.0, func->numCalls); + func->cycles = 0; + func->numCalls = 0; + } +#endif +} + #if BOOST_OS_WINDOWS bool InitializeGlobalVulkan() @@ -57,7 +125,12 @@ bool InitializeDeviceVulkan(VkDevice device) #define VKFUNC_DEVICE_INIT #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" - + +#if VULKAN_API_CPU_BENCHMARK != 0 + #define VKFUNC_DEFINE_CUSTOM(__func) __func = VkWrapperFuncGenTest(__func, #__func) + #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" +#endif + return true; } @@ -121,7 +194,12 @@ bool InitializeDeviceVulkan(VkDevice device) #define VKFUNC_DEVICE_INIT #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" - + +#if VULKAN_API_CPU_BENCHMARK != 0 + #define VKFUNC_DEFINE_CUSTOM(__func) __func = VkWrapperFuncGenTest(__func, #__func) + #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" +#endif + return true; } diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index de4f1bb8..0489bb4e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -14,7 +14,11 @@ extern bool g_vulkan_available; #endif -#ifdef VKFUNC_DEFINE +#ifdef VKFUNC_DEFINE_CUSTOM + #define VKFUNC(__FUNC__) VKFUNC_DEFINE_CUSTOM(__FUNC__) + #define VKFUNC_INSTANCE(__FUNC__) VKFUNC_DEFINE_CUSTOM(__FUNC__) + #define VKFUNC_DEVICE(__FUNC__) VKFUNC_DEFINE_CUSTOM(__FUNC__) +#elif defined(VKFUNC_DEFINE) #define VKFUNC(__FUNC__) NOEXPORT PFN_##__FUNC__ __FUNC__ = nullptr #define VKFUNC_INSTANCE(__FUNC__) NOEXPORT PFN_##__FUNC__ __FUNC__ = nullptr #define VKFUNC_DEVICE(__FUNC__) NOEXPORT PFN_##__FUNC__ __FUNC__ = nullptr @@ -238,4 +242,5 @@ VKFUNC_DEVICE(vkDestroyDescriptorSetLayout); #undef VKFUNC #undef VKFUNC_INSTANCE -#undef VKFUNC_DEVICE \ No newline at end of file +#undef VKFUNC_DEVICE +#undef VKFUNC_DEFINE_CUSTOM diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 2b8376d5..d084a399 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2768,6 +2768,8 @@ void VulkanRenderer::NotifyLatteCommandProcessorIdle() SubmitCommandBuffer(); } +void VulkanBenchmarkPrintResults(); + void VulkanRenderer::SwapBuffers(bool swapTV, bool swapDRC) { SubmitCommandBuffer(); @@ -2777,6 +2779,9 @@ void VulkanRenderer::SwapBuffers(bool swapTV, bool swapDRC) if (swapDRC && IsSwapchainInfoValid(false)) SwapBuffer(false); + + if(swapTV) + VulkanBenchmarkPrintResults(); } void VulkanRenderer::ClearColorbuffer(bool padView) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 5bffcc68..8b0e3b63 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -1574,7 +1574,7 @@ void VulkanRenderer::draw_updateVertexBuffersDirectAccess() uint32 bufferSize = LatteGPUState.contextRegister[bufferBaseRegisterIndex + 1] + 1; uint32 bufferStride = (LatteGPUState.contextRegister[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF; - if (bufferAddress == MPTR_NULL) + if (bufferAddress == MPTR_NULL) [[unlikely]] { bufferAddress = 0x10000000; } @@ -1597,7 +1597,7 @@ void VulkanRenderer::draw_updateUniformBuffersDirectAccess(LatteDecompilerShader MPTR physicalAddr = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 0]; uint32 uniformSize = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 1] + 1; - if (physicalAddr == MPTR_NULL) + if (physicalAddr == MPTR_NULL) [[unlikely]] { cemu_assert_unimplemented(); continue; From 5ad57bb0c9f6d0966d8ff04696aa16ba384c3ed0 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:05:40 +0200 Subject: [PATCH 041/101] Add support for games in NUS format (.app) Requires title.tmd and title.tik in same directory --- src/Cafe/OS/RPL/rpl.cpp | 7 ------- src/Cafe/TitleList/TitleInfo.cpp | 23 ++++++++++++++++++----- src/Cafe/TitleList/TitleInfo.h | 1 + src/Cafe/TitleList/TitleList.cpp | 21 ++++++++++++++------- src/Common/precompiled.h | 8 ++++++++ src/gui/MainWindow.cpp | 4 +++- src/gui/components/wxTitleManagerList.cpp | 13 +++++++++---- src/gui/components/wxTitleManagerList.h | 1 + 8 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index 48c7acc4..0e6d153f 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -78,13 +78,6 @@ struct RPLRegionMappingTable void RPLLoader_UnloadModule(RPLModule* rpl); void RPLLoader_RemoveDependency(const char* name); -char _ansiToLower(char c) -{ - if (c >= 'A' && c <= 'Z') - c -= ('A' - 'a'); - return c; -} - uint8* RPLLoader_AllocateTrampolineCodeSpace(RPLModule* rplLoaderContext, sint32 size) { if (rplLoaderContext) diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index 8bbb940d..867e0a7f 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -99,6 +99,7 @@ TitleInfo::TitleInfo(const TitleInfo::CachedInfo& cachedInfo) if (cachedInfo.titleDataFormat != TitleDataFormat::HOST_FS && cachedInfo.titleDataFormat != TitleDataFormat::WIIU_ARCHIVE && cachedInfo.titleDataFormat != TitleDataFormat::WUD && + cachedInfo.titleDataFormat != TitleDataFormat::NUS && cachedInfo.titleDataFormat != TitleDataFormat::INVALID_STRUCTURE) return; if (cachedInfo.path.empty()) @@ -197,13 +198,19 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF } } else if (boost::iends_with(filenameStr, ".wud") || - boost::iends_with(filenameStr, ".wux") || - boost::iends_with(filenameStr, ".iso")) + boost::iends_with(filenameStr, ".wux") || + boost::iends_with(filenameStr, ".iso")) { formatOut = TitleDataFormat::WUD; pathOut = path; return true; } + else if (boost::iequals(filenameStr, "title.tmd")) + { + formatOut = TitleDataFormat::NUS; + pathOut = path; + return true; + } else if (boost::iends_with(filenameStr, ".wua")) { formatOut = TitleDataFormat::WIIU_ARCHIVE; @@ -378,12 +385,15 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder, return false; } } - else if (m_titleFormat == TitleDataFormat::WUD) + else if (m_titleFormat == TitleDataFormat::WUD || m_titleFormat == TitleDataFormat::NUS) { if (m_mountpoints.empty()) { cemu_assert_debug(!m_wudVolume); - m_wudVolume = FSTVolume::OpenFromDiscImage(m_fullPath); + if(m_titleFormat == TitleDataFormat::WUD) + m_wudVolume = FSTVolume::OpenFromDiscImage(m_fullPath); // open wud/wux + else + m_wudVolume = FSTVolume::OpenFromContentFolder(m_fullPath.parent_path()); // open from .app files directory, the path points to /title.tmd } if (!m_wudVolume) return false; @@ -433,7 +443,7 @@ void TitleInfo::Unmount(std::string_view virtualPath) { if (m_wudVolume) { - cemu_assert_debug(m_titleFormat == TitleDataFormat::WUD); + cemu_assert_debug(m_titleFormat == TitleDataFormat::WUD || m_titleFormat == TitleDataFormat::NUS); delete m_wudVolume; m_wudVolume = nullptr; } @@ -664,6 +674,9 @@ std::string TitleInfo::GetPrintPath() const case TitleDataFormat::WUD: tmp.append(" [WUD]"); break; + case TitleDataFormat::NUS: + tmp.append(" [NUS]"); + break; case TitleDataFormat::WIIU_ARCHIVE: tmp.append(" [WUA]"); break; diff --git a/src/Cafe/TitleList/TitleInfo.h b/src/Cafe/TitleList/TitleInfo.h index da430adc..536e9ccb 100644 --- a/src/Cafe/TitleList/TitleInfo.h +++ b/src/Cafe/TitleList/TitleInfo.h @@ -60,6 +60,7 @@ public: HOST_FS = 1, // host filesystem directory (fullPath points to root with content/code/meta subfolders) WUD = 2, // WUD or WUX WIIU_ARCHIVE = 3, // Wii U compressed single-file archive (.wua) + NUS = 4, // NUS format. Directory with .app files, title.tik and title.tmd // error INVALID_STRUCTURE = 0, }; diff --git a/src/Cafe/TitleList/TitleList.cpp b/src/Cafe/TitleList/TitleList.cpp index 2e50cbf9..03fd0855 100644 --- a/src/Cafe/TitleList/TitleList.cpp +++ b/src/Cafe/TitleList/TitleList.cpp @@ -324,17 +324,25 @@ bool CafeTitleList::RefreshWorkerThread() return true; } -bool _IsKnownFileExtension(std::string fileExtension) +bool _IsKnownFileNameOrExtension(const fs::path& path) { + std::string fileExtension = _pathToUtf8(path.extension()); for (auto& it : fileExtension) - if (it >= 'A' && it <= 'Z') - it -= ('A' - 'a'); + it = _ansiToLower(it); + if(fileExtension == ".tmd") + { + // must be "title.tmd" + std::string fileName = _pathToUtf8(path.filename()); + for (auto& it : fileName) + it = _ansiToLower(it); + return fileName == "title.tmd"; + } return fileExtension == ".wud" || fileExtension == ".wux" || fileExtension == ".iso" || fileExtension == ".wua"; - // note: To detect extracted titles with RPX we use the content/code/meta folder structure + // note: To detect extracted titles with RPX we rely on the presence of the content,code,meta directory structure } void CafeTitleList::ScanGamePath(const fs::path& path) @@ -353,7 +361,6 @@ void CafeTitleList::ScanGamePath(const fs::path& path) else if (it.is_directory(ec)) { dirsInDirectory.emplace_back(it.path()); - std::string dirName = _pathToUtf8(it.path().filename()); if (boost::iequals(dirName, "content")) hasContentFolder = true; @@ -366,10 +373,10 @@ void CafeTitleList::ScanGamePath(const fs::path& path) // always check individual files for (auto& it : filesInDirectory) { - // since checking files is slow, we only do it for known file extensions + // since checking individual files is slow, we limit it to known file names or extensions if (!it.has_extension()) continue; - if (!_IsKnownFileExtension(_pathToUtf8(it.extension()))) + if (!_IsKnownFileNameOrExtension(it)) continue; AddTitleFromPath(it); } diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 580aeb23..60495f53 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -468,6 +468,14 @@ inline fs::path _utf8ToPath(std::string_view input) return fs::path(v); } +// locale-independent variant of tolower() which also matches Wii U behavior +inline char _ansiToLower(char c) +{ + if (c >= 'A' && c <= 'Z') + c -= ('A' - 'a'); + return c; +} + class RunAtCemuBoot // -> replaces this with direct function calls. Linkers other than MSVC may optimize way object files entirely if they are not referenced from outside. So a source file self-registering using this would be causing issues { public: diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index d0ec6e9f..e8e90f02 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -639,13 +639,15 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) if (menuId == MAINFRAME_MENU_ID_FILE_LOAD) { const auto wildcard = formatWxString( - "{}|*.wud;*.wux;*.wua;*.iso;*.rpx;*.elf" + "{}|*.wud;*.wux;*.wua;*.iso;*.rpx;*.elf;title.tmd" "|{}|*.wud;*.wux;*.iso" + "|{}|title.tmd" "|{}|*.wua" "|{}|*.rpx;*.elf" "|{}|*", _("All Wii U files (*.wud, *.wux, *.wua, *.iso, *.rpx, *.elf)"), _("Wii U image (*.wud, *.wux, *.iso, *.wad)"), + _("Wii U NUS content"), _("Wii U archive (*.wua)"), _("Wii U executable (*.rpx, *.elf)"), _("All files (*.*)") diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index aad46c52..c65459aa 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -941,6 +941,8 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu return _("Folder"); case wxTitleManagerList::EntryFormat::WUD: return _("WUD"); + case wxTitleManagerList::EntryFormat::NUS: + return _("NUS"); case wxTitleManagerList::EntryFormat::WUA: return _("WUA"); } @@ -1010,16 +1012,19 @@ void wxTitleManagerList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt wxTitleManagerList::EntryFormat entryFormat; switch (titleInfo.GetFormat()) { - case TitleInfo::TitleDataFormat::HOST_FS: - default: - entryFormat = EntryFormat::Folder; - break; case TitleInfo::TitleDataFormat::WUD: entryFormat = EntryFormat::WUD; break; + case TitleInfo::TitleDataFormat::NUS: + entryFormat = EntryFormat::NUS; + break; case TitleInfo::TitleDataFormat::WIIU_ARCHIVE: entryFormat = EntryFormat::WUA; break; + case TitleInfo::TitleDataFormat::HOST_FS: + default: + entryFormat = EntryFormat::Folder; + break; } if (evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED) diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/components/wxTitleManagerList.h index 07556068..cab531c4 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/components/wxTitleManagerList.h @@ -42,6 +42,7 @@ public: { Folder, WUD, + NUS, WUA, }; From f6c3c96d9448b3903d2656074cf3817c4893cdde Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:38:51 +0200 Subject: [PATCH 042/101] More detailed error messages when encrypted titles fail to launch --- src/Cafe/Filesystem/FST/FST.cpp | 35 ++++++++++++++++++++++++-------- src/Cafe/Filesystem/FST/FST.h | 16 ++++++++++++--- src/Cafe/TitleList/TitleInfo.cpp | 21 +++++++++++++++++-- src/Cafe/TitleList/TitleInfo.h | 14 ++++++++++++- src/gui/MainWindow.cpp | 10 +++++++++ 5 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/Cafe/Filesystem/FST/FST.cpp b/src/Cafe/Filesystem/FST/FST.cpp index a4bbfeed..10ae659d 100644 --- a/src/Cafe/Filesystem/FST/FST.cpp +++ b/src/Cafe/Filesystem/FST/FST.cpp @@ -12,6 +12,8 @@ #include "boost/range/adaptor/reversed.hpp" +#define SET_FST_ERROR(__code) if (errorCodeOut) *errorCodeOut = ErrorCode::__code + class FSTDataSource { public: @@ -215,23 +217,22 @@ bool FSTVolume::FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey) // open WUD image using key cache // if no matching key is found then keyFound will return false -FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, bool* keyFound) +FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, ErrorCode* errorCodeOut) { + SET_FST_ERROR(UNKNOWN_ERROR); KeyCache_Prepare(); NCrypto::AesKey discTitleKey; if (!FindDiscKey(path, discTitleKey)) { - if(keyFound) - *keyFound = false; + SET_FST_ERROR(DISC_KEY_MISSING); return nullptr; } - if(keyFound) - *keyFound = true; - return OpenFromDiscImage(path, discTitleKey); + return OpenFromDiscImage(path, discTitleKey, errorCodeOut); } // open WUD image -FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey) +FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey, ErrorCode* errorCodeOut) + { // WUD images support multiple partitions, each with their own key and FST // the process for loading game data FSTVolume from a WUD image is as follows: @@ -240,6 +241,7 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d // 3) find main GM partition // 4) use SI information to get titleKey for GM partition // 5) Load FST for GM + SET_FST_ERROR(UNKNOWN_ERROR); std::unique_ptr dataSource(FSTDataSourceWUD::Open(path)); if (!dataSource) return nullptr; @@ -365,11 +367,15 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d // load GM partition dataSource->SetBaseOffset((uint64)partitionArray[gmPartitionIndex].partitionAddress * DISC_SECTOR_SIZE); - return OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast(partitionHeaderGM.fstHashType)); + FSTVolume* r = OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast(partitionHeaderGM.fstHashType)); + if (r) + SET_FST_ERROR(OK); + return r; } -FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath) +FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath, ErrorCode* errorCodeOut) { + SET_FST_ERROR(UNKNOWN_ERROR); // load TMD FileStream* tmdFile = FileStream::openFile2(folderPath / "title.tmd"); if (!tmdFile) @@ -379,17 +385,26 @@ FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath) delete tmdFile; NCrypto::TMDParser tmdParser; if (!tmdParser.parse(tmdData.data(), tmdData.size())) + { + SET_FST_ERROR(BAD_TITLE_TMD); return nullptr; + } // load ticket FileStream* ticketFile = FileStream::openFile2(folderPath / "title.tik"); if (!ticketFile) + { + SET_FST_ERROR(TITLE_TIK_MISSING); return nullptr; + } std::vector ticketData; ticketFile->extract(ticketData); delete ticketFile; NCrypto::ETicketParser ticketParser; if (!ticketParser.parse(ticketData.data(), ticketData.size())) + { + SET_FST_ERROR(BAD_TITLE_TIK); return nullptr; + } NCrypto::AesKey titleKey; ticketParser.GetTitleKey(titleKey); // open data source @@ -412,6 +427,8 @@ FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath) // load FST // fstSize = size of first cluster? FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode); + if (fstVolume) + SET_FST_ERROR(OK); return fstVolume; } diff --git a/src/Cafe/Filesystem/FST/FST.h b/src/Cafe/Filesystem/FST/FST.h index 3f59152f..98bf1ae6 100644 --- a/src/Cafe/Filesystem/FST/FST.h +++ b/src/Cafe/Filesystem/FST/FST.h @@ -20,11 +20,21 @@ private: class FSTVolume { public: + enum class ErrorCode + { + OK = 0, + UNKNOWN_ERROR = 1, + DISC_KEY_MISSING = 2, + TITLE_TIK_MISSING = 3, + BAD_TITLE_TMD = 4, + BAD_TITLE_TIK = 5, + }; + static bool FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey); - static FSTVolume* OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey); - static FSTVolume* OpenFromDiscImage(const fs::path& path, bool* keyFound = nullptr); - static FSTVolume* OpenFromContentFolder(fs::path folderPath); + static FSTVolume* OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey, ErrorCode* errorCodeOut = nullptr); + static FSTVolume* OpenFromDiscImage(const fs::path& path, ErrorCode* errorCodeOut = nullptr); + static FSTVolume* OpenFromContentFolder(fs::path folderPath, ErrorCode* errorCodeOut = nullptr); ~FSTVolume(); diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index 867e0a7f..ff457575 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -77,6 +77,7 @@ TitleInfo::TitleInfo(const fs::path& path, std::string_view subPath) if (!path.has_filename()) { m_isValid = false; + SetInvalidReason(InvalidReason::BAD_PATH_OR_INACCESSIBLE); return; } m_isValid = true; @@ -269,6 +270,7 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF return true; } } + SetInvalidReason(InvalidReason::UNKNOWN_FORMAT); return false; } @@ -321,6 +323,12 @@ uint64 TitleInfo::GetUID() return m_uid; } +void TitleInfo::SetInvalidReason(InvalidReason reason) +{ + if(m_invalidReason == InvalidReason::NONE) + m_invalidReason = reason; // only update reason when it hasn't been set before +} + std::mutex sZArchivePoolMtx; std::map> sZArchivePool; @@ -382,21 +390,29 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder, if (!r) { cemuLog_log(LogType::Force, "Failed to mount {} to {}", virtualPath, subfolder); + SetInvalidReason(InvalidReason::BAD_PATH_OR_INACCESSIBLE); return false; } } else if (m_titleFormat == TitleDataFormat::WUD || m_titleFormat == TitleDataFormat::NUS) { + FSTVolume::ErrorCode fstError; if (m_mountpoints.empty()) { cemu_assert_debug(!m_wudVolume); if(m_titleFormat == TitleDataFormat::WUD) - m_wudVolume = FSTVolume::OpenFromDiscImage(m_fullPath); // open wud/wux + m_wudVolume = FSTVolume::OpenFromDiscImage(m_fullPath, &fstError); // open wud/wux else - m_wudVolume = FSTVolume::OpenFromContentFolder(m_fullPath.parent_path()); // open from .app files directory, the path points to /title.tmd + m_wudVolume = FSTVolume::OpenFromContentFolder(m_fullPath.parent_path(), &fstError); // open from .app files directory, the path points to /title.tmd } if (!m_wudVolume) + { + if (fstError == FSTVolume::ErrorCode::DISC_KEY_MISSING) + SetInvalidReason(InvalidReason::NO_DISC_KEY); + else if (fstError == FSTVolume::ErrorCode::TITLE_TIK_MISSING) + SetInvalidReason(InvalidReason::NO_TITLE_TIK); return false; + } bool r = FSCDeviceWUD_Mount(virtualPath, subfolder, m_wudVolume, mountPriority); cemu_assert_debug(r); if (!r) @@ -518,6 +534,7 @@ bool TitleInfo::ParseXmlInfo() m_parsedAppXml = nullptr; m_parsedCosXml = nullptr; m_isValid = false; + SetInvalidReason(InvalidReason::MISSING_XML_FILES); return false; } m_isValid = true; diff --git a/src/Cafe/TitleList/TitleInfo.h b/src/Cafe/TitleList/TitleInfo.h index 536e9ccb..eca6624d 100644 --- a/src/Cafe/TitleList/TitleInfo.h +++ b/src/Cafe/TitleList/TitleInfo.h @@ -65,6 +65,16 @@ public: INVALID_STRUCTURE = 0, }; + enum class InvalidReason : uint8 + { + NONE = 0, + BAD_PATH_OR_INACCESSIBLE = 1, + UNKNOWN_FORMAT = 2, + NO_DISC_KEY = 3, + NO_TITLE_TIK = 4, + MISSING_XML_FILES = 4, + }; + struct CachedInfo { TitleDataFormat titleDataFormat; @@ -101,6 +111,7 @@ public: CachedInfo MakeCacheEntry(); bool IsValid() const; + InvalidReason GetInvalidReason() const { return m_invalidReason; } uint64 GetUID(); // returns a unique identifier derived from the absolute canonical title location which can be used to identify this title by its location. May not persist across sessions, especially when Cemu is used portable fs::path GetPath() const; @@ -182,7 +193,7 @@ private: bool DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataFormat& formatOut); void CalcUID(); - + void SetInvalidReason(InvalidReason reason); bool ParseAppXml(std::vector& appXmlData); bool m_isValid{ false }; @@ -190,6 +201,7 @@ private: fs::path m_fullPath; std::string m_subPath; // used for formats where fullPath isn't unique on its own (like WUA) uint64 m_uid{}; + InvalidReason m_invalidReason{ InvalidReason::NONE }; // if m_isValid == false, this contains a more detailed error code // mounting info std::vector> m_mountpoints; class FSTVolume* m_wudVolume{}; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e8e90f02..69159df2 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -559,6 +559,16 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY { wxString t = _("Unable to launch game\nPath:\n"); t.append(fileName); + if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_DISC_KEY) + { + t.append(_("\n\n")); + t.append(_("Could not decrypt title. Make sure that keys.txt contains the correct disc key for this title.")); + } + if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_TITLE_TIK) + { + t.append(_("")); + t.append(_("\n\nCould not decrypt title because title.tik is missing.")); + } wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; } From abce406ee8640d0fa28a6ee95dde3712e8d613be Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 28 Sep 2023 02:51:40 +0200 Subject: [PATCH 043/101] Refactor more wstring instances to utf8-encoded string --- src/Cafe/GraphicPack/GraphicPack2.cpp | 22 ++++------ src/Cafe/GraphicPack/GraphicPack2.h | 9 ++-- src/Cafe/GraphicPack/GraphicPack2Patches.cpp | 19 +++------ .../GraphicPack/GraphicPack2PatchesParser.cpp | 2 +- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 6 +-- .../Renderer/Vulkan/RendererShaderVk.cpp | 2 +- .../Vulkan/VulkanPipelineStableCache.cpp | 4 +- src/Cafe/IOSU/legacy/iosu_act.cpp | 2 +- src/Cafe/IOSU/legacy/iosu_crypto.cpp | 6 +-- src/Cafe/IOSU/legacy/iosu_crypto.h | 2 +- src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp | 9 ++-- src/Cafe/OS/libs/nn_nfp/nn_nfp.h | 2 +- src/Cemu/Logging/CemuLogging.cpp | 8 ---- src/Cemu/Logging/CemuLogging.h | 1 - .../Tools/DownloadManager/DownloadManager.cpp | 2 +- src/Common/unix/FileStream_unix.cpp | 1 - src/config/ActiveSettings.cpp | 2 +- src/config/CemuConfig.cpp | 26 +++++------- src/config/CemuConfig.h | 10 ++--- src/gui/CemuApp.cpp | 6 +-- src/gui/GeneralSettings2.cpp | 4 +- src/gui/GettingStartedDialog.cpp | 4 +- src/gui/GraphicPacksWindow2.cpp | 2 +- src/gui/MainWindow.cpp | 41 +++++++++---------- src/gui/MainWindow.h | 2 +- src/gui/components/wxTitleManagerList.cpp | 2 +- 26 files changed, 82 insertions(+), 114 deletions(-) diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index 4594aed0..72e301c4 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -54,7 +54,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath) if (versionNum > GP_LEGACY_VERSION) { - GraphicPack2::LoadGraphicPack(rulesPath.generic_wstring(), iniParser); + GraphicPack2::LoadGraphicPack(_pathToUtf8(rulesPath), iniParser); return; } } @@ -79,7 +79,7 @@ void GraphicPack2::LoadAll() } } -bool GraphicPack2::LoadGraphicPack(const std::wstring& filename, IniParser& rules) +bool GraphicPack2::LoadGraphicPack(const std::string& filename, IniParser& rules) { try { @@ -216,12 +216,6 @@ void GraphicPack2::WaitUntilReady() std::this_thread::sleep_for(std::chrono::milliseconds(5)); } -GraphicPack2::GraphicPack2(std::wstring filename) - : m_filename(std::move(filename)) -{ - // unused for now -} - std::unordered_map GraphicPack2::ParsePresetVars(IniParser& rules) const { ExpressionParser parser; @@ -255,7 +249,7 @@ std::unordered_map GraphicPack2::ParsePres return vars; } -GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules) +GraphicPack2::GraphicPack2(std::string filename, IniParser& rules) : m_filename(std::move(filename)) { // we're already in [Definition] @@ -265,7 +259,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules) m_version = StringHelpers::ToInt(*option_version, -1); if (m_version < 0) { - cemuLog_log(LogType::Force, L"{}: Invalid version", m_filename); + cemuLog_log(LogType::Force, "{}: Invalid version", m_filename); throw std::exception(); } @@ -839,7 +833,7 @@ void GraphicPack2::LoadReplacedFiles() return; m_patchedFilesLoaded = true; - fs::path gfxPackPath(m_filename.c_str()); + fs::path gfxPackPath = _utf8ToPath(m_filename); gfxPackPath = gfxPackPath.remove_filename(); // /content/ @@ -892,14 +886,14 @@ bool GraphicPack2::Activate() return false; } - FileStream* fs_rules = FileStream::openFile2({ m_filename }); + FileStream* fs_rules = FileStream::openFile2(_utf8ToPath(m_filename)); if (!fs_rules) return false; std::vector rulesData; fs_rules->extract(rulesData); delete fs_rules; - IniParser rules({ (char*)rulesData.data(), rulesData.size()}, boost::nowide::narrow(m_filename)); + IniParser rules({ (char*)rulesData.data(), rulesData.size()}, m_filename); // load rules try @@ -953,7 +947,7 @@ bool GraphicPack2::Activate() else if (anisotropyValue == 16) rule.overwrite_settings.anistropic_value = 4; else - cemuLog_log(LogType::Force, fmt::format(L"Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, m_filename)); + cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, m_filename); } m_texture_rules.emplace_back(rule); } diff --git a/src/Cafe/GraphicPack/GraphicPack2.h b/src/Cafe/GraphicPack/GraphicPack2.h index a087f757..6396ecc7 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.h +++ b/src/Cafe/GraphicPack/GraphicPack2.h @@ -97,13 +97,12 @@ public: }; using PresetPtr = std::shared_ptr; - GraphicPack2(std::wstring filename); - GraphicPack2(std::wstring filename, IniParser& rules); + GraphicPack2(std::string filename, IniParser& rules); bool IsEnabled() const { return m_enabled; } bool IsActivated() const { return m_activated; } sint32 GetVersion() const { return m_version; } - const std::wstring& GetFilename() const { return m_filename; } + const std::string& GetFilename() const { return m_filename; } const fs::path GetFilename2() const { return fs::path(m_filename); } bool RequiresRestart(bool changeEnableState, bool changePreset); bool Reload(); @@ -165,7 +164,7 @@ public: static const std::vector>& GetGraphicPacks() { return s_graphic_packs; } static const std::vector>& GetActiveGraphicPacks() { return s_active_graphic_packs; } static void LoadGraphicPack(fs::path graphicPackPath); - static bool LoadGraphicPack(const std::wstring& filename, class IniParser& rules); + static bool LoadGraphicPack(const std::string& filename, class IniParser& rules); static bool ActivateGraphicPack(const std::shared_ptr& graphic_pack); static bool DeactivateGraphicPack(const std::shared_ptr& graphic_pack); static void ClearGraphicPacks(); @@ -209,7 +208,7 @@ private: parser.TryAddConstant(var.first, (TType)var.second.second); } - std::wstring m_filename; + std::string m_filename; sint32 m_version; std::string m_name; diff --git a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp index 7fa1e7fe..5c79630c 100644 --- a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp @@ -83,7 +83,7 @@ bool GraphicPack2::LoadCemuPatches() }; bool foundPatches = false; - fs::path path(m_filename); + fs::path path(_utf8ToPath(m_filename)); path.remove_filename(); for (auto& p : fs::directory_iterator(path)) { @@ -91,10 +91,10 @@ bool GraphicPack2::LoadCemuPatches() if (fs::is_regular_file(p.status()) && path.has_filename()) { // check if filename matches - std::wstring filename = path.filename().generic_wstring(); - if (boost::istarts_with(filename, L"patch_") && boost::iends_with(filename, L".asm")) + std::string filename = _pathToUtf8(path.filename()); + if (boost::istarts_with(filename, "patch_") && boost::iends_with(filename, ".asm")) { - FileStream* patchFile = FileStream::openFile(path.generic_wstring().c_str()); + FileStream* patchFile = FileStream::openFile2(path); if (patchFile) { // read file @@ -126,27 +126,20 @@ void GraphicPack2::LoadPatchFiles() // order of loading patches: // 1) Load Cemu-style patches (patch_.asm), stop here if at least one patch file exists // 2) Load Cemuhook patches.txt - - // update: As of 1.20.2b Cemu always takes over patching since Cemuhook patching broke due to other internal changes (memory allocation changed and some reordering on when graphic packs get loaded) if (LoadCemuPatches()) return; // exit if at least one Cemu style patch file was found // fall back to Cemuhook patches.txt to guarantee backward compatibility - fs::path path(m_filename); + fs::path path(_utf8ToPath(m_filename)); path.remove_filename(); path.append("patches.txt"); - - FileStream* patchFile = FileStream::openFile(path.generic_wstring().c_str()); - + FileStream* patchFile = FileStream::openFile2(path); if (patchFile == nullptr) return; - // read file std::vector fileData; patchFile->extract(fileData); delete patchFile; - cemu_assert_debug(list_patchGroups.empty()); - // parse MemStreamReader patchesStream(fileData.data(), (sint32)fileData.size()); ParseCemuhookPatchesTxtInternal(patchesStream); diff --git a/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp b/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp index ce04bf93..d011a10b 100644 --- a/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp @@ -25,7 +25,7 @@ sint32 GraphicPack2::GetLengthWithoutComment(const char* str, size_t length) void GraphicPack2::LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg) { - cemuLog_log(LogType::Force, fmt::format(L"Syntax error while parsing patch for graphic pack '{}':", this->GetFilename())); + cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", this->GetFilename()); if(lineNumber >= 0) cemuLog_log(LogType::Force, fmt::format("Line {0}: {1}", lineNumber, errorMsg)); else diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index 0883c436..6c15f26e 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -511,9 +511,9 @@ void debugger_enterTW(PPCInterpreter_t* hCPU) { if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled) { - std::wstring logName = !bp->comment.empty() ? L"Breakpoint '"+bp->comment+L"'" : fmt::format(L"Breakpoint at 0x{:08X} (no comment)", bp->address); - std::wstring logContext = fmt::format(L"Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? L" Stack Trace:" : L""); - cemuLog_log(LogType::Force, L"[Debugger] {} was executed! {}", logName, logContext); + std::string logName = !bp->comment.empty() ? "Breakpoint '"+boost::nowide::narrow(bp->comment)+"'" : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); + std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); + cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); if (cemuLog_advancedPPCLoggingEnabled()) DebugLogStackTrace(coreinitThread_getCurrentThreadDepr(hCPU), hCPU->gpr[1]); break; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp index 4061be33..e4c87d62 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp @@ -440,7 +440,7 @@ void RendererShaderVk::ShaderCacheLoading_begin(uint64 cacheTitleId) } uint32 spirvCacheMagic = GeneratePrecompiledCacheId(); const std::string cacheFilename = fmt::format("{:016x}_spirv.bin", cacheTitleId); - const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); + const fs::path cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename); s_spirvCache = FileCache::Open(cachePath, true, spirvCacheMagic); if (s_spirvCache == nullptr) cemuLog_log(LogType::Force, "Unable to open SPIR-V cache {}", cacheFilename); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp index 74247b9a..0ee9f023 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp @@ -59,10 +59,10 @@ uint32 VulkanPipelineStableCache::BeginLoading(uint64 cacheTitleId) // open cache file or create it cemu_assert_debug(s_cache == nullptr); - s_cache = FileCache::Open(pathCacheFile.generic_wstring(), true, LatteShaderCache_getPipelineCacheExtraVersion(cacheTitleId)); + s_cache = FileCache::Open(pathCacheFile, true, LatteShaderCache_getPipelineCacheExtraVersion(cacheTitleId)); if (!s_cache) { - cemuLog_log(LogType::Force, "Failed to open or create Vulkan pipeline cache file: {}", pathCacheFile.generic_string()); + cemuLog_log(LogType::Force, "Failed to open or create Vulkan pipeline cache file: {}", _pathToUtf8(pathCacheFile)); return 0; } else diff --git a/src/Cafe/IOSU/legacy/iosu_act.cpp b/src/Cafe/IOSU/legacy/iosu_act.cpp index 919b7b0f..e7418e8f 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.cpp +++ b/src/Cafe/IOSU/legacy/iosu_act.cpp @@ -113,7 +113,7 @@ void iosuAct_loadAccounts() // } //} - cemuLog_log(LogType::Force, L"IOSU_ACT: using account {} in first slot", first_acc.GetMiiName()); + cemuLog_log(LogType::Force, "IOSU_ACT: using account {} in first slot", boost::nowide::narrow(first_acc.GetMiiName())); _actAccountDataInitialized = true; } diff --git a/src/Cafe/IOSU/legacy/iosu_crypto.cpp b/src/Cafe/IOSU/legacy/iosu_crypto.cpp index 9d7ab875..80eb2f01 100644 --- a/src/Cafe/IOSU/legacy/iosu_crypto.cpp +++ b/src/Cafe/IOSU/legacy/iosu_crypto.cpp @@ -615,10 +615,10 @@ void iosuCrypto_init() iosuCrypto_loadSSLCertificates(); } -bool iosuCrypto_checkRequirementMLCFile(std::string_view mlcSubpath, std::wstring& additionalErrorInfo_filePath) +bool iosuCrypto_checkRequirementMLCFile(std::string_view mlcSubpath, std::string& additionalErrorInfo_filePath) { const auto path = ActiveSettings::GetMlcPath(mlcSubpath); - additionalErrorInfo_filePath = path.generic_wstring(); + additionalErrorInfo_filePath = _pathToUtf8(path); sint32 fileDataSize = 0; auto fileData = FileStream::LoadIntoMemory(path); if (!fileData) @@ -626,7 +626,7 @@ bool iosuCrypto_checkRequirementMLCFile(std::string_view mlcSubpath, std::wstrin return true; } -sint32 iosuCrypt_checkRequirementsForOnlineMode(std::wstring& additionalErrorInfo) +sint32 iosuCrypt_checkRequirementsForOnlineMode(std::string& additionalErrorInfo) { std::error_code ec; // check if otp.bin is present diff --git a/src/Cafe/IOSU/legacy/iosu_crypto.h b/src/Cafe/IOSU/legacy/iosu_crypto.h index bf3d7e2f..9f1429c7 100644 --- a/src/Cafe/IOSU/legacy/iosu_crypto.h +++ b/src/Cafe/IOSU/legacy/iosu_crypto.h @@ -74,7 +74,7 @@ enum IOS_CRYPTO_ONLINE_REQ_MISSING_FILE }; -sint32 iosuCrypt_checkRequirementsForOnlineMode(std::wstring& additionalErrorInfo); +sint32 iosuCrypt_checkRequirementsForOnlineMode(std::string& additionalErrorInfo); void iosuCrypto_readOtpData(void* output, sint32 wordIndex, sint32 size); std::vector iosuCrypt_getCertificateKeys(); diff --git a/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp b/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp index 27a858c1..ad2ea203 100644 --- a/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp +++ b/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp @@ -180,7 +180,7 @@ struct bool hasOpenApplicationArea; // set to true if application area was opened or created // currently active Amiibo bool hasActiveAmiibo; - std::wstring amiiboPath; + fs::path amiiboPath; bool hasInvalidHMAC; uint32 amiiboTouchTime; AmiiboRawNFCData amiiboNFCData; // raw data @@ -188,7 +188,6 @@ struct AmiiboProcessedData amiiboProcessedData; }nfp_data = { 0 }; -bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError); bool nnNfp_writeCurrentAmiibo(); #include "AmiiboCrypto.h" @@ -770,7 +769,7 @@ void nnNfp_unloadAmiibo() nnNfpUnlock(); } -bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError) +bool nnNfp_touchNfcTagFromFile(const fs::path& filePath, uint32* nfcError) { AmiiboRawNFCData rawData = { 0 }; auto nfcData = FileStream::LoadIntoMemory(filePath); @@ -847,11 +846,11 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError) memcpy(&nfp_data.amiiboNFCData, &rawData, sizeof(AmiiboRawNFCData)); // decrypt amiibo amiiboDecrypt(); - nfp_data.amiiboPath = std::wstring(filePath); + nfp_data.amiiboPath = filePath; nfp_data.hasActiveAmiibo = true; if (nfp_data.activateEvent) { - coreinit::OSEvent* osEvent = (coreinit::OSEvent*)memory_getPointerFromVirtualOffset(nfp_data.activateEvent); + MEMPTR osEvent(nfp_data.activateEvent); coreinit::OSSignalEvent(osEvent); } nfp_data.amiiboTouchTime = GetTickCount(); diff --git a/src/Cafe/OS/libs/nn_nfp/nn_nfp.h b/src/Cafe/OS/libs/nn_nfp/nn_nfp.h index 793b3bc3..e8a1c55f 100644 --- a/src/Cafe/OS/libs/nn_nfp/nn_nfp.h +++ b/src/Cafe/OS/libs/nn_nfp/nn_nfp.h @@ -8,7 +8,7 @@ namespace nn::nfp void nnNfp_load(); void nnNfp_update(); -bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError); +bool nnNfp_touchNfcTagFromFile(const fs::path& filePath, uint32* nfcError); #define NFP_STATE_NONE (0) #define NFP_STATE_INIT (1) diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index ac228530..f8ce7265 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -157,14 +157,6 @@ bool cemuLog_log(LogType type, std::u8string_view text) return cemuLog_log(type, s); } -bool cemuLog_log(LogType type, std::wstring_view text) -{ - if (!cemuLog_isLoggingEnabled(type)) - return false; - - return cemuLog_log(type, boost::nowide::narrow(text.data(), text.size())); -} - void cemuLog_waitForFlush() { cemuLog_createLogFile(false); diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index e6599c5a..8983c847 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -68,7 +68,6 @@ inline bool cemuLog_isLoggingEnabled(LogType type) bool cemuLog_log(LogType type, std::string_view text); bool cemuLog_log(LogType type, std::u8string_view text); -bool cemuLog_log(LogType type, std::wstring_view text); void cemuLog_waitForFlush(); // wait until all log lines are written template diff --git a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp index ec39b928..09093792 100644 --- a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp +++ b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp @@ -1541,7 +1541,7 @@ void DownloadManager::runManager() auto cacheFilePath = ActiveSettings::GetMlcPath("usr/save/system/nim/nup/"); fs::create_directories(cacheFilePath); cacheFilePath /= "cemu_cache.dat"; - s_nupFileCache = FileCache::Open(cacheFilePath.generic_wstring(), true); + s_nupFileCache = FileCache::Open(cacheFilePath, true); // launch worker thread std::thread t(&DownloadManager::threadFunc, this); t.detach(); diff --git a/src/Common/unix/FileStream_unix.cpp b/src/Common/unix/FileStream_unix.cpp index c65a3219..2dba17b7 100644 --- a/src/Common/unix/FileStream_unix.cpp +++ b/src/Common/unix/FileStream_unix.cpp @@ -33,7 +33,6 @@ FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite) FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite) { - //return openFile(path.generic_wstring().c_str(), allowWrite); FileStream* fs = new FileStream(path, true, allowWrite); if (fs->m_isValid) return fs; diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp index af7f521e..2049bd65 100644 --- a/src/config/ActiveSettings.cpp +++ b/src/config/ActiveSettings.cpp @@ -40,7 +40,7 @@ ActiveSettings::LoadOnce( g_config.SetFilename(GetConfigPath("settings.xml").generic_wstring()); g_config.Load(); LaunchSettings::ChangeNetworkServiceURL(GetConfig().account.active_service); - std::wstring additionalErrorInfo; + std::string additionalErrorInfo; s_has_required_online_files = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo) == IOS_CRYPTO_ONLINE_REQ_OK; return failed_write_access; } diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 220a2295..1801759a 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -110,7 +110,7 @@ void CemuConfig::Load(XMLConfigParser& parser) try { - recent_launch_files.emplace_back(boost::nowide::widen(path)); + recent_launch_files.emplace_back(path); } catch (const std::exception&) { @@ -125,10 +125,9 @@ void CemuConfig::Load(XMLConfigParser& parser) const std::string path = element.value(""); if (path.empty()) continue; - try { - recent_nfc_files.emplace_back(boost::nowide::widen(path)); + recent_nfc_files.emplace_back(path); } catch (const std::exception&) { @@ -143,10 +142,9 @@ void CemuConfig::Load(XMLConfigParser& parser) const std::string path = element.value(""); if (path.empty()) continue; - try { - game_paths.emplace_back(boost::nowide::widen(path)); + game_paths.emplace_back(path); } catch (const std::exception&) { @@ -402,20 +400,20 @@ void CemuConfig::Save(XMLConfigParser& parser) auto launch_files_parser = config.set("RecentLaunchFiles"); for (const auto& entry : recent_launch_files) { - launch_files_parser.set("Entry", boost::nowide::narrow(entry).c_str()); + launch_files_parser.set("Entry", entry.c_str()); } auto nfc_files_parser = config.set("RecentNFCFiles"); for (const auto& entry : recent_nfc_files) { - nfc_files_parser.set("Entry", boost::nowide::narrow(entry).c_str()); + nfc_files_parser.set("Entry", entry.c_str()); } // game paths auto game_path_parser = config.set("GamePaths"); for (const auto& entry : game_paths) { - game_path_parser.set("Entry", boost::nowide::narrow(entry).c_str()); + game_path_parser.set("Entry", entry.c_str()); } // game list cache @@ -593,22 +591,18 @@ void CemuConfig::SetGameListCustomName(uint64 titleId, std::string customName) gameEntry->custom_name = std::move(customName); } -void CemuConfig::AddRecentlyLaunchedFile(std::wstring_view file) +void CemuConfig::AddRecentlyLaunchedFile(std::string_view file) { - // insert into front - recent_launch_files.insert(recent_launch_files.begin(), std::wstring{ file }); + recent_launch_files.insert(recent_launch_files.begin(), std::string(file)); RemoveDuplicatesKeepOrder(recent_launch_files); - // keep maximum of entries while(recent_launch_files.size() > kMaxRecentEntries) recent_launch_files.pop_back(); } -void CemuConfig::AddRecentNfcFile(std::wstring_view file) +void CemuConfig::AddRecentNfcFile(std::string_view file) { - // insert into front - recent_nfc_files.insert(recent_nfc_files.begin(), std::wstring{ file }); + recent_nfc_files.insert(recent_nfc_files.begin(), std::string(file)); RemoveDuplicatesKeepOrder(recent_nfc_files); - // keep maximum of entries while (recent_nfc_files.size() > kMaxRecentEntries) recent_nfc_files.pop_back(); } diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index ea6c3f2c..eb552fce 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -379,7 +379,7 @@ struct CemuConfig ConfigValue disable_screensaver{DISABLE_SCREENSAVER_DEFAULT}; #undef DISABLE_SCREENSAVER_DEFAULT - std::vector game_paths; + std::vector game_paths; std::mutex game_cache_entries_mutex; std::vector game_cache_entries; @@ -399,8 +399,8 @@ struct CemuConfig // max 15 entries static constexpr size_t kMaxRecentEntries = 15; - std::vector recent_launch_files; - std::vector recent_nfc_files; + std::vector recent_launch_files; + std::vector recent_nfc_files; Vector2i window_position{-1,-1}; Vector2i window_size{ -1,-1 }; @@ -499,8 +499,8 @@ struct CemuConfig void Load(XMLConfigParser& parser); void Save(XMLConfigParser& parser); - void AddRecentlyLaunchedFile(std::wstring_view file); - void AddRecentNfcFile(std::wstring_view file); + void AddRecentlyLaunchedFile(std::string_view file); + void AddRecentNfcFile(std::string_view file); bool IsGameListFavorite(uint64 titleId); void SetGameListFavorite(uint64 titleId, bool isFavorite); diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 74ef6848..04823ca4 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -198,9 +198,9 @@ void CemuApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, { cemuLog_createLogFile(false); cemuLog_log(LogType::Force, "Encountered wxWidgets assert!"); - cemuLog_log(LogType::Force, fmt::format(L"File: {0} Line: {1}", std::wstring_view(file), line)); - cemuLog_log(LogType::Force, fmt::format(L"Func: {0} Cond: {1}", func, std::wstring_view(cond))); - cemuLog_log(LogType::Force, fmt::format(L"Message: {}", std::wstring_view(msg))); + cemuLog_log(LogType::Force, "File: {0} Line: {1}", wxString(file).utf8_string(), line); + cemuLog_log(LogType::Force, "Func: {0} Cond: {1}", wxString(func).utf8_string(), wxString(cond).utf8_string()); + cemuLog_log(LogType::Force, "Message: {}", wxString(msg).utf8_string()); #if BOOST_OS_WINDOWS DumpThreadStackTrace(); diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index e069c10a..4dd3f9a3 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -923,7 +923,7 @@ void GeneralSettings2::StoreConfig() config.game_paths.clear(); for (auto& path : m_game_paths->GetStrings()) - config.game_paths.emplace_back(path); + config.game_paths.emplace_back(path.utf8_string()); auto selection = m_language->GetSelection(); if (selection == 0) @@ -1530,7 +1530,7 @@ void GeneralSettings2::ApplyConfig() for (auto& path : config.game_paths) { - m_game_paths->Append(path); + m_game_paths->Append(to_wxString(path)); } const auto app = (CemuApp*)wxTheApp; diff --git a/src/gui/GettingStartedDialog.cpp b/src/gui/GettingStartedDialog.cpp index 69f429b0..91cc3a11 100644 --- a/src/gui/GettingStartedDialog.cpp +++ b/src/gui/GettingStartedDialog.cpp @@ -229,7 +229,7 @@ void GettingStartedDialog::OnClose(wxCloseEvent& event) const auto it = std::find(config.game_paths.cbegin(), config.game_paths.cend(), gamePath); if (it == config.game_paths.cend()) { - config.game_paths.emplace_back(gamePath.generic_wstring()); + config.game_paths.emplace_back(_pathToUtf8(gamePath)); m_game_path_changed = true; } } @@ -248,7 +248,7 @@ void GettingStartedDialog::OnClose(wxCloseEvent& event) CafeTitleList::ClearScanPaths(); for (auto& it : GetConfig().game_paths) - CafeTitleList::AddScanPath(it); + CafeTitleList::AddScanPath(_utf8ToPath(it)); CafeTitleList::Refresh(); } diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index 2b618e86..13fec49a 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -329,7 +329,7 @@ void GraphicPacksWindow2::SaveStateToConfig() for (const auto& gp : GraphicPack2::GetGraphicPacks()) { - auto filename = MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename()).lexically_normal(); + auto filename = MakeRelativePath(ActiveSettings::GetUserDataPath(), _utf8ToPath(gp->GetFilename())).lexically_normal(); if (gp->IsEnabled()) { data.graphic_pack_entries.try_emplace(filename); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 69159df2..d9c5f4fb 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -248,7 +248,7 @@ public: bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override { if(!m_window->IsGameLaunched() && filenames.GetCount() == 1) - return m_window->FileLoad(filenames[0].wc_str(), wxLaunchGameEvent::INITIATED_BY::DRAG_AND_DROP); + return m_window->FileLoad(_utf8ToPath(filenames[0].utf8_string()), wxLaunchGameEvent::INITIATED_BY::DRAG_AND_DROP); return false; } @@ -265,11 +265,11 @@ public: { if (!m_window->IsGameLaunched() || filenames.GetCount() != 1) return false; - uint32 nfcError; - if (nnNfp_touchNfcTagFromFile(filenames[0].wc_str(), &nfcError)) + std::string path = filenames[0].utf8_string(); + if (nnNfp_touchNfcTagFromFile(_utf8ToPath(path), &nfcError)) { - GetConfig().AddRecentNfcFile((wchar_t*)filenames[0].wc_str()); + GetConfig().AddRecentNfcFile(path); m_window->UpdateNFCMenu(); return true; } @@ -493,9 +493,8 @@ bool MainWindow::InstallUpdate(const fs::path& metaFilePath) return false; } -bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY initiatedBy) +bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATED_BY initiatedBy) { - const fs::path launchPath = fs::path(fileName); TitleInfo launchTitle{ launchPath }; if (launchTitle.IsValid()) { @@ -518,14 +517,14 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY else if (r == CafeSystem::STATUS_CODE::UNABLE_TO_MOUNT) { wxString t = _("Unable to mount title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nFile which failed to load:\n"); - t.append(fileName); + t.append(_pathToUtf8(launchPath)); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; } else if (r != CafeSystem::STATUS_CODE::SUCCESS) { wxString t = _("Failed to launch game."); - t.append(fileName); + t.append(_pathToUtf8(launchPath)); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; } @@ -542,7 +541,7 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY { cemu_assert_debug(false); // todo wxString t = _("Failed to launch executable. Path: "); - t.append(fileName); + t.append(_pathToUtf8(launchPath)); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; } @@ -550,7 +549,7 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY else if (initiatedBy == wxLaunchGameEvent::INITIATED_BY::GAME_LIST) { wxString t = _("Unable to launch title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nPath which failed to load:\n"); - t.append(fileName); + t.append(_pathToUtf8(launchPath)); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; } @@ -558,7 +557,7 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY initiatedBy == wxLaunchGameEvent::INITIATED_BY::COMMAND_LINE) { wxString t = _("Unable to launch game\nPath:\n"); - t.append(fileName); + t.append(_pathToUtf8(launchPath)); if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_DISC_KEY) { t.append(_("\n\n")); @@ -575,16 +574,16 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY else { wxString t = _("Unable to launch game\nPath:\n"); - t.append(fileName); + t.append(_pathToUtf8(launchPath)); wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; } } if(launchTitle.IsValid()) - GetConfig().AddRecentlyLaunchedFile(launchTitle.GetPath().generic_wstring()); + GetConfig().AddRecentlyLaunchedFile(_pathToUtf8(launchTitle.GetPath())); else - GetConfig().AddRecentlyLaunchedFile(fileName); + GetConfig().AddRecentlyLaunchedFile(_pathToUtf8(launchPath)); wxWindowUpdateLocker lock(this); @@ -640,7 +639,7 @@ void MainWindow::OnLaunchFromFile(wxLaunchGameEvent& event) { if (event.GetPath().empty()) return; - FileLoad(event.GetPath().generic_wstring(), event.GetInitiatedBy()); + FileLoad(event.GetPath(), event.GetInitiatedBy()); } void MainWindow::OnFileMenu(wxCommandEvent& event) @@ -669,7 +668,7 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) return; const wxString wxStrFilePath = openFileDialog.GetPath(); - FileLoad(wxStrFilePath.wc_str(), wxLaunchGameEvent::INITIATED_BY::MENU); + FileLoad(_utf8ToPath(wxStrFilePath.utf8_string()), wxLaunchGameEvent::INITIATED_BY::MENU); } else if (menuId >= MAINFRAME_MENU_ID_FILE_RECENT_0 && menuId <= MAINFRAME_MENU_ID_FILE_RECENT_LAST) { @@ -749,7 +748,7 @@ void MainWindow::OnNFCMenu(wxCommandEvent& event) return; wxString wxStrFilePath = openFileDialog.GetPath(); uint32 nfcError; - if (nnNfp_touchNfcTagFromFile(wxStrFilePath.wc_str(), &nfcError) == false) + if (nnNfp_touchNfcTagFromFile(_utf8ToPath(wxStrFilePath.utf8_string()), &nfcError) == false) { if (nfcError == NFC_ERROR_NO_ACCESS) wxMessageBox(_("Cannot open file")); @@ -758,7 +757,7 @@ void MainWindow::OnNFCMenu(wxCommandEvent& event) } else { - GetConfig().AddRecentNfcFile((wchar_t*)wxStrFilePath.wc_str()); + GetConfig().AddRecentNfcFile(wxStrFilePath.utf8_string()); UpdateNFCMenu(); } } @@ -772,7 +771,7 @@ void MainWindow::OnNFCMenu(wxCommandEvent& event) if (!path.empty()) { uint32 nfcError = 0; - if (nnNfp_touchNfcTagFromFile(path.c_str(), &nfcError) == false) + if (nnNfp_touchNfcTagFromFile(_utf8ToPath(path), &nfcError) == false) { if (nfcError == NFC_ERROR_NO_ACCESS) wxMessageBox(_("Cannot open file")); @@ -1766,7 +1765,7 @@ void MainWindow::UpdateNFCMenu() if (recentFileIndex == 0) m_nfcMenuSeparator0 = m_nfcMenu->AppendSeparator(); - m_nfcMenu->Append(MAINFRAME_MENU_ID_NFC_RECENT_0 + i, fmt::format(L"{}. {}", recentFileIndex, entry )); + m_nfcMenu->Append(MAINFRAME_MENU_ID_NFC_RECENT_0 + i, to_wxString(fmt::format("{}. {}", recentFileIndex, entry))); recentFileIndex++; if (recentFileIndex >= 12) @@ -2106,7 +2105,7 @@ void MainWindow::RecreateMenu() if (recentFileIndex == 0) m_fileMenuSeparator0 = m_fileMenu->AppendSeparator(); - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, fmt::format(L"{}. {}", recentFileIndex, entry)); + m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, to_wxString(fmt::format("{}. {}", recentFileIndex, entry))); recentFileIndex++; if (recentFileIndex >= 8) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index c1762867..1c4b5235 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -65,7 +65,7 @@ public: void UpdateSettingsAfterGameLaunch(); void RestoreSettingsAfterGameExited(); - bool FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY initiatedBy); + bool FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATED_BY initiatedBy); [[nodiscard]] bool IsGameLaunched() const { return m_game_launched; } diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index c65459aa..93f86fdd 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -352,7 +352,7 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 righ boost::replace_all(shortName, ":", ""); } // for the default output directory we use the first game path configured by the user - std::wstring defaultDir = L""; + std::string defaultDir = ""; if (!GetConfig().game_paths.empty()) defaultDir = GetConfig().game_paths.front(); // get the short name, which we will use as a suggested default file name From 21c1f84a87b64e57ffe7d98b0dab5a9dab2c18f9 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 28 Sep 2023 05:21:48 +0200 Subject: [PATCH 044/101] Fix WUA conversion not detecting updates --- src/gui/components/wxTitleManagerList.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index 93f86fdd..ea0cc3d3 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -274,6 +274,9 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 righ break; // prefer the users selection } } + } + for (const auto& data : m_data) + { if (hasUpdateTitleId && data->entry.title_id == updateTitleId) { if (!titleInfo_update.IsValid()) From 6217276681b7fb7905235958e01ab8788c53616c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 28 Sep 2023 10:02:12 +0200 Subject: [PATCH 045/101] Enable DPI awareness on Windows --- dist/windows/Cemu.manifest | 16 ++++++++++++++++ src/CMakeLists.txt | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 dist/windows/Cemu.manifest diff --git a/dist/windows/Cemu.manifest b/dist/windows/Cemu.manifest new file mode 100644 index 00000000..5ff952b1 --- /dev/null +++ b/dist/windows/Cemu.manifest @@ -0,0 +1,16 @@ + + + + + + + + + + + + + True/PM + + + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a343f5a3..00a43a80 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,7 +59,8 @@ add_executable(CemuBin if(WIN32) target_sources(CemuBin PRIVATE resource/cemu.rc -) + ../dist/windows/cemu.manifest + ) endif() set_property(TARGET CemuBin PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") @@ -77,7 +78,7 @@ if (MACOS_BUNDLE) set(MACOSX_BUNDLE_BUNDLE_NAME "Cemu") set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CMAKE_PROJECT_VERSION}) set(MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION}) - set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2022 Cemu Project") + set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2023 Cemu Project") set(MACOSX_BUNDLE_CATEGORY "public.app-category.games") From 8a4abb8bbbdaf2e2d2162237f8cb39dbf8143dce Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 29 Sep 2023 05:41:33 +0200 Subject: [PATCH 046/101] Update Windows build instructions --- BUILD.md | 20 ++++++++++---------- CODING_STYLE.md | 6 +++--- src/Cafe/OS/libs/coreinit/coreinit_Time.cpp | 13 +------------ 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/BUILD.md b/BUILD.md index 35aaffb7..c63251d7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -3,20 +3,19 @@ ## Windows Prerequisites: -- A recent version of Visual Studio 2022 (recommended but not required) with the following additional components: - - C++ CMake tools for Windows - - Windows 10/11 SDK - git +- A recent version of Visual Studio 2022 with the following additional components: + - C++ CMake tools for Windows + - Windows 10/11 SDK -Instructions: +Instructions for Visual Studio 2022: 1. Run `git clone --recursive https://github.com/cemu-project/Cemu` -2. Launch `Cemu/generate_vs_solution.bat`. - - If you installed VS to a custom location or use VS 2019, you may need to manually change the path inside the .bat file. -3. Wait until it's done, then open `Cemu/build/Cemu.sln` in Visual Studio. -4. Then build the solution and once finished you can run and debug it, or build it and check the /bin folder for the final Cemu_release.exe. +2. Open the newly created Cemu directory in Visual Studio using the "Open a local folder" option +3. In the menu select Project -> Configure CMake. Wait until it is done, this may take a long time +4. You can now build, run and debug Cemu -You can also skip steps 3-5 and open the root folder of the cloned repo directly in Visual Studio (as a folder) and use the built-in CMake support but be warned that cmake support in VS can be a bit finicky. +Any other IDE should also work as long as it has CMake and MSVC support. CLion and Visual Studio Code have been confirmed to work. ## Linux @@ -46,7 +45,8 @@ To compile Cemu, a recent enough compiler and STL with C++20 support is required 5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`. #### Using GCC -While we use and test Cemu using clang, using GCC might work better with your distro (they should be fairly similar performance/issues wise and should only be considered if compilation is the issue). +While we build and test Cemu using clang, using GCC might work better with your distro (they should be fairly similar performance/issues wise and should only be considered if compilation is the issue). + You can use GCC by doing the following: - make sure you have g++ installed in your system - installation for Ubuntu and derivatives: `sudo apt install g++` diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 26e11733..54767052 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -15,7 +15,7 @@ Cemu comes with a `.clang-format` file which is supported by most IDEs for forma ## About types -Cemu provides it's own set of basic fixed-width types. They are: +Cemu provides its own set of basic fixed-width types. They are: `uint8`, `sint8`, `uint16`, `sint16`, `uint32`, `sint32`, `uint64`, `sint64`. Always use these types over something like `uint32_t`. Using `size_t` is also acceptable where suitable. Avoid C types like `int` or `long`. The only exception is when interacting with external libraries which expect these types as parameters. ## When and where to put brackets @@ -48,7 +48,7 @@ In UI related code you can use `formatWxString`, but be aware that number format ## Strings and encoding -We use UTF-8 encoded `std::string` where possible. Some conversations need special handling and we have helper functions for those: +We use UTF-8 encoded `std::string` where possible. Some conversions need special handling and we have helper functions for those: ```cpp // std::filesystem::path <-> std::string (in precompiled.h) std::string _pathToUtf8(const fs::path& path); @@ -69,7 +69,7 @@ If you want to write to log.txt use `cemuLog_log()`. The log type parameter shou A pretty large part of Cemu's code base are re-implementations of various Cafe OS modules (e.g. `coreinit.rpl`, `gx2.rpl`...). These generally run in the context of the emulated process, thus special care has to be taken to use types with the correct size and endianness when interacting with memory. -Keep in mind that the emulated Espresso CPU is 32bit big-endian, while the host architectures targeted by Cemu are 64bit litte-endian! +Keep in mind that the emulated Espresso CPU is 32bit big-endian, while the host architectures targeted by Cemu are 64bit little-endian! To keep code simple and remove the need for manual endian-swapping, Cemu has templates and aliases of the basic types with explicit endian-ness. For big-endian types add the suffix `be`. Example: `uint32be` diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp index 465439ba..5a75b406 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp @@ -28,16 +28,6 @@ namespace coreinit osLib_returnFromFunction64(hCPU, osTime); } - uint64 coreinit_getTimeBase_dummy() - { - return __rdtsc(); - } - - void export_OSGetSystemTimeDummy(PPCInterpreter_t* hCPU) - { - osLib_returnFromFunction64(hCPU, coreinit_getTimeBase_dummy()); - } - void export_OSGetSystemTime(PPCInterpreter_t* hCPU) { osLib_returnFromFunction64(hCPU, coreinit_getTimerTick()); @@ -371,14 +361,13 @@ namespace coreinit void InitializeTimeAndCalendar() { osLib_addFunction("coreinit", "OSGetTime", export_OSGetTime); - osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTimeDummy); + osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime); osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick); osLib_addFunction("coreinit", "OSGetSystemTick", export_OSGetSystemTick); cafeExportRegister("coreinit", OSTicksToCalendarTime, LogType::Placeholder); cafeExportRegister("coreinit", OSCalendarTimeToTicks, LogType::Placeholder); - osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime); //timeTest(); } From 8bb7ce098c7e439ff88421e431d30378c3f3739b Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:17:28 +0200 Subject: [PATCH 047/101] Bump CI clang version to 15 + workaround for unsafe fiber optimizations (#982) --- .github/workflows/build.yml | 10 ++--- .../workflows/deploy_experimental_release.yml | 4 +- .gitignore | 2 +- BUILD.md | 12 ++--- generate_vs_solution.bat | 2 - src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 3 +- src/Cafe/HW/Espresso/Debugger/GDBStub.cpp | 3 +- .../Interpreter/PPCInterpreterMain.cpp | 19 +++++--- src/Cafe/HW/Espresso/PPCCallback.h | 15 ++++--- src/Cafe/HW/Espresso/PPCScheduler.cpp | 19 ++++---- src/Cafe/HW/Espresso/PPCSchedulerLLE.cpp | 2 +- src/Cafe/HW/Espresso/PPCState.h | 3 +- .../Espresso/Recompiler/PPCRecompilerX64.cpp | 4 +- src/Cafe/IOSU/legacy/iosu_ioctl.cpp | 2 +- src/Cafe/OS/common/OSUtil.h | 2 +- src/Cafe/OS/libs/coreinit/coreinit.cpp | 4 +- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 2 +- src/Cafe/OS/libs/coreinit/coreinit_Init.cpp | 9 ++-- src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp | 8 ++-- src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 20 ++++----- .../OS/libs/coreinit/coreinit_ThreadQueue.cpp | 4 +- src/Cafe/OS/libs/gx2/GX2.cpp | 8 ++-- src/Cafe/OS/libs/gx2/GX2_Command.cpp | 23 +++++----- src/Cafe/OS/libs/gx2/GX2_Event.cpp | 2 +- src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp | 4 +- src/Cafe/OS/libs/nn_save/nn_save.cpp | 44 +++++++++---------- src/Cafe/OS/libs/snd_core/ax_aux.cpp | 14 +++--- src/Cafe/OS/libs/snd_core/ax_voice.cpp | 13 +++--- src/Cafe/OS/libs/zlib125/zlib125.cpp | 5 +-- .../ExceptionHandler/ExceptionHandler.cpp | 9 ++-- src/Common/precompiled.h | 11 +++-- 31 files changed, 150 insertions(+), 132 deletions(-) delete mode 100644 generate_vs_solution.bat diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb6ac099..d23faa31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ env: jobs: build-ubuntu: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: "Checkout repo" uses: actions/checkout@v3 @@ -53,7 +53,7 @@ jobs: - name: "Install system dependencies" run: | sudo apt update -qq - sudo apt install -y clang-12 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build + sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build - name: "Bootstrap vcpkg" run: | @@ -75,7 +75,7 @@ jobs: - name: "cmake" run: | - cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DPORTABLE=OFF -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja + cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DPORTABLE=OFF -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja - name: "Build Cemu" run: | @@ -93,7 +93,7 @@ jobs: path: ./bin/Cemu build-appimage: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: build-ubuntu steps: - name: Checkout Upstream Repo @@ -107,7 +107,7 @@ jobs: - name: "Install system dependencies" run: | sudo apt update -qq - sudo apt install -y clang-12 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream + sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream - name: "Build AppImage" run: | diff --git a/.github/workflows/deploy_experimental_release.yml b/.github/workflows/deploy_experimental_release.yml index 9296c1cc..3bf86db4 100644 --- a/.github/workflows/deploy_experimental_release.yml +++ b/.github/workflows/deploy_experimental_release.yml @@ -10,7 +10,7 @@ jobs: experimentalversion: ${{ github.run_number }} deploy: name: Deploy experimental release - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: call-release-build steps: - uses: actions/checkout@v3 @@ -72,7 +72,7 @@ jobs: ls ./bin/ cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu - zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-20.04-x64.zip ${{ env.CEMU_FOLDER_NAME }} + zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-22.04-x64.zip ${{ env.CEMU_FOLDER_NAME }} rm -r ./${{ env.CEMU_FOLDER_NAME }} - name: Create release from macos-bin diff --git a/.gitignore b/.gitignore index 9e9ff7df..18f14cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ .idea/ build/ -cmake-build-*-*/ +cmake-build-*/ out/ .cache/ bin/Cemu_* diff --git a/BUILD.md b/BUILD.md index c63251d7..e4993ca1 100644 --- a/BUILD.md +++ b/BUILD.md @@ -19,17 +19,17 @@ Any other IDE should also work as long as it has CMake and MSVC support. CLion a ## Linux -To compile Cemu, a recent enough compiler and STL with C++20 support is required! clang-12 or higher is what we recommend. +To compile Cemu, a recent enough compiler and STL with C++20 support is required! clang-15 or higher is what we recommend. ### Installing dependencies #### For Ubuntu and derivatives: -`sudo apt install -y cmake curl freeglut3-dev git libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build` +`sudo apt install -y cmake curl clang-15 freeglut3-dev git libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build` -*Additionally, for Ubuntu 22.04 only:* - - `sudo apt install -y clang-12` - - At step 3 while building, use - `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja` +You may also need to install `libusb-1.0-0-dev` as a workaround for an issue with the vcpkg hidapi package. + +At step 3 while building, use: + `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja` #### For Arch and derivatives: `sudo pacman -S --needed base-devel clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip` diff --git a/generate_vs_solution.bat b/generate_vs_solution.bat deleted file mode 100644 index 21060027..00000000 --- a/generate_vs_solution.bat +++ /dev/null @@ -1,2 +0,0 @@ -"C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO\2022\COMMUNITY\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -B build/ -pause \ No newline at end of file diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index 6c15f26e..e99ce522 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -210,7 +210,8 @@ void debugger_handleSingleStepException(uint64 dr6) } if (catchBP) { - debugger_createCodeBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT); + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + debugger_createCodeBreakpoint(hCPU->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT); } } diff --git a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp index d83ea46b..b7e15407 100644 --- a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp +++ b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp @@ -959,8 +959,9 @@ void GDBServer::HandleAccessException(uint64 dr6) if (!response.empty()) { + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); cemuLog_logDebug(LogType::Force, "Received matching breakpoint exception: {}", response); - auto nextInstructions = findNextInstruction(ppcInterpreterCurrentInstance->instructionPointer, ppcInterpreterCurrentInstance->spr.LR, ppcInterpreterCurrentInstance->spr.CTR); + auto nextInstructions = findNextInstruction(hCPU->instructionPointer, hCPU->spr.LR, hCPU->spr.CTR); for (MPTR nextInstr : nextInstructions) { auto bpIt = m_patchedInstructions.find(nextInstr); diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp index 2d808fef..a9ab49a5 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp @@ -6,7 +6,6 @@ thread_local PPCInterpreter_t* ppcInterpreterCurrentInstance; // main thread instruction counter and timing -volatile uint64 ppcMainThreadCycleCounter = 0; uint64 ppcMainThreadDECCycleValue = 0; // value that was set to dec register uint64 ppcMainThreadDECCycleStart = 0; // at which cycle the dec register was set, if == 0 -> dec is 0 uint64 ppcCyclesSince2000 = 0; @@ -29,11 +28,16 @@ PPCInterpreter_t* PPCInterpreter_createInstance(unsigned int Entrypoint) return pData; } -PPCInterpreter_t* PPCInterpreter_getCurrentInstance() +TLS_WORKAROUND_NOINLINE PPCInterpreter_t* PPCInterpreter_getCurrentInstance() { return ppcInterpreterCurrentInstance; } +TLS_WORKAROUND_NOINLINE void PPCInterpreter_setCurrentInstance(PPCInterpreter_t* hCPU) +{ + ppcInterpreterCurrentInstance = hCPU; +} + uint64 PPCInterpreter_getMainCoreCycleCounter() { return PPCTimer_getFromRDTSC(); @@ -78,24 +82,25 @@ uint32 PPCInterpreter_getCoreIndex(PPCInterpreter_t* hCPU) uint32 PPCInterpreter_getCurrentCoreIndex() { - return ppcInterpreterCurrentInstance->spr.UPIR; + return PPCInterpreter_getCurrentInstance()->spr.UPIR; }; uint8* PPCInterpreterGetStackPointer() { - return memory_getPointerFromVirtualOffset(ppcInterpreterCurrentInstance->gpr[1]); + return memory_getPointerFromVirtualOffset(PPCInterpreter_getCurrentInstance()->gpr[1]); } uint8* PPCInterpreterGetAndModifyStackPointer(sint32 offset) { - uint8* result = memory_getPointerFromVirtualOffset(ppcInterpreterCurrentInstance->gpr[1] - offset); - ppcInterpreterCurrentInstance->gpr[1] -= offset; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + uint8* result = memory_getPointerFromVirtualOffset(hCPU->gpr[1] - offset); + hCPU->gpr[1] -= offset; return result; } void PPCInterpreterModifyStackPointer(sint32 offset) { - ppcInterpreterCurrentInstance->gpr[1] -= offset; + PPCInterpreter_getCurrentInstance()->gpr[1] -= offset; } uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(PPCInterpreter_t* hCPU)); diff --git a/src/Cafe/HW/Espresso/PPCCallback.h b/src/Cafe/HW/Espresso/PPCCallback.h index fd790f0c..19fcd4d1 100644 --- a/src/Cafe/HW/Espresso/PPCCallback.h +++ b/src/Cafe/HW/Espresso/PPCCallback.h @@ -18,19 +18,20 @@ uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg, { cemu_assert_debug(data.gprCount <= 8); cemu_assert_debug(data.floatCount <= 8); + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); if constexpr (std::is_pointer_v) { - ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = MEMPTR(currentArg).GetMPTR(); + hCPU->gpr[3 + data.gprCount] = MEMPTR(currentArg).GetMPTR(); data.gprCount++; } else if constexpr (std::is_base_of_v>) { - ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = currentArg.GetMPTR(); + hCPU->gpr[3 + data.gprCount] = currentArg.GetMPTR(); data.gprCount++; } else if constexpr (std::is_reference_v) { - ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = MEMPTR(¤tArg).GetMPTR(); + hCPU->gpr[3 + data.gprCount] = MEMPTR(¤tArg).GetMPTR(); data.gprCount++; } else if constexpr(std::is_enum_v) @@ -40,19 +41,19 @@ uint32 PPCCoreCallback(MPTR function, PPCCoreCallbackData_t& data, T currentArg, } else if constexpr (std::is_floating_point_v) { - ppcInterpreterCurrentInstance->fpr[1 + data.floatCount].fpr = (double)currentArg; + hCPU->fpr[1 + data.floatCount].fpr = (double)currentArg; data.floatCount++; } else if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64)) { - ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = (uint32)(currentArg >> 32); // high - ppcInterpreterCurrentInstance->gpr[3 + data.gprCount + 1] = (uint32)currentArg; // low + hCPU->gpr[3 + data.gprCount] = (uint32)(currentArg >> 32); // high + hCPU->gpr[3 + data.gprCount + 1] = (uint32)currentArg; // low data.gprCount += 2; } else { - ppcInterpreterCurrentInstance->gpr[3 + data.gprCount] = (uint32)currentArg; + hCPU->gpr[3 + data.gprCount] = (uint32)currentArg; data.gprCount++; } diff --git a/src/Cafe/HW/Espresso/PPCScheduler.cpp b/src/Cafe/HW/Espresso/PPCScheduler.cpp index 2a3a4aaa..a4c04aaa 100644 --- a/src/Cafe/HW/Espresso/PPCScheduler.cpp +++ b/src/Cafe/HW/Espresso/PPCScheduler.cpp @@ -11,21 +11,24 @@ uint32 ppcThreadQuantum = 45000; // execute 45000 instructions before thread res void PPCInterpreter_relinquishTimeslice() { - if( ppcInterpreterCurrentInstance->remainingCycles >= 0 ) + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + if( hCPU->remainingCycles >= 0 ) { - ppcInterpreterCurrentInstance->skippedCycles = ppcInterpreterCurrentInstance->remainingCycles + 1; - ppcInterpreterCurrentInstance->remainingCycles = -1; + hCPU->skippedCycles = hCPU->remainingCycles + 1; + hCPU->remainingCycles = -1; } } void PPCCore_boostQuantum(sint32 numCycles) { - ppcInterpreterCurrentInstance->remainingCycles += numCycles; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + hCPU->remainingCycles += numCycles; } void PPCCore_deboostQuantum(sint32 numCycles) { - ppcInterpreterCurrentInstance->remainingCycles -= numCycles; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + hCPU->remainingCycles -= numCycles; } namespace coreinit @@ -36,7 +39,7 @@ namespace coreinit void PPCCore_switchToScheduler() { cemu_assert_debug(__OSHasSchedulerLock() == false); // scheduler lock must not be hold past thread time slice - cemu_assert_debug(ppcInterpreterCurrentInstance->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600); + cemu_assert_debug(PPCInterpreter_getCurrentInstance()->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600); __OSLockScheduler(); coreinit::__OSThreadSwitchToNext(); __OSUnlockScheduler(); @@ -45,7 +48,7 @@ void PPCCore_switchToScheduler() void PPCCore_switchToSchedulerWithLock() { cemu_assert_debug(__OSHasSchedulerLock() == true); // scheduler lock must be hold - cemu_assert_debug(ppcInterpreterCurrentInstance->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600); + cemu_assert_debug(PPCInterpreter_getCurrentInstance()->coreInterruptMask != 0 || CafeSystem::GetForegroundTitleId() == 0x000500001019e600); coreinit::__OSThreadSwitchToNext(); } @@ -58,7 +61,7 @@ void _PPCCore_callbackExit(PPCInterpreter_t* hCPU) PPCInterpreter_t* PPCCore_executeCallbackInternal(uint32 functionMPTR) { cemu_assert_debug(functionMPTR != 0); - PPCInterpreter_t* hCPU = ppcInterpreterCurrentInstance; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); // remember LR and instruction pointer uint32 lr = hCPU->spr.LR; uint32 ip = hCPU->instructionPointer; diff --git a/src/Cafe/HW/Espresso/PPCSchedulerLLE.cpp b/src/Cafe/HW/Espresso/PPCSchedulerLLE.cpp index 8ef54256..bb4bf9ff 100644 --- a/src/Cafe/HW/Espresso/PPCSchedulerLLE.cpp +++ b/src/Cafe/HW/Espresso/PPCSchedulerLLE.cpp @@ -220,7 +220,7 @@ void PPCCoreLLE_startSingleCoreScheduler(uint32 entrypoint) for (uint32 coreIndex = 0; coreIndex < 3; coreIndex++) { PPCInterpreter_t* hCPU = cpuContext->cores+coreIndex; - ppcInterpreterCurrentInstance = hCPU; + PPCInterpreter_setCurrentInstance(hCPU); if (coreIndex == 1) { // check SCR core 1 enable bit diff --git a/src/Cafe/HW/Espresso/PPCState.h b/src/Cafe/HW/Espresso/PPCState.h index 2b30326b..85b2dc04 100644 --- a/src/Cafe/HW/Espresso/PPCState.h +++ b/src/Cafe/HW/Espresso/PPCState.h @@ -149,6 +149,7 @@ static uint64 PPCInterpreter_getCallParamU64(PPCInterpreter_t* hCPU, uint32 inde PPCInterpreter_t* PPCInterpreter_createInstance(unsigned int Entrypoint); PPCInterpreter_t* PPCInterpreter_getCurrentInstance(); +void PPCInterpreter_setCurrentInstance(PPCInterpreter_t* hCPU); uint64 PPCInterpreter_getMainCoreCycleCounter(); @@ -192,7 +193,6 @@ uint32 PPCInterpreter_getCurrentCoreIndex(); void PPCInterpreter_setDEC(PPCInterpreter_t* hCPU, uint32 newValue); // timing for main processor -extern volatile uint64 ppcMainThreadCycleCounter; extern uint64 ppcCyclesSince2000; // on init this is set to the cycles that passed since 1.1.2000 extern uint64 ppcCyclesSince2000TimerClock; // on init this is set to the cycles that passed since 1.1.2000 / 20 extern uint64 ppcCyclesSince2000_UTC; @@ -213,7 +213,6 @@ void PPCTimer_start(); // core info and control extern uint32 ppcThreadQuantum; -extern thread_local PPCInterpreter_t *ppcInterpreterCurrentInstance; uint8* PPCInterpreterGetAndModifyStackPointer(sint32 offset); uint8* PPCInterpreterGetStackPointer(); void PPCInterpreterModifyStackPointer(sint32 offset); diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp index 14d2febb..a30295b5 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerX64.cpp @@ -100,7 +100,7 @@ void* ATTR_MS_ABI PPCRecompiler_virtualHLE(PPCInterpreter_t* hCPU, uint32 hleFun hCPU->remainingCycles -= 500; // let subtract about 500 cycles for each HLE call hCPU->gpr[3] = 0; PPCInterpreter_nextInstruction(hCPU); - return ppcInterpreterCurrentInstance; + return hCPU; } else { @@ -109,7 +109,7 @@ void* ATTR_MS_ABI PPCRecompiler_virtualHLE(PPCInterpreter_t* hCPU, uint32 hleFun hleCall(hCPU); } hCPU->rspTemp = prevRSPTemp; - return ppcInterpreterCurrentInstance; + return PPCInterpreter_getCurrentInstance(); } void ATTR_MS_ABI PPCRecompiler_getTBL(PPCInterpreter_t* hCPU, uint32 gprIndex) diff --git a/src/Cafe/IOSU/legacy/iosu_ioctl.cpp b/src/Cafe/IOSU/legacy/iosu_ioctl.cpp index 00be31b0..1fc2a27a 100644 --- a/src/Cafe/IOSU/legacy/iosu_ioctl.cpp +++ b/src/Cafe/IOSU/legacy/iosu_ioctl.cpp @@ -23,7 +23,7 @@ sint32 iosuIoctl_pushAndWait(uint32 ioctlHandle, ioQueueEntry_t* ioQueueEntry) } __OSLockScheduler(); ioctlMutex.lock(); - ioQueueEntry->ppcThread = coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance); + ioQueueEntry->ppcThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); _ioctlRingbuffer[ioctlHandle].Push(ioQueueEntry); ioctlMutex.unlock(); diff --git a/src/Cafe/OS/common/OSUtil.h b/src/Cafe/OS/common/OSUtil.h index 9bf8480b..6801f6af 100644 --- a/src/Cafe/OS/common/OSUtil.h +++ b/src/Cafe/OS/common/OSUtil.h @@ -65,7 +65,7 @@ public: } else if constexpr (std::is_floating_point_v) { - v = (T)ppcInterpreterCurrentInstance->fpr[1 + fprIndex].fpr; + v = (T)hCPU->fpr[1 + fprIndex].fpr; fprIndex++; } else diff --git a/src/Cafe/OS/libs/coreinit/coreinit.cpp b/src/Cafe/OS/libs/coreinit/coreinit.cpp index e8e4ce1f..8738e3a4 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit.cpp @@ -204,7 +204,7 @@ namespace coreinit { sint32 OSGetCoreId() { - return PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + return PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance()); } uint32 OSGetCoreCount() @@ -239,7 +239,7 @@ namespace coreinit uint32 OSGetStackPointer() { - return ppcInterpreterCurrentInstance->gpr[1]; + return PPCInterpreter_getCurrentInstance()->gpr[1]; } void coreinitExport_ENVGetEnvironmentVariable(PPCInterpreter_t* hCPU) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index a2f59d4d..916563c8 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -691,7 +691,7 @@ namespace coreinit while (OSSendMessage(ioMsgQueue, &fsCmdBlockBody->asyncResult.msgUnion.osMsg, 0) == 0) { cemuLog_log(LogType::Force, "FS driver: Failed to add message to result queue. Retrying..."); - if (ppcInterpreterCurrentInstance) + if (PPCInterpreter_getCurrentInstance()) PPCCore_switchToScheduler(); else std::this_thread::sleep_for(std::chrono::milliseconds(10)); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Init.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Init.cpp index 51a3f542..72f6ac11 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Init.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Init.cpp @@ -142,10 +142,11 @@ void CafeInit() } } // setup UGQR - ppcInterpreterCurrentInstance->spr.UGQR[0 + 2] = 0x00040004; - ppcInterpreterCurrentInstance->spr.UGQR[0 + 3] = 0x00050005; - ppcInterpreterCurrentInstance->spr.UGQR[0 + 4] = 0x00060006; - ppcInterpreterCurrentInstance->spr.UGQR[0 + 5] = 0x00070007; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + hCPU->spr.UGQR[0 + 2] = 0x00040004; + hCPU->spr.UGQR[0 + 3] = 0x00050005; + hCPU->spr.UGQR[0 + 4] = 0x00060006; + hCPU->spr.UGQR[0 + 5] = 0x00070007; coreinit::InitForegroundBucket(); coreinit::InitSysHeap(); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp index 05660c71..d5cd0018 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp @@ -235,7 +235,7 @@ namespace coreinit sint32 __os_snprintf(char* outputStr, sint32 maxLength, const char* formatStr) { - sint32 r = ppcSprintf(formatStr, outputStr, maxLength, ppcInterpreterCurrentInstance, 3); + sint32 r = ppcSprintf(formatStr, outputStr, maxLength, PPCInterpreter_getCurrentInstance(), 3); return r; } @@ -303,7 +303,7 @@ namespace coreinit void OSReport(const char* format) { char buffer[1024 * 2]; - sint32 len = ppcSprintf(format, buffer, sizeof(buffer), ppcInterpreterCurrentInstance, 1); + sint32 len = ppcSprintf(format, buffer, sizeof(buffer), PPCInterpreter_getCurrentInstance(), 1); WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len); } @@ -316,7 +316,7 @@ namespace coreinit { char buffer[1024 * 2]; int prefixLen = sprintf(buffer, "[COSWarn-%d] ", moduleId); - sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, ppcInterpreterCurrentInstance, 2); + sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, PPCInterpreter_getCurrentInstance(), 2); WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len + prefixLen); } @@ -324,7 +324,7 @@ namespace coreinit { char buffer[1024 * 2]; int prefixLen = sprintf(buffer, "[OSLogPrintf-%d-%d-%d] ", ukn1, ukn2, ukn3); - sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, ppcInterpreterCurrentInstance, 4); + sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, PPCInterpreter_getCurrentInstance(), 4); WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len + prefixLen); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 59bd034e..71e5d493 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -305,7 +305,7 @@ namespace coreinit affinityMask = attr & 0x7; // if no core is selected -> set current one if (affinityMask == 0) - affinityMask |= (1 << PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance)); + affinityMask |= (1 << PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance())); // set attr // todo: Support for other attr bits thread->attr = (affinityMask & 0xFF) | (attr & OSThread_t::ATTR_BIT::ATTR_DETACHED); @@ -325,7 +325,7 @@ namespace coreinit { __OSLockScheduler(); - cemu_assert_debug(ppcInterpreterCurrentInstance == nullptr || OSGetCurrentThread() != thread); // called on self, what should this function do? + cemu_assert_debug(PPCInterpreter_getCurrentInstance() == nullptr || OSGetCurrentThread() != thread); // called on self, what should this function do? if (thread->state != OSThread_t::THREAD_STATE::STATE_NONE && thread->state != OSThread_t::THREAD_STATE::STATE_MORIBUND) { @@ -607,7 +607,7 @@ namespace coreinit // todo - only set this once? thread->wakeUpTime = PPCInterpreter_getMainCoreCycleCounter(); // reschedule if thread has higher priority - if (ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) + if (PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) PPCCore_switchToSchedulerWithLock(); } return previousSuspendCount; @@ -930,17 +930,17 @@ 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)ppcInterpreterCurrentInstance->remainingCycles, (uint64)thread->quantumTicks); + uint64 remainingCycles = std::min((uint64)hCPU->remainingCycles, (uint64)thread->quantumTicks); uint64 executedCycles = thread->quantumTicks - remainingCycles; - if (executedCycles < ppcInterpreterCurrentInstance->skippedCycles) + if (executedCycles < hCPU->skippedCycles) executedCycles = 0; else - executedCycles -= ppcInterpreterCurrentInstance->skippedCycles; + executedCycles -= hCPU->skippedCycles; thread->totalCycles += executedCycles; // store context and set current thread to null __OSThreadStoreContext(hCPU, thread); OSSetCurrentThread(OSGetCoreId(), nullptr); - ppcInterpreterCurrentInstance = nullptr; + PPCInterpreter_setCurrentInstance(nullptr); } void __OSLoadThread(OSThread_t* thread, PPCInterpreter_t* hCPU, uint32 coreIndex) @@ -951,7 +951,7 @@ namespace coreinit hCPU->reservedMemValue = 0; hCPU->spr.UPIR = coreIndex; hCPU->coreInterruptMask = 1; - ppcInterpreterCurrentInstance = hCPU; + PPCInterpreter_setCurrentInstance(hCPU); OSSetCurrentThread(OSGetCoreId(), thread); __OSThreadLoadContext(hCPU, thread); thread->context.upir = coreIndex; @@ -1076,7 +1076,7 @@ namespace coreinit // store context of current thread __OSStoreThread(OSGetCurrentThread(), &hostThread->ppcInstance); - cemu_assert_debug(ppcInterpreterCurrentInstance == nullptr); + cemu_assert_debug(PPCInterpreter_getCurrentInstance() == nullptr); if (!sSchedulerActive.load(std::memory_order::relaxed)) { @@ -1165,7 +1165,7 @@ namespace coreinit // create scheduler idle fiber and switch to it g_idleLoopFiber[t_assignedCoreIndex] = new Fiber(__OSThreadCoreIdle, nullptr, nullptr); - cemu_assert_debug(ppcInterpreterCurrentInstance == nullptr); + cemu_assert_debug(PPCInterpreter_getCurrentInstance() == nullptr); __OSLockScheduler(); Fiber::Switch(*g_idleLoopFiber[t_assignedCoreIndex]); // returned from scheduler loop, exit thread diff --git a/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp b/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp index 38302d5a..68cb22b3 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp @@ -139,7 +139,7 @@ namespace coreinit thread->state = OSThread_t::THREAD_STATE::STATE_READY; thread->currentWaitQueue = nullptr; coreinit::__OSAddReadyThreadToRunQueue(thread); - if (reschedule && thread->suspendCounter == 0 && ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) + if (reschedule && thread->suspendCounter == 0 && PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) shouldReschedule = true; } if (shouldReschedule) @@ -159,7 +159,7 @@ namespace coreinit thread->state = OSThread_t::THREAD_STATE::STATE_READY; thread->currentWaitQueue = nullptr; coreinit::__OSAddReadyThreadToRunQueue(thread); - if (reschedule && thread->suspendCounter == 0 && ppcInterpreterCurrentInstance && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) + if (reschedule && thread->suspendCounter == 0 && PPCInterpreter_getCurrentInstance() && __OSCoreShouldSwitchToThread(coreinit::OSGetCurrentThread(), thread)) shouldReschedule = true; } if (shouldReschedule) diff --git a/src/Cafe/OS/libs/gx2/GX2.cpp b/src/Cafe/OS/libs/gx2/GX2.cpp index 8c3fbc64..82aef164 100644 --- a/src/Cafe/OS/libs/gx2/GX2.cpp +++ b/src/Cafe/OS/libs/gx2/GX2.cpp @@ -4,8 +4,8 @@ #include "GX2.h" #include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/OS/libs/coreinit/coreinit_Time.h" +#include "Cafe/OS/libs/coreinit/coreinit_Thread.h" #include "Cafe/CafeSystem.h" - #include "Cafe/HW/Latte/Core/LattePM4.h" #include "GX2_Command.h" @@ -68,7 +68,7 @@ void gx2Export_GX2SwapScanBuffers(PPCInterpreter_t* hCPU) // Orochi Warriors seems to call GX2SwapScanBuffers on arbitrary threads/cores. The PM4 commands should go through to the GPU as long as there is no active display list and no other core is submitting commands simultaneously // right now, we work around this by avoiding the infinite loop below (request counter incremented, but PM4 not sent) - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = coreinit::OSGetCoreId(); if (GX2::sGX2MainCoreIndex == coreIndex) LatteGPUState.sharedArea->flipRequestCountBE = _swapEndianU32(_swapEndianU32(LatteGPUState.sharedArea->flipRequestCountBE) + 1); @@ -332,7 +332,7 @@ uint64 Latte_GetTime() void _GX2SubmitToTCL() { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance()); // do nothing if called from non-main GX2 core if (GX2::sGX2MainCoreIndex != coreIndex) { @@ -373,7 +373,7 @@ uint32 _GX2GetUnflushedBytes(uint32 coreIndex) */ void GX2ReserveCmdSpace(uint32 reservedFreeSpaceInU32) { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = coreinit::OSGetCoreId(); // if we are in a display list then do nothing if( gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL ) return; diff --git a/src/Cafe/OS/libs/gx2/GX2_Command.cpp b/src/Cafe/OS/libs/gx2/GX2_Command.cpp index 6da19741..804e3da0 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Command.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Command.cpp @@ -3,6 +3,7 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/HW/Latte/Core/LattePM4.h" #include "Cafe/OS/libs/coreinit/coreinit.h" +#include "Cafe/OS/libs/coreinit/coreinit_Thread.h" #include "Cafe/HW/Latte/ISA/RegDefines.h" #include "GX2.h" #include "GX2_Command.h" @@ -15,7 +16,7 @@ GX2WriteGatherPipeState gx2WriteGatherPipe = { 0 }; void gx2WriteGather_submitU32AsBE(uint32 v) { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance()); if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == NULL) return; *(uint32*)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) = _swapEndianU32(v); @@ -24,7 +25,7 @@ void gx2WriteGather_submitU32AsBE(uint32 v) void gx2WriteGather_submitU32AsLE(uint32 v) { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance()); if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == NULL) return; *(uint32*)(*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]) = v; @@ -33,7 +34,7 @@ void gx2WriteGather_submitU32AsLE(uint32 v) void gx2WriteGather_submitU32AsLEArray(uint32* v, uint32 numValues) { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance()); if (gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex] == NULL) return; memcpy_dwords((*gx2WriteGatherPipe.writeGatherPtrWrite[coreIndex]), v, numValues); @@ -134,7 +135,7 @@ namespace GX2 bool GX2GetCurrentDisplayList(betype* displayListAddr, uint32be* displayListSize) { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = coreinit::OSGetCoreId(); if (gx2WriteGatherPipe.displayListStart[coreIndex] == MPTR_NULL) return false; @@ -149,13 +150,13 @@ namespace GX2 bool GX2GetDisplayListWriteStatus() { // returns true if we are writing to a display list - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = coreinit::OSGetCoreId(); return gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL; } bool GX2WriteGather_isDisplayListActive() { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = coreinit::OSGetCoreId(); if (gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL) return true; return false; @@ -171,7 +172,7 @@ namespace GX2 void GX2WriteGather_checkAndInsertWrapAroundMark() { - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = coreinit::OSGetCoreId(); if (coreIndex != sGX2MainCoreIndex) // only if main gx2 core return; if (gx2WriteGatherPipe.displayListStart[coreIndex] != MPTR_NULL) @@ -187,18 +188,18 @@ namespace GX2 void GX2BeginDisplayList(MEMPTR displayListAddr, uint32 size) { - GX2WriteGather_beginDisplayList(ppcInterpreterCurrentInstance, displayListAddr.GetMPTR(), size); + GX2WriteGather_beginDisplayList(PPCInterpreter_getCurrentInstance(), displayListAddr.GetMPTR(), size); } void GX2BeginDisplayListEx(MEMPTR displayListAddr, uint32 size, bool profiling) { - GX2WriteGather_beginDisplayList(ppcInterpreterCurrentInstance, displayListAddr.GetMPTR(), size); + GX2WriteGather_beginDisplayList(PPCInterpreter_getCurrentInstance(), displayListAddr.GetMPTR(), size); } uint32 GX2EndDisplayList(MEMPTR displayListAddr) { cemu_assert_debug(displayListAddr != nullptr); - uint32 displayListSize = GX2WriteGather_endDisplayList(ppcInterpreterCurrentInstance, displayListAddr.GetMPTR()); + uint32 displayListSize = GX2WriteGather_endDisplayList(PPCInterpreter_getCurrentInstance(), displayListAddr.GetMPTR()); return displayListSize; } @@ -220,7 +221,7 @@ namespace GX2 // its basically a way to manually submit a command buffer to the GPU // as such it also affects the submission and retire timestamps - uint32 coreIndex = PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance); + uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance()); cemu_assert_debug(coreIndex == sGX2MainCoreIndex); coreIndex = sGX2MainCoreIndex; // always submit to main queue which is owned by GX2 main core (TCLSubmitToRing does not need this workaround) diff --git a/src/Cafe/OS/libs/gx2/GX2_Event.cpp b/src/Cafe/OS/libs/gx2/GX2_Event.cpp index ba498477..9748e20b 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Event.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Event.cpp @@ -263,7 +263,7 @@ namespace GX2 gx2WriteGather_submitU32AsBE(0x00000000); // unused } // flush pipeline - if (_GX2GetUnflushedBytes(PPCInterpreter_getCoreIndex(ppcInterpreterCurrentInstance)) > 0) + if (_GX2GetUnflushedBytes(coreinit::OSGetCoreId()) > 0) _GX2SubmitToTCL(); uint64 ts = GX2GetLastSubmittedTimeStamp(); diff --git a/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp b/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp index 9afb9f85..ce9501ab 100644 --- a/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp +++ b/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp @@ -225,7 +225,7 @@ void CurlWorkerThread(CURL_t* curl, PPCConcurrentQueue* callerQueue, uint32 SendOrderToWorker(CURL_t* curl, QueueOrder order, uint32 arg1 = 0) { - OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance); + OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); curl->curlThread = currentThread; // cemuLog_logDebug(LogType::Force, "CURRENTTHREAD: 0x{} -> {}",currentThread, order) @@ -707,7 +707,7 @@ void export_curl_easy_init(PPCInterpreter_t* hCPU) memset(result.GetPtr(), 0, sizeof(CURL_t)); *result = {}; result->curl = curl_easy_init(); - result->curlThread = coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance); + result->curlThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); result->info_contentType = nullptr; result->info_redirectUrl = nullptr; diff --git a/src/Cafe/OS/libs/nn_save/nn_save.cpp b/src/Cafe/OS/libs/nn_save/nn_save.cpp index 109c00d2..1311dd46 100644 --- a/src/Cafe/OS/libs/nn_save/nn_save.cpp +++ b/src/Cafe/OS/libs/nn_save/nn_save.cpp @@ -451,14 +451,14 @@ namespace save asyncParams.userCallback = PPCInterpreter_makeCallableExportDepr(AsyncCallback); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetPointer(); SAVEStatus status = SAVEOpenFileOtherApplicationAsync(client, block, titleId, accountSlot, path, mode, hFile, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -685,14 +685,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEGetFreeSpaceSizeAsync(client, block, accountSlot, freeSize, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -754,14 +754,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVERemoveAsync(client, block, accountSlot, path, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -867,14 +867,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEOpenDirAsync(client, block, accountSlot, path, hDir, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -940,14 +940,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEOpenDirOtherApplicationAsync(client, block, titleId, accountSlot, path, hDir, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1076,14 +1076,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEMakeDirAsync(client, block, accountSlot, path, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1127,14 +1127,14 @@ namespace save asyncParams.userCallback = PPCInterpreter_makeCallableExportDepr(AsyncCallback); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetPointer(); SAVEStatus status = SAVEOpenFileAsync(client, block, accountSlot, path, mode, hFile, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1187,14 +1187,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEGetStatAsync(client, block, accountSlot, path, stat, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1238,14 +1238,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEGetStatOtherApplicationAsync(client, block, titleId, accountSlot, path, stat, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1427,14 +1427,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEChangeDirAsync(client, block, accountSlot, path, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1496,14 +1496,14 @@ namespace save asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEFlushQuotaAsync(client, block, accountSlot, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance), 1000); + coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); PPCCore_switchToScheduler(); return param->returnStatus; } diff --git a/src/Cafe/OS/libs/snd_core/ax_aux.cpp b/src/Cafe/OS/libs/snd_core/ax_aux.cpp index 837adf06..a9176562 100644 --- a/src/Cafe/OS/libs/snd_core/ax_aux.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_aux.cpp @@ -218,9 +218,10 @@ namespace snd_core AXAUXCBCHANNELINFO* cbStruct = __AXAuxCB_auxCBStruct.GetPtr(); cbStruct->numChannels = tvChannelCount; cbStruct->numSamples = sampleCount; - ppcInterpreterCurrentInstance->gpr[3] = __AXAuxCB_dataPtrs.GetMPTR(); - ppcInterpreterCurrentInstance->gpr[4] = __AXAuxTVCallbackUserParam[auxBusIndex]; - ppcInterpreterCurrentInstance->gpr[5] = __AXAuxCB_auxCBStruct.GetMPTR(); + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + hCPU->gpr[3] = __AXAuxCB_dataPtrs.GetMPTR(); + hCPU->gpr[4] = __AXAuxTVCallbackUserParam[auxBusIndex]; + hCPU->gpr[5] = __AXAuxCB_auxCBStruct.GetMPTR(); PPCCore_executeCallbackInternal(auxCBFuncMPTR); } else @@ -255,9 +256,10 @@ namespace snd_core AXAUXCBCHANNELINFO* cbStruct = __AXAuxCB_auxCBStruct.GetPtr(); cbStruct->numChannels = drcChannelCount; cbStruct->numSamples = sampleCount; - ppcInterpreterCurrentInstance->gpr[3] = __AXAuxCB_dataPtrs.GetMPTR(); - ppcInterpreterCurrentInstance->gpr[4] = __AXAuxDRCCallbackUserParam[auxBusIndex + drcIndex * 3]; - ppcInterpreterCurrentInstance->gpr[5] = __AXAuxCB_auxCBStruct.GetMPTR(); + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + hCPU->gpr[3] = __AXAuxCB_dataPtrs.GetMPTR(); + hCPU->gpr[4] = __AXAuxDRCCallbackUserParam[auxBusIndex + drcIndex * 3]; + hCPU->gpr[5] = __AXAuxCB_auxCBStruct.GetMPTR(); PPCCore_executeCallbackInternal(auxCBFuncMPTR); } else diff --git a/src/Cafe/OS/libs/snd_core/ax_voice.cpp b/src/Cafe/OS/libs/snd_core/ax_voice.cpp index 8a49b0f4..877eecab 100644 --- a/src/Cafe/OS/libs/snd_core/ax_voice.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_voice.cpp @@ -3,6 +3,7 @@ #include "Cafe/HW/Espresso/PPCCallback.h" #include "Cafe/OS/libs/snd_core/ax.h" #include "Cafe/OS/libs/snd_core/ax_internal.h" +#include "Cafe/OS/libs/coreinit/coreinit_Thread.h" #include "util/helpers/fspinlock.h" namespace snd_core @@ -120,7 +121,7 @@ namespace snd_core { return -2; } - MPTR currentThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + MPTR currentThreadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); for (sint32 i = __AXUserProtectionArraySize - 1; i >= 0; i--) { if (__AXUserProtectionArray[i].threadMPTR == currentThreadMPTR) @@ -151,7 +152,7 @@ namespace snd_core PPCCore_deboostQuantum(10000); if (AXIst_IsFrameBeingProcessed()) return -2; - MPTR currentThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + MPTR currentThreadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); for (sint32 i = __AXUserProtectionArraySize - 1; i >= 0; i--) { if (__AXUserProtectionArray[i].threadMPTR == currentThreadMPTR) @@ -206,7 +207,7 @@ namespace snd_core if (AXIst_IsFrameBeingProcessed()) isProtected = __AXVoiceProtection[index].threadMPTR != MPTR_NULL; else - isProtected = __AXVoiceProtection[index].threadMPTR != coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + isProtected = __AXVoiceProtection[index].threadMPTR != memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); return isProtected; } @@ -219,7 +220,7 @@ namespace snd_core return; if (__AXVoiceProtection[index].threadMPTR == MPTR_NULL) { - __AXVoiceProtection[index].threadMPTR = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + __AXVoiceProtection[index].threadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); // does not set count? } } @@ -246,7 +247,7 @@ namespace snd_core } if (AXIst_IsFrameBeingProcessed()) return -2; - MPTR currentThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + MPTR currentThreadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); if (__AXVoiceProtection[index].threadMPTR == MPTR_NULL) { __AXVoiceProtection[index].threadMPTR = currentThreadMPTR; @@ -286,7 +287,7 @@ namespace snd_core } if (AXIst_IsFrameBeingProcessed()) return -2; - MPTR currentThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(ppcInterpreterCurrentInstance); + MPTR currentThreadMPTR = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); if (__AXVoiceProtection[index].threadMPTR == currentThreadMPTR) { if (__AXVoiceProtection[index].count > 0) diff --git a/src/Cafe/OS/libs/zlib125/zlib125.cpp b/src/Cafe/OS/libs/zlib125/zlib125.cpp index 72855c61..25df6a9d 100644 --- a/src/Cafe/OS/libs/zlib125/zlib125.cpp +++ b/src/Cafe/OS/libs/zlib125/zlib125.cpp @@ -28,8 +28,7 @@ static_assert(sizeof(z_stream_ppc2) == 0x38); voidpf zcallocWrapper(voidpf opaque, uInt items, uInt size) { z_stream_ppc2* zstream = (z_stream_ppc2*)opaque; - - PPCInterpreter_t* hCPU = ppcInterpreterCurrentInstance; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); hCPU->gpr[3] = zstream->opaque.GetMPTR(); hCPU->gpr[4] = items; hCPU->gpr[5] = size; @@ -41,7 +40,7 @@ voidpf zcallocWrapper(voidpf opaque, uInt items, uInt size) void zcfreeWrapper(voidpf opaque, voidpf baseIndex) { z_stream_ppc2* zstream = (z_stream_ppc2*)opaque; - PPCInterpreter_t* hCPU = ppcInterpreterCurrentInstance; + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); hCPU->gpr[3] = zstream->opaque.GetMPTR(); hCPU->gpr[4] = memory_getVirtualOffsetFromPointer(baseIndex); PPCCore_executeCallbackInternal(zstream->zfree.GetMPTR()); diff --git a/src/Common/ExceptionHandler/ExceptionHandler.cpp b/src/Common/ExceptionHandler/ExceptionHandler.cpp index 5308ea8e..5fefc8ca 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler.cpp @@ -73,16 +73,17 @@ void ExceptionHandler_LogGeneralInfo() // info about active PPC instance: CrashLog_WriteLine(""); CrashLog_WriteHeader("Active PPC instance"); - if (ppcInterpreterCurrentInstance) + PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); + if (hCPU) { OSThread_t* currentThread = coreinit::OSGetCurrentThread(); uint32 threadPtr = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread()); - sprintf(dumpLine, "IP 0x%08x LR 0x%08x Thread 0x%08x", ppcInterpreterCurrentInstance->instructionPointer, ppcInterpreterCurrentInstance->spr.LR, threadPtr); + sprintf(dumpLine, "IP 0x%08x LR 0x%08x Thread 0x%08x", hCPU->instructionPointer, hCPU->spr.LR, threadPtr); CrashLog_WriteLine(dumpLine); // GPR info CrashLog_WriteLine(""); - auto gprs = ppcInterpreterCurrentInstance->gpr; + auto gprs = hCPU->gpr; sprintf(dumpLine, "r0 =%08x r1 =%08x r2 =%08x r3 =%08x r4 =%08x r5 =%08x r6 =%08x r7 =%08x", gprs[0], gprs[1], gprs[2], gprs[3], gprs[4], gprs[5], gprs[6], gprs[7]); CrashLog_WriteLine(dumpLine); sprintf(dumpLine, "r8 =%08x r9 =%08x r10=%08x r11=%08x r12=%08x r13=%08x r14=%08x r15=%08x", gprs[8], gprs[9], gprs[10], gprs[11], gprs[12], gprs[13], gprs[14], gprs[15]); @@ -93,7 +94,7 @@ void ExceptionHandler_LogGeneralInfo() CrashLog_WriteLine(dumpLine); // stack trace - MPTR currentStackVAddr = ppcInterpreterCurrentInstance->gpr[1]; + MPTR currentStackVAddr = hCPU->gpr[1]; CrashLog_WriteLine(""); CrashLog_WriteHeader("PPC stack trace"); DebugLogStackTrace(currentThread, currentStackVAddr); diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 60495f53..c55314d5 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -236,12 +236,17 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor, #if defined(_MSC_VER) #define UNREACHABLE __assume(false) #define ASSUME(__cond) __assume(__cond) -#elif defined(__GNUC__) + #define TLS_WORKAROUND_NOINLINE // no-op for MSVC as it has a flag for fiber-safe TLS optimizations +#elif defined(__GNUC__) && !defined(__llvm__) #define UNREACHABLE __builtin_unreachable() #define ASSUME(__cond) __attribute__((assume(__cond))) + #define TLS_WORKAROUND_NOINLINE __attribute__((noinline)) +#elif defined(__clang__) + #define UNREACHABLE __builtin_unreachable() + #define ASSUME(__cond) __builtin_assume(__cond) + #define TLS_WORKAROUND_NOINLINE __attribute__((noinline)) #else - #define UNREACHABLE - #define ASSUME(__cond) + #error Unknown compiler #endif #if defined(_MSC_VER) From ce34b95b82399923df88d12d7783c9318acc6320 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 30 Sep 2023 03:07:49 +0200 Subject: [PATCH 048/101] Fix game path not respecting utf8 encoding --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f7a66bf9..1ccc2805 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -139,7 +139,7 @@ void CemuCommonInit() // init title list CafeTitleList::Initialize(ActiveSettings::GetUserDataPath("title_list_cache.xml")); for (auto& it : GetConfig().game_paths) - CafeTitleList::AddScanPath(it); + CafeTitleList::AddScanPath(_utf8ToPath(it)); fs::path mlcPath = ActiveSettings::GetMlcPath(); if (!mlcPath.empty()) CafeTitleList::SetMLCPath(mlcPath); From 43976ca7ebbc91fc4eb6cd4822ae240301095b53 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 30 Sep 2023 06:21:14 +0200 Subject: [PATCH 049/101] Prioritize non-NUS format over NUS If a title exists multiple times in the game folder in different formats, then prefer and use non-NUS format if one is available. This is so we match previous Cemu behavior where Cemu would pick non-NUS simply due the fact that NUS format wasn't supported yet. --- src/Cafe/TitleList/GameInfo.h | 31 +++++++++++++++++------ src/Cafe/TitleList/TitleList.cpp | 3 +-- src/gui/components/wxTitleManagerList.cpp | 4 +-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Cafe/TitleList/GameInfo.h b/src/Cafe/TitleList/GameInfo.h index d1c557ab..8836d1e4 100644 --- a/src/Cafe/TitleList/GameInfo.h +++ b/src/Cafe/TitleList/GameInfo.h @@ -27,17 +27,13 @@ public: void SetBase(const TitleInfo& titleInfo) { - m_base = titleInfo; + if (IsPrioritizedVersionOrFormat(m_base, titleInfo)) + m_base = titleInfo; } void SetUpdate(const TitleInfo& titleInfo) { - if (HasUpdate()) - { - if (titleInfo.GetAppTitleVersion() > m_update.GetAppTitleVersion()) - m_update = titleInfo; - } - else + if (IsPrioritizedVersionOrFormat(m_update, titleInfo)) m_update = titleInfo; } @@ -53,7 +49,7 @@ public: auto it = std::find_if(m_aoc.begin(), m_aoc.end(), [aocTitleId](const TitleInfo& rhs) { return rhs.GetAppTitleId() == aocTitleId; }); if (it != m_aoc.end()) { - if(it->GetAppTitleVersion() >= aocVersion) + if (!IsPrioritizedVersionOrFormat(*it, titleInfo)) return; m_aoc.erase(it); } @@ -126,6 +122,25 @@ public: } private: + bool IsPrioritizedVersionOrFormat(const TitleInfo& currentTitle, const TitleInfo& newTitle) + { + if (!currentTitle.IsValid()) + return true; // always prefer a valid title over an invalid one + // always prefer higher version + if (newTitle.GetAppTitleVersion() > currentTitle.GetAppTitleVersion()) + return true; + // never prefer lower version + if (newTitle.GetAppTitleVersion() < currentTitle.GetAppTitleVersion()) + return false; + // for users which have both NUS and non-NUS titles in their games folder we want to prioritize non-NUS formats + // this is to stay consistent with previous Cemu versions which did not support NUS format at all + TitleInfo::TitleDataFormat currentFormat = currentTitle.GetFormat(); + TitleInfo::TitleDataFormat newFormat = newTitle.GetFormat(); + if (currentFormat != newFormat && currentFormat == TitleInfo::TitleDataFormat::NUS) + return true; + return true; + }; + TitleInfo m_base; TitleInfo m_update; std::vector m_aoc; diff --git a/src/Cafe/TitleList/TitleList.cpp b/src/Cafe/TitleList/TitleList.cpp index 03fd0855..1cc084b8 100644 --- a/src/Cafe/TitleList/TitleList.cpp +++ b/src/Cafe/TitleList/TitleList.cpp @@ -633,8 +633,7 @@ GameInfo2 CafeTitleList::GetGameInfo(TitleId titleId) uint64 baseTitleId; if (!FindBaseTitleId(titleId, baseTitleId)) { - cemuLog_logDebug(LogType::Force, "Failed to translate title id in GetGameInfo()"); - return gameInfo; + cemu_assert_suspicious(); } // determine if an optional update title id exists TitleIdParser tip(baseTitleId); diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index ea0cc3d3..d6ad8118 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -953,9 +953,7 @@ wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColu } case ColumnLocation: { - const auto relative_mlc_path = - entry.path.lexically_relative(ActiveSettings::GetMlcPath()).string(); - + const auto relative_mlc_path = _pathToUtf8(entry.path.lexically_relative(ActiveSettings::GetMlcPath())); if (relative_mlc_path.starts_with("usr") || relative_mlc_path.starts_with("sys")) return _("MLC"); else From 5b27d32cb7b7c02996ef6681690d5909c6a543f7 Mon Sep 17 00:00:00 2001 From: Francesco Saltori Date: Sat, 30 Sep 2023 15:27:56 +0200 Subject: [PATCH 050/101] Minor localization adjustments (#984) --- CODING_STYLE.md | 1 + src/audio/audioDebuggerWindow.cpp | 36 +++++++++---------- src/config/LaunchSettings.cpp | 2 +- src/gui/CemuUpdateWindow.cpp | 4 +-- src/gui/GeneralSettings2.cpp | 36 +++++++++---------- src/gui/MainWindow.cpp | 14 +++----- src/gui/MainWindow.h | 1 - .../CreateAccount/wxCreateAccountDialog.cpp | 4 +-- src/gui/input/InputAPIAddWindow.cpp | 4 +-- src/gui/input/InputSettings2.cpp | 4 +-- 10 files changed, 50 insertions(+), 56 deletions(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 54767052..39e1d342 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -55,6 +55,7 @@ std::string _pathToUtf8(const fs::path& path); fs::path _utf8ToPath(std::string_view input); // wxString <-> std::string +wxString wxString::FromUTF8(const std::string& s) wxString to_wxString(std::string_view str); // in gui/helpers.h std::string wxString::utf8_string(); diff --git a/src/audio/audioDebuggerWindow.cpp b/src/audio/audioDebuggerWindow.cpp index 41107820..3a51d187 100644 --- a/src/audio/audioDebuggerWindow.cpp +++ b/src/audio/audioDebuggerWindow.cpp @@ -33,93 +33,93 @@ AudioDebuggerWindow::AudioDebuggerWindow(wxFrame& parent) // add columns wxListItem col0; col0.SetId(0); - col0.SetText(wxT("idx")); + col0.SetText("idx"); col0.SetWidth(40); voiceListbox->InsertColumn(0, col0); wxListItem col1; col1.SetId(1); - col1.SetText(wxT("state")); + col1.SetText("state"); col1.SetWidth(48); voiceListbox->InsertColumn(1, col1); //wxListItem col2; // format col1.SetId(2); - col1.SetText(wxT("fmt")); + col1.SetText("fmt"); col1.SetWidth(52); voiceListbox->InsertColumn(2, col1); // sample base addr col1.SetId(3); - col1.SetText(wxT("base")); + col1.SetText("base"); col1.SetWidth(70); voiceListbox->InsertColumn(3, col1); // current offset col1.SetId(4); - col1.SetText(wxT("current")); + col1.SetText("current"); col1.SetWidth(70); voiceListbox->InsertColumn(4, col1); // loop offset col1.SetId(5); - col1.SetText(wxT("loop")); + col1.SetText("loop"); col1.SetWidth(70); voiceListbox->InsertColumn(5, col1); // end offset col1.SetId(6); - col1.SetText(wxT("end")); + col1.SetText("end"); col1.SetWidth(70); voiceListbox->InsertColumn(6, col1); // volume col1.SetId(7); - col1.SetText(wxT("vol")); + col1.SetText("vol"); col1.SetWidth(46); voiceListbox->InsertColumn(7, col1); // volume delta col1.SetId(8); - col1.SetText(wxT("volD")); + col1.SetText("volD"); col1.SetWidth(46); voiceListbox->InsertColumn(8, col1); // src col1.SetId(9); - col1.SetText(wxT("src")); + col1.SetText("src"); col1.SetWidth(70); voiceListbox->InsertColumn(9, col1); // low-pass filter coef a0 col1.SetId(10); - col1.SetText(wxT("lpa0")); + col1.SetText("lpa0"); col1.SetWidth(46); voiceListbox->InsertColumn(10, col1); // low-pass filter coef b0 col1.SetId(11); - col1.SetText(wxT("lpb0")); + col1.SetText("lpb0"); col1.SetWidth(46); voiceListbox->InsertColumn(11, col1); // biquad filter coef b0 col1.SetId(12); - col1.SetText(wxT("bqb0")); + col1.SetText("bqb0"); col1.SetWidth(46); voiceListbox->InsertColumn(12, col1); // biquad filter coef b0 col1.SetId(13); - col1.SetText(wxT("bqb1")); + col1.SetText("bqb1"); col1.SetWidth(46); voiceListbox->InsertColumn(13, col1); // biquad filter coef b0 col1.SetId(14); - col1.SetText(wxT("bqb2")); + col1.SetText("bqb2"); col1.SetWidth(46); voiceListbox->InsertColumn(14, col1); // biquad filter coef a0 col1.SetId(15); - col1.SetText(wxT("bqa1")); + col1.SetText("bqa1"); col1.SetWidth(46); voiceListbox->InsertColumn(15, col1); // biquad filter coef a1 col1.SetId(16); - col1.SetText(wxT("bqa2")); + col1.SetText("bqa2"); col1.SetWidth(46); voiceListbox->InsertColumn(16, col1); // device mix col1.SetId(17); - col1.SetText(wxT("deviceMix")); + col1.SetText("deviceMix"); col1.SetWidth(186); voiceListbox->InsertColumn(17, col1); diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index 92289edd..fdd4cc65 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -215,7 +215,7 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) std::string errorMsg; errorMsg.append("Error while trying to parse command line parameter:\n"); errorMsg.append(ex.what()); - wxMessageBox(errorMsg, wxT("Parameter error"), wxICON_ERROR); + wxMessageBox(errorMsg, "Parameter error", wxICON_ERROR); return false; } diff --git a/src/gui/CemuUpdateWindow.cpp b/src/gui/CemuUpdateWindow.cpp index f3568ee7..91394ee2 100644 --- a/src/gui/CemuUpdateWindow.cpp +++ b/src/gui/CemuUpdateWindow.cpp @@ -24,7 +24,7 @@ wxDECLARE_EVENT(wxEVT_PROGRESS, wxCommandEvent); wxDEFINE_EVENT(wxEVT_PROGRESS, wxCommandEvent); CemuUpdateWindow::CemuUpdateWindow(wxWindow* parent) - : wxDialog(parent, wxID_ANY, "Cemu update", wxDefaultPosition, wxDefaultSize, + : wxDialog(parent, wxID_ANY, _("Cemu update"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX) { auto* sizer = new wxBoxSizer(wxVERTICAL); @@ -35,7 +35,7 @@ CemuUpdateWindow::CemuUpdateWindow(wxWindow* parent) auto* rows = new wxFlexGridSizer(0, 2, 0, 0); rows->AddGrowableCol(1); - m_text = new wxStaticText(this, wxID_ANY, "Checking for latest version..."); + m_text = new wxStaticText(this, wxID_ANY, _("Checking for latest version...")); rows->Add(m_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); { diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 4dd3f9a3..e406c698 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -51,17 +51,17 @@ #include "util/ScreenSaver/ScreenSaver.h" -const wxString kDirectSound(wxT("DirectSound")); -const wxString kXAudio27(wxT("XAudio2.7")); -const wxString kXAudio2(wxT("XAudio2")); -const wxString kCubeb(wxT("Cubeb")); +const wxString kDirectSound("DirectSound"); +const wxString kXAudio27("XAudio2.7"); +const wxString kXAudio2("XAudio2"); +const wxString kCubeb("Cubeb"); -const wxString kPropertyPersistentId(wxT("PersistentId")); -const wxString kPropertyMiiName(wxT("MiiName")); -const wxString kPropertyBirthday(wxT("Birthday")); -const wxString kPropertyGender(wxT("Gender")); -const wxString kPropertyEmail(wxT("Email")); -const wxString kPropertyCountry(wxT("Country")); +const wxString kPropertyPersistentId("PersistentId"); +const wxString kPropertyMiiName("MiiName"); +const wxString kPropertyBirthday("Birthday"); +const wxString kPropertyGender("Gender"); +const wxString kPropertyEmail("Email"); +const wxString kPropertyCountry("Country"); wxDEFINE_EVENT(wxEVT_ACCOUNTLIST_REFRESH, wxCommandEvent); @@ -211,7 +211,7 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook) box_sizer->Add(m_mlc_path, 1, wxALL | wxEXPAND, 5); - auto* change_path = new wxButton(box, wxID_ANY, wxT("...")); + auto* change_path = new wxButton(box, wxID_ANY, "..."); change_path->Bind(wxEVT_BUTTON, &GeneralSettings2::OnMLCPathSelect, this); change_path->SetToolTip(_("Select a custom mlc path\nThe mlc path is used to store Wii U related files like save games, game updates and dlc data")); box_sizer->Add(change_path, 0, wxALL, 5); @@ -369,7 +369,7 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook) m_audio_latency = new wxSlider(box, wxID_ANY, 2, 0, IAudioAPI::kBlockCount - 1, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); m_audio_latency->SetToolTip(_("Controls the amount of buffered audio data\nHigher values will create a delay in audio playback, but may avoid audio problems when emulation is too slow")); audio_general_row->Add(m_audio_latency, 0, wxEXPAND | wxALL, 5); - auto latency_text = new wxStaticText(box, wxID_ANY, wxT("24ms")); + auto latency_text = new wxStaticText(box, wxID_ANY, "24ms"); audio_general_row->Add(latency_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); m_audio_latency->Bind(wxEVT_SLIDER, &GeneralSettings2::OnLatencySliderChanged, this, wxID_ANY, wxID_ANY, new wxControlObject(latency_text)); m_audio_latency->Bind(wxEVT_SLIDER, &GeneralSettings2::OnAudioLatencyChanged, this); @@ -408,7 +408,7 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook) audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_tv_volume = new wxSlider(box, wxID_ANY, 100, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); audio_tv_row->Add(m_tv_volume, 0, wxEXPAND | wxALL, 5); - auto audio_tv_volume_text = new wxStaticText(box, wxID_ANY, wxT("100%")); + auto audio_tv_volume_text = new wxStaticText(box, wxID_ANY, "100%"); audio_tv_row->Add(audio_tv_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); m_tv_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_tv_volume_text)); @@ -449,7 +449,7 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook) audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_pad_volume = new wxSlider(box, wxID_ANY, 100, 0, 100); audio_pad_row->Add(m_pad_volume, 0, wxEXPAND | wxALL, 5); - auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, wxT("100%")); + auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, "100%"); audio_pad_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); m_pad_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text)); @@ -490,7 +490,7 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook) audio_input_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_input_volume = new wxSlider(box, wxID_ANY, 100, 0, 100); audio_input_row->Add(m_input_volume, 0, wxEXPAND | wxALL, 5); - auto audio_input_volume_text = new wxStaticText(box, wxID_ANY, wxT("100%")); + auto audio_input_volume_text = new wxStaticText(box, wxID_ANY, "100%"); audio_input_row->Add(audio_input_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); m_input_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_input_volume_text)); @@ -747,7 +747,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook) m_account_grid->SetMinSize({ 300, -1 }); //m_account_grid->Append(new wxPropertyCategory("Main")); - auto* persistent_id_gprop = m_account_grid->Append(new wxStringProperty(wxT("PersistentId"), kPropertyPersistentId)); + auto* persistent_id_gprop = m_account_grid->Append(new wxStringProperty("PersistentId", kPropertyPersistentId)); persistent_id_gprop->SetHelpString(_("The persistent id is the internal folder name used for your saves")); m_account_grid->SetPropertyReadOnly(persistent_id_gprop); @@ -757,7 +757,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook) wxPGChoices gender; gender.Add(_("Female"), 0); gender.Add(_("Male"), 1); - m_account_grid->Append(new wxEnumProperty("Gender", kPropertyGender, gender)); + m_account_grid->Append(new wxEnumProperty(_("Gender"), kPropertyGender, gender)); m_account_grid->Append(new wxStringProperty(_("Email"), kPropertyEmail)); @@ -821,7 +821,7 @@ wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook) debug_row->Add(new wxStaticText(panel, wxID_ANY, _("GDB Stub port"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_gdb_port = new wxSpinCtrl(panel, wxID_ANY, wxT("1337"), wxDefaultPosition, wxDefaultSize, 0, 1000, 65535); + m_gdb_port = new wxSpinCtrl(panel, wxID_ANY, "1337", wxDefaultPosition, wxDefaultSize, 0, 1000, 65535); m_gdb_port->SetToolTip(_("Changes the port that the GDB stub will use, which you can use by either starting Cemu with the --enable-gdbstub option or by enabling it the Debug tab.")); debug_row->Add(m_gdb_port, 0, wxALL | wxEXPAND, 5); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index d9c5f4fb..e1985653 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -150,8 +150,7 @@ enum MAINFRAME_MENU_ID_DEBUG_DUMP_FST, MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, // help - MAINFRAME_MENU_ID_HELP_WEB = 21700, - MAINFRAME_MENU_ID_HELP_ABOUT, + MAINFRAME_MENU_ID_HELP_ABOUT = 21700, MAINFRAME_MENU_ID_HELP_UPDATE, MAINFRAME_MENU_ID_HELP_GETTING_STARTED, @@ -225,7 +224,6 @@ EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER, MainWindow::OnDebugViewPPCDe EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER, MainWindow::OnDebugViewAudioDebugger) EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS, MainWindow::OnDebugViewTextureRelations) // help menu -EVT_MENU(MAINFRAME_MENU_ID_HELP_WEB, MainWindow::OnHelpVistWebpage) EVT_MENU(MAINFRAME_MENU_ID_HELP_ABOUT, MainWindow::OnHelpAbout) EVT_MENU(MAINFRAME_MENU_ID_HELP_UPDATE, MainWindow::OnHelpUpdate) EVT_MENU(MAINFRAME_MENU_ID_HELP_GETTING_STARTED, MainWindow::OnHelpGettingStarted) @@ -560,13 +558,13 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE t.append(_pathToUtf8(launchPath)); if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_DISC_KEY) { - t.append(_("\n\n")); + t.append("\n\n"); t.append(_("Could not decrypt title. Make sure that keys.txt contains the correct disc key for this title.")); } if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_TITLE_TIK) { - t.append(_("")); - t.append(_("\n\nCould not decrypt title because title.tik is missing.")); + t.append("\n\n"); + t.append(_("Could not decrypt title because title.tik is missing.")); } wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); return false; @@ -1815,8 +1813,6 @@ void MainWindow::OnTimer(wxTimerEvent& event) } -void MainWindow::OnHelpVistWebpage(wxCommandEvent& event) {} - #define BUILD_DATE __DATE__ " " __TIME__ class CemuAboutDialog : public wxDialog @@ -2270,8 +2266,6 @@ void MainWindow::RecreateMenu() m_menuBar->Append(debugMenu, _("&Debug")); // help menu wxMenu* helpMenu = new wxMenu(); - //helpMenu->Append(MAINFRAME_MENU_ID_HELP_WEB, wxT("&Visit website")); - //helpMenu->AppendSeparator(); m_check_update_menu = helpMenu->Append(MAINFRAME_MENU_ID_HELP_UPDATE, _("&Check for updates")); #if BOOST_OS_LINUX || BOOST_OS_MACOS m_check_update_menu->Enable(false); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 1c4b5235..07189b52 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -100,7 +100,6 @@ public: void OnOptionsInput(wxCommandEvent& event); void OnAccountSelect(wxCommandEvent& event); void OnConsoleLanguage(wxCommandEvent& event); - void OnHelpVistWebpage(wxCommandEvent& event); void OnHelpAbout(wxCommandEvent& event); void OnHelpGettingStarted(wxCommandEvent& event); void OnHelpUpdate(wxCommandEvent& event); diff --git a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp b/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp index 1da92c34..82b4d795 100644 --- a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp +++ b/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp @@ -18,13 +18,13 @@ wxCreateAccountDialog::wxCreateAccountDialog(wxWindow* parent) main_sizer->SetFlexibleDirection(wxBOTH); main_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - main_sizer->Add(new wxStaticText(this, wxID_ANY, wxT("PersistentId")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); + main_sizer->Add(new wxStaticText(this, wxID_ANY, "PersistentId"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_persistent_id = new wxTextCtrl(this, wxID_ANY, fmt::format("{:x}", Account::GetNextPersistentId())); m_persistent_id->SetToolTip(_("The persistent id is the internal folder name used for your saves. Only change this if you are importing saves from a Wii U with a specific id")); main_sizer->Add(m_persistent_id, 1, wxALL | wxEXPAND, 5); - main_sizer->Add(new wxStaticText(this, wxID_ANY, wxT("Mii name")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); + main_sizer->Add(new wxStaticText(this, wxID_ANY, _("Mii name")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_mii_name = new wxTextCtrl(this, wxID_ANY); m_mii_name->SetFocus(); diff --git a/src/gui/input/InputAPIAddWindow.cpp b/src/gui/input/InputAPIAddWindow.cpp index 8fa85fa3..a6d1f1a9 100644 --- a/src/gui/input/InputAPIAddWindow.cpp +++ b/src/gui/input/InputAPIAddWindow.cpp @@ -90,11 +90,11 @@ InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position, auto* row = new wxBoxSizer(wxHORIZONTAL); // we only have dsu settings atm, so add elements now row->Add(new wxStaticText(m_settings_panel, wxID_ANY, _("IP")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_ip = new wxTextCtrl(m_settings_panel, wxID_ANY, wxT("127.0.0.1")); + m_ip = new wxTextCtrl(m_settings_panel, wxID_ANY, "127.0.0.1"); row->Add(m_ip, 0, wxALL, 5); row->Add(new wxStaticText(m_settings_panel, wxID_ANY, _("Port")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_port = new wxTextCtrl(m_settings_panel, wxID_ANY, wxT("26760")); + m_port = new wxTextCtrl(m_settings_panel, wxID_ANY, "26760"); row->Add(m_port, 0, wxALL, 5); panel_sizer->Add(row, 0, wxEXPAND); diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp index 7a52f865..58c168a3 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/input/InputSettings2.cpp @@ -238,11 +238,11 @@ wxWindow* InputSettings2::initialize_page(size_t index) // add/remove buttons auto* bttn_sizer = new wxBoxSizer(wxHORIZONTAL); - auto* add_api = new wxButton(page, wxID_ANY, wxT(" + "), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + auto* add_api = new wxButton(page, wxID_ANY, " + ", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); add_api->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_add, this); bttn_sizer->Add(add_api, 0, wxALL, 5); - auto* remove_api = new wxButton(page, wxID_ANY, wxT(" - "), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + auto* remove_api = new wxButton(page, wxID_ANY, " - ", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); remove_api->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_remove, this); bttn_sizer->Add(remove_api, 0, wxALL, 5); From 9523993a248155be5986ba4f32a81ba7e4ab3de6 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:59:45 +0200 Subject: [PATCH 051/101] Fix file menu list of recent games --- src/gui/MainWindow.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e1985653..c220c686 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -674,7 +674,7 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) const size_t index = menuId - MAINFRAME_MENU_ID_FILE_RECENT_0; if (index < config.recent_launch_files.size()) { - const auto& path = config.recent_launch_files[index]; + fs::path path = _utf8ToPath(config.recent_launch_files[index]); if (!path.empty()) FileLoad(path, wxLaunchGameEvent::INITIATED_BY::MENU); } @@ -2091,17 +2091,12 @@ void MainWindow::RecreateMenu() m_fileMenuSeparator1 = nullptr; for (size_t i = 0; i < config.recent_launch_files.size(); i++) { - const auto& entry = config.recent_launch_files[i]; - if (entry.empty()) + const std::string& pathStr = config.recent_launch_files[i]; + if (pathStr.empty()) continue; - - if (!fs::exists(entry)) - continue; - if (recentFileIndex == 0) m_fileMenuSeparator0 = m_fileMenu->AppendSeparator(); - - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, to_wxString(fmt::format("{}. {}", recentFileIndex, entry))); + m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, to_wxString(fmt::format("{}. {}", recentFileIndex, pathStr))); recentFileIndex++; if (recentFileIndex >= 8) From ff9d180154699ed991e0fbda380c7f8db88e92de Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 1 Oct 2023 11:43:24 +0200 Subject: [PATCH 052/101] Code cleanup --- CMakeSettings.json | 2 +- src/Cafe/CafeSystem.cpp | 7 --- src/Cafe/GamePatch.cpp | 2 +- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 6 +- src/Cafe/HW/Espresso/Debugger/GDBStub.cpp | 4 +- src/Cafe/HW/Latte/Core/LatteConst.h | 24 +++++--- src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp | 8 +-- .../LatteDecompilerAnalyzer.cpp | 8 +-- .../HW/Latte/Renderer/Vulkan/CachedFBOVk.h | 4 -- .../Renderer/Vulkan/VulkanRendererCore.cpp | 12 ++-- src/Cafe/IOSU/legacy/iosu_ioctl.cpp | 3 +- src/Cafe/OS/libs/coreinit/coreinit.cpp | 6 +- src/Cafe/OS/libs/coreinit/coreinit.h | 9 +-- src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp | 1 - src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 48 +++++++--------- src/Cafe/OS/libs/coreinit/coreinit_Thread.h | 5 +- src/Cafe/OS/libs/gx2/GX2.h | 5 -- src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp | 4 +- src/Cafe/OS/libs/nn_act/nn_act.cpp | 1 - src/Cafe/OS/libs/nn_save/nn_save.cpp | 55 +++++++++++-------- src/Cafe/OS/libs/nsysnet/nsysnet.cpp | 2 +- src/gui/MainWindow.cpp | 12 +--- src/gui/TitleManager.h | 1 - src/gui/components/wxTitleManagerList.h | 3 +- src/gui/debugger/DisasmCtrl.cpp | 2 +- src/gui/guiWrapper.h | 2 - 26 files changed, 105 insertions(+), 131 deletions(-) diff --git a/CMakeSettings.json b/CMakeSettings.json index 3097caab..0927e98b 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -14,7 +14,7 @@ "generator": "Ninja", "inheritEnvironments": [ "msvc_x64_x64" ], "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}" }, { "name": "Debug", diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index dd761f6e..668def01 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -254,13 +254,6 @@ void InfoLog_PrintActiveSettings() cemuLog_log(LogType::Force, "Console language: {}", config.console_language); } -void PPCCore_setupSPR(PPCInterpreter_t* hCPU, uint32 coreIndex) -{ - hCPU->sprExtended.PVR = 0x70010001; - hCPU->spr.UPIR = coreIndex; - hCPU->sprExtended.msr |= MSR_FP; // enable floating point -} - struct SharedDataEntry { /* +0x00 */ uint32be name; diff --git a/src/Cafe/GamePatch.cpp b/src/Cafe/GamePatch.cpp index 84bfcb21..77eaff32 100644 --- a/src/Cafe/GamePatch.cpp +++ b/src/Cafe/GamePatch.cpp @@ -52,7 +52,7 @@ typedef struct void hleExport_xcx_enterCriticalSection(PPCInterpreter_t* hCPU) { ppcDefineParamStructPtr(xcxCS, xcxCS_t, 0); - uint32 threadId = coreinitThread_getCurrentThreadMPTRDepr(hCPU); + uint32 threadId = MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(); cemu_assert_debug(xcxCS->ukn08 != 0); cemu_assert_debug(threadId); if (xcxCS->ownerThreadId == (uint32be)threadId) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index e99ce522..62a5d592 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -513,10 +513,10 @@ void debugger_enterTW(PPCInterpreter_t* hCPU) if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled) { std::string logName = !bp->comment.empty() ? "Breakpoint '"+boost::nowide::narrow(bp->comment)+"'" : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); - std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); + std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); if (cemuLog_advancedPPCLoggingEnabled()) - DebugLogStackTrace(coreinitThread_getCurrentThreadDepr(hCPU), hCPU->gpr[1]); + DebugLogStackTrace(coreinit::OSGetCurrentThread(), hCPU->gpr[1]); break; } bp = bp->next; @@ -535,7 +535,7 @@ void debugger_enterTW(PPCInterpreter_t* hCPU) // handle breakpoints debuggerState.debugSession.isTrapped = true; - debuggerState.debugSession.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU); + debuggerState.debugSession.debuggedThreadMPTR = MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(); debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; debuggerState.debugSession.hCPU = hCPU; debugger_createPPCStateSnapshot(hCPU); diff --git a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp index b7e15407..e934e55d 100644 --- a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp +++ b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp @@ -900,7 +900,7 @@ void GDBServer::HandleTrapInstruction(PPCInterpreter_t* hCPU) return cemu_assert_suspicious(); // Secondly, delete one-shot breakpoints but also temporarily delete patched instruction to run original instruction - OSThread_t* currThread = coreinitThread_getCurrentThreadDepr(hCPU); + OSThread_t* currThread = coreinit::OSGetCurrentThread(); std::string pauseReason = fmt::format("T05thread:{:08X};core:{:02X};{}", GET_THREAD_ID(currThread), PPCInterpreter_getCoreIndex(hCPU), patchedBP->second.GetReason()); bool pauseThreads = patchedBP->second.ShouldBreakThreads() || patchedBP->second.ShouldBreakThreadsOnNextInterrupt(); if (patchedBP->second.IsPersistent()) @@ -939,7 +939,7 @@ void GDBServer::HandleTrapInstruction(PPCInterpreter_t* hCPU) ThreadPool::FireAndForget(&waitForBrokenThreads, std::move(m_resumed_context), pauseReason); } - breakThreads(GET_THREAD_ID(coreinitThread_getCurrentThreadDepr(hCPU))); + breakThreads(GET_THREAD_ID(coreinit::OSGetCurrentThread())); cemuLog_logDebug(LogType::Force, "[GDBStub] Resumed from a breakpoint!"); } } diff --git a/src/Cafe/HW/Latte/Core/LatteConst.h b/src/Cafe/HW/Latte/Core/LatteConst.h index ffbead1c..04c7b888 100644 --- a/src/Cafe/HW/Latte/Core/LatteConst.h +++ b/src/Cafe/HW/Latte/Core/LatteConst.h @@ -1,21 +1,27 @@ #pragma once #include "Cafe/HW/Latte/ISA/LatteReg.h" -// this file contains legacy C-style defines, modernize and merge into LatteReg.h +// todo - this file contains legacy C-style defines, modernize and merge into LatteReg.h // GPU7/Latte hardware info -#define LATTE_NUM_GPR (128) -#define LATTE_NUM_STREAMOUT_BUFFER (4) -#define LATTE_NUM_COLOR_TARGET (8) +#define LATTE_NUM_GPR 128 +#define LATTE_NUM_STREAMOUT_BUFFER 4 +#define LATTE_NUM_COLOR_TARGET 8 -#define LATTE_NUM_MAX_TEX_UNITS (18) // number of available texture units per shader stage (this might be higher than 18? BotW is the only game which uses more than 16?) -#define LATTE_NUM_MAX_UNIFORM_BUFFERS (16) // number of supported uniform buffer binding locations +#define LATTE_NUM_MAX_TEX_UNITS 18 // number of available texture units per shader stage (this might be higher than 18? BotW is the only game which uses more than 16?) +#define LATTE_NUM_MAX_UNIFORM_BUFFERS 16 // number of supported uniform buffer binding locations -#define LATTE_VS_ATTRIBUTE_LIMIT (32) // todo: verify -#define LATTE_NUM_MAX_ATTRIBUTE_LOCATIONS (256) // should this be 128 since there are only 128 GPRs? +#define LATTE_VS_ATTRIBUTE_LIMIT 32 // todo: verify +#define LATTE_NUM_MAX_ATTRIBUTE_LOCATIONS 256 // should this be 128 since there are only 128 GPRs? -#define LATTE_MAX_VERTEX_BUFFERS (16) +#define LATTE_MAX_VERTEX_BUFFERS 16 + +// Cemu-specific constants + +#define LATTE_CEMU_PS_TEX_UNIT_BASE 0 +#define LATTE_CEMU_VS_TEX_UNIT_BASE 32 +#define LATTE_CEMU_GS_TEX_UNIT_BASE 64 // vertex formats diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp index 1bf17c51..9cce2526 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp @@ -1,10 +1,8 @@ #include "Cafe/HW/Latte/ISA/RegDefines.h" -#include "Cafe/OS/libs/gx2/GX2.h" // todo - remove this dependency #include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/HW/Latte/Core/LatteShader.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" -#include "Cafe/GraphicPack/GraphicPack2.h" #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h" @@ -330,15 +328,15 @@ void LatteTexture_updateTextures() // pixel shader LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader(); if (pixelShader) - LatteTexture_updateTexturesForStage(pixelShader, CEMU_PS_TEX_UNIT_BASE, LatteGPUState.contextNew.SQ_TEX_START_PS); + LatteTexture_updateTexturesForStage(pixelShader, LATTE_CEMU_PS_TEX_UNIT_BASE, LatteGPUState.contextNew.SQ_TEX_START_PS); // vertex shader LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader(); cemu_assert_debug(vertexShader != nullptr); - LatteTexture_updateTexturesForStage(vertexShader, CEMU_VS_TEX_UNIT_BASE, LatteGPUState.contextNew.SQ_TEX_START_VS); + LatteTexture_updateTexturesForStage(vertexShader, LATTE_CEMU_VS_TEX_UNIT_BASE, LatteGPUState.contextNew.SQ_TEX_START_VS); // geometry shader LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader(); if (geometryShader) - LatteTexture_updateTexturesForStage(geometryShader, CEMU_GS_TEX_UNIT_BASE, LatteGPUState.contextNew.SQ_TEX_START_GS); + LatteTexture_updateTexturesForStage(geometryShader, LATTE_CEMU_GS_TEX_UNIT_BASE, LatteGPUState.contextNew.SQ_TEX_START_GS); } // returns the width, height, depth of the texture diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp index 7285d312..2e837198 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp @@ -1,9 +1,7 @@ #include "Cafe/HW/Latte/Core/LatteConst.h" #include "Cafe/HW/Latte/Core/LatteShaderAssembly.h" #include "Cafe/HW/Latte/ISA/RegDefines.h" -#include "Cafe/OS/libs/gx2/GX2.h" // todo - remove this dependency #include "Cafe/HW/Latte/Core/Latte.h" -#include "Cafe/HW/Latte/Core/LatteDraw.h" #include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h" #include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h" #include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h" @@ -477,11 +475,11 @@ namespace LatteDecompiler continue; sint32 textureBindingPoint; if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex) - textureBindingPoint = i + CEMU_VS_TEX_UNIT_BASE; + textureBindingPoint = i + LATTE_CEMU_VS_TEX_UNIT_BASE; else if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry) - textureBindingPoint = i + CEMU_GS_TEX_UNIT_BASE; + textureBindingPoint = i + LATTE_CEMU_GS_TEX_UNIT_BASE; else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel) - textureBindingPoint = i + CEMU_PS_TEX_UNIT_BASE; + textureBindingPoint = i + LATTE_CEMU_PS_TEX_UNIT_BASE; decompilerContext->output->resourceMappingGL.textureUnitToBindingPoint[i] = textureBindingPoint; } diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h index 4e6be012..bf72996e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h @@ -75,10 +75,6 @@ private: VkRenderingAttachmentInfoKHR m_vkColorAttachments[8]; VkRenderingAttachmentInfoKHR m_vkDepthAttachment; VkRenderingAttachmentInfoKHR m_vkStencilAttachment; - //uint8 m_vkColorAttachmentsCount{0}; - bool m_vkHasDepthAttachment{ false }; - bool m_vkHasStencilAttachment{ false }; - std::vector m_usedByPipelines; // PipelineInfo objects which use this renderpass/framebuffer }; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 8b0e3b63..320357f1 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -513,15 +513,15 @@ uint64 VulkanRenderer::GetDescriptorSetStateHash(LatteDecompilerShader* shader) switch (shader->shaderType) { case LatteConst::ShaderType::Vertex: - hostTextureUnit += CEMU_VS_TEX_UNIT_BASE; + hostTextureUnit += LATTE_CEMU_VS_TEX_UNIT_BASE; texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS; break; case LatteConst::ShaderType::Pixel: - hostTextureUnit += CEMU_PS_TEX_UNIT_BASE; + hostTextureUnit += LATTE_CEMU_PS_TEX_UNIT_BASE; texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS; break; case LatteConst::ShaderType::Geometry: - hostTextureUnit += CEMU_GS_TEX_UNIT_BASE; + hostTextureUnit += LATTE_CEMU_GS_TEX_UNIT_BASE; texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS; break; default: @@ -631,15 +631,15 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* switch (shader->shaderType) { case LatteConst::ShaderType::Vertex: - hostTextureUnit += CEMU_VS_TEX_UNIT_BASE; + hostTextureUnit += LATTE_CEMU_VS_TEX_UNIT_BASE; texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS; break; case LatteConst::ShaderType::Pixel: - hostTextureUnit += CEMU_PS_TEX_UNIT_BASE; + hostTextureUnit += LATTE_CEMU_PS_TEX_UNIT_BASE; texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS; break; case LatteConst::ShaderType::Geometry: - hostTextureUnit += CEMU_GS_TEX_UNIT_BASE; + hostTextureUnit += LATTE_CEMU_GS_TEX_UNIT_BASE; texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS; break; default: diff --git a/src/Cafe/IOSU/legacy/iosu_ioctl.cpp b/src/Cafe/IOSU/legacy/iosu_ioctl.cpp index 1fc2a27a..22e5a55d 100644 --- a/src/Cafe/IOSU/legacy/iosu_ioctl.cpp +++ b/src/Cafe/IOSU/legacy/iosu_ioctl.cpp @@ -1,6 +1,5 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/OS/libs/coreinit/coreinit_Thread.h" -#include "Cafe/OS/libs/coreinit/coreinit.h" #include "iosu_ioctl.h" #include "util/helpers/ringbuffer.h" @@ -23,7 +22,7 @@ sint32 iosuIoctl_pushAndWait(uint32 ioctlHandle, ioQueueEntry_t* ioQueueEntry) } __OSLockScheduler(); ioctlMutex.lock(); - ioQueueEntry->ppcThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); + ioQueueEntry->ppcThread = coreinit::OSGetCurrentThread(); _ioctlRingbuffer[ioctlHandle].Push(ioQueueEntry); ioctlMutex.unlock(); diff --git a/src/Cafe/OS/libs/coreinit/coreinit.cpp b/src/Cafe/OS/libs/coreinit/coreinit.cpp index 8738e3a4..660f874f 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit.cpp @@ -35,7 +35,7 @@ #include "Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.h" #include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h" -coreinitData_t* gCoreinitData = NULL; +CoreinitSharedData* gCoreinitData = NULL; sint32 ScoreStackTrace(OSThread_t* thread, MPTR sp) { @@ -323,8 +323,8 @@ void coreinit_load() coreinit::InitializeSysHeap(); // allocate coreinit global data - gCoreinitData = (coreinitData_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(coreinitData_t), 32)); - memset(gCoreinitData, 0x00, sizeof(coreinitData_t)); + gCoreinitData = (CoreinitSharedData*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(CoreinitSharedData), 32)); + memset(gCoreinitData, 0x00, sizeof(CoreinitSharedData)); // coreinit weak links osLib_addVirtualPointer("coreinit", "MEMAllocFromDefaultHeap", memory_getVirtualOffsetFromPointer(&gCoreinitData->MEMAllocFromDefaultHeap)); diff --git a/src/Cafe/OS/libs/coreinit/coreinit.h b/src/Cafe/OS/libs/coreinit/coreinit.h index 046ffe1e..74aab9b2 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit.h +++ b/src/Cafe/OS/libs/coreinit/coreinit.h @@ -16,8 +16,7 @@ void coreinitAsyncCallback_addWithLock(MPTR functionMPTR, uint32 numParameters, void coreinit_load(); // coreinit shared memory - -typedef struct +struct CoreinitSharedData { MEMPTR MEMAllocFromDefaultHeap; MEMPTR MEMAllocFromDefaultHeapEx; @@ -26,11 +25,9 @@ typedef struct MPTR __cpp_exception_init_ptr; MPTR __cpp_exception_cleanup_ptr; MPTR __stdio_cleanup; -}coreinitData_t; +}; -extern coreinitData_t* gCoreinitData; - -#include "Cafe/OS/libs/coreinit/coreinit_Spinlock.h" +extern CoreinitSharedData* gCoreinitData; // coreinit init void coreinit_start(PPCInterpreter_t* hCPU); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp b/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp index b348218f..14d7a645 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp @@ -71,7 +71,6 @@ sint32 MCP_GetSysProdSettings(MCPHANDLE mcpHandle, SysProdSettings* sysProdSetti void coreinitExport_MCP_GetSysProdSettings(PPCInterpreter_t* hCPU) { - cemuLog_logDebug(LogType::Force, "MCP_GetSysProdSettings(0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4]); sint32 result = MCP_GetSysProdSettings(hCPU->gpr[3], (SysProdSettings*)memory_getPointerFromVirtualOffset(hCPU->gpr[4])); osLib_returnFromFunction(hCPU, result); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 71e5d493..d9b33dca 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -198,7 +198,7 @@ namespace coreinit void threadEntry(PPCInterpreter_t* hCPU) { - OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(hCPU); + OSThread_t* currentThread = coreinit::OSGetCurrentThread(); uint32 r3 = hCPU->gpr[3]; uint32 r4 = hCPU->gpr[4]; uint32 lr = hCPU->spr.LR; @@ -368,39 +368,38 @@ namespace coreinit { PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); hCPU->gpr[3] = exitValue; - OSThread_t* threadBE = coreinitThread_getCurrentThreadDepr(hCPU); - MPTR t = memory_getVirtualOffsetFromPointer(threadBE); + OSThread_t* currentThread = coreinit::OSGetCurrentThread(); // thread cleanup callback - if (!threadBE->cleanupCallback2.IsNull()) + if (!currentThread->cleanupCallback2.IsNull()) { - threadBE->stateFlags = _swapEndianU32(_swapEndianU32(threadBE->stateFlags) | 0x00000001); - PPCCoreCallback(threadBE->cleanupCallback2.GetMPTR(), threadBE, _swapEndianU32(threadBE->stackEnd)); + currentThread->stateFlags = _swapEndianU32(_swapEndianU32(currentThread->stateFlags) | 0x00000001); + PPCCoreCallback(currentThread->cleanupCallback2.GetMPTR(), currentThread, _swapEndianU32(currentThread->stackEnd)); } // cpp exception cleanup - if (gCoreinitData->__cpp_exception_cleanup_ptr != 0 && threadBE->crt.eh_globals != nullptr) + if (gCoreinitData->__cpp_exception_cleanup_ptr != 0 && currentThread->crt.eh_globals != nullptr) { - PPCCoreCallback(_swapEndianU32(gCoreinitData->__cpp_exception_cleanup_ptr), &threadBE->crt.eh_globals); - threadBE->crt.eh_globals = nullptr; + PPCCoreCallback(_swapEndianU32(gCoreinitData->__cpp_exception_cleanup_ptr), ¤tThread->crt.eh_globals); + currentThread->crt.eh_globals = nullptr; } // set exit code - threadBE->exitValue = exitValue; + currentThread->exitValue = exitValue; __OSLockScheduler(); // release held synchronization primitives - if (!threadBE->mutexQueue.isEmpty()) + if (!currentThread->mutexQueue.isEmpty()) { cemuLog_log(LogType::Force, "OSExitThread: Thread is holding mutexes"); while (true) { - OSMutex* mutex = threadBE->mutexQueue.getFirst(); + OSMutex* mutex = currentThread->mutexQueue.getFirst(); if (!mutex) break; - if (mutex->owner != threadBE) + if (mutex->owner != currentThread) { cemuLog_log(LogType::Force, "OSExitThread: Thread is holding mutex which it doesn't own"); - threadBE->mutexQueue.removeMutex(mutex); + currentThread->mutexQueue.removeMutex(mutex); continue; } coreinit::OSUnlockMutexInternal(mutex); @@ -409,22 +408,22 @@ namespace coreinit // todo - release all fast mutexes // handle join queue - if (!threadBE->joinQueue.isEmpty()) - threadBE->joinQueue.wakeupEntireWaitQueue(false); + if (!currentThread->joinQueue.isEmpty()) + currentThread->joinQueue.wakeupEntireWaitQueue(false); - if ((threadBE->attr & 8) != 0) + if ((currentThread->attr & 8) != 0) { // deactivate thread since it is detached - threadBE->state = OSThread_t::THREAD_STATE::STATE_NONE; - coreinit::__OSDeactivateThread(threadBE); + currentThread->state = OSThread_t::THREAD_STATE::STATE_NONE; + coreinit::__OSDeactivateThread(currentThread); // queue call to thread deallocator if set - if (!threadBE->deallocatorFunc.IsNull()) - __OSQueueThreadDeallocation(threadBE); + if (!currentThread->deallocatorFunc.IsNull()) + __OSQueueThreadDeallocation(currentThread); } else { // non-detached threads remain active - threadBE->state = OSThread_t::THREAD_STATE::STATE_MORIBUND; + currentThread->state = OSThread_t::THREAD_STATE::STATE_MORIBUND; } PPCCore_switchToSchedulerWithLock(); } @@ -1401,11 +1400,6 @@ void coreinit_resumeThread(OSThread_t* OSThreadBE, sint32 count) __OSUnlockScheduler(); } -MPTR coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_t* hCPU) -{ - return memory_getVirtualOffsetFromPointer(coreinit::__currentCoreThread[PPCInterpreter_getCoreIndex(hCPU)]); -} - OSThread_t* coreinitThread_getCurrentThreadDepr(PPCInterpreter_t* hCPU) { return coreinit::__currentCoreThread[PPCInterpreter_getCoreIndex(hCPU)]; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h index e2f5bef2..e619d5b6 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h @@ -486,8 +486,8 @@ struct OSThread_t /* +0x668 */ MPTR tlsBlocksMPTR; /* +0x66C */ MEMPTR waitingForFastMutex; - /* +0x670 */ coreinit::OSFastMutexLink contendedFastMutex; // link or queue? - /* +0x678 */ coreinit::OSFastMutexLink ownedFastMutex; // link or queue? + /* +0x670 */ coreinit::OSFastMutexLink contendedFastMutex; + /* +0x678 */ coreinit::OSFastMutexLink ownedFastMutex; /* +0x680 */ uint32 padding680[28 / 4]; }; @@ -615,7 +615,6 @@ namespace coreinit void coreinit_suspendThread(OSThread_t* OSThreadBE, sint32 count = 1); void coreinit_resumeThread(OSThread_t* OSThreadBE, sint32 count = 1); -MPTR coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_t* hCPU); OSThread_t* coreinitThread_getCurrentThreadDepr(PPCInterpreter_t* hCPU); extern MPTR activeThread[256]; diff --git a/src/Cafe/OS/libs/gx2/GX2.h b/src/Cafe/OS/libs/gx2/GX2.h index b8a3f919..58d98191 100644 --- a/src/Cafe/OS/libs/gx2/GX2.h +++ b/src/Cafe/OS/libs/gx2/GX2.h @@ -7,11 +7,6 @@ #define GX2_ENABLE 1 #define GX2_DISABLE 0 -// tex unit base for render backends -#define CEMU_PS_TEX_UNIT_BASE 0 -#define CEMU_VS_TEX_UNIT_BASE 32 -#define CEMU_GS_TEX_UNIT_BASE 64 - #include "GX2_Surface.h" // general diff --git a/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp b/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp index ce9501ab..53981a5a 100644 --- a/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp +++ b/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp @@ -225,7 +225,7 @@ void CurlWorkerThread(CURL_t* curl, PPCConcurrentQueue* callerQueue, uint32 SendOrderToWorker(CURL_t* curl, QueueOrder order, uint32 arg1 = 0) { - OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); + OSThread_t* currentThread = coreinit::OSGetCurrentThread(); curl->curlThread = currentThread; // cemuLog_logDebug(LogType::Force, "CURRENTTHREAD: 0x{} -> {}",currentThread, order) @@ -707,7 +707,7 @@ void export_curl_easy_init(PPCInterpreter_t* hCPU) memset(result.GetPtr(), 0, sizeof(CURL_t)); *result = {}; result->curl = curl_easy_init(); - result->curlThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); + result->curlThread = coreinit::OSGetCurrentThread(); result->info_contentType = nullptr; result->info_redirectUrl = nullptr; diff --git a/src/Cafe/OS/libs/nn_act/nn_act.cpp b/src/Cafe/OS/libs/nn_act/nn_act.cpp index fb1d4d14..68109586 100644 --- a/src/Cafe/OS/libs/nn_act/nn_act.cpp +++ b/src/Cafe/OS/libs/nn_act/nn_act.cpp @@ -283,7 +283,6 @@ void nnActExport_GetSimpleAddressIdEx(PPCInterpreter_t* hCPU) void nnActExport_GetPrincipalId(PPCInterpreter_t* hCPU) { // return error for non-nnid accounts? - cemuLog_logDebug(LogType::Force, "nn_act.GetPrincipalId()"); uint32be principalId; GetPrincipalIdEx(&principalId, iosu::act::ACT_SLOT_CURRENT); osLib_returnFromFunction(hCPU, (uint32)principalId); diff --git a/src/Cafe/OS/libs/nn_save/nn_save.cpp b/src/Cafe/OS/libs/nn_save/nn_save.cpp index 1311dd46..78de8291 100644 --- a/src/Cafe/OS/libs/nn_save/nn_save.cpp +++ b/src/Cafe/OS/libs/nn_save/nn_save.cpp @@ -446,19 +446,20 @@ namespace save SAVEStatus SAVEOpenFileOtherApplication(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint64 titleId, uint8 accountSlot, const char* path, const char* mode, FSFileHandleDepr_t* hFile, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParamsNew_t asyncParams; asyncParams.ioMsgQueue = nullptr; asyncParams.userCallback = PPCInterpreter_makeCallableExportDepr(AsyncCallback); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetPointer(); SAVEStatus status = SAVEOpenFileOtherApplicationAsync(client, block, titleId, accountSlot, path, mode, hFile, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -680,19 +681,20 @@ namespace save SAVEStatus SAVEGetFreeSpaceSize(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, FSLargeSize* freeSize, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEGetFreeSpaceSizeAsync(client, block, accountSlot, freeSize, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -749,19 +751,20 @@ namespace save SAVEStatus SAVERemove(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, const char* path, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVERemoveAsync(client, block, accountSlot, path, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -862,19 +865,20 @@ namespace save SAVEStatus SAVEOpenDir(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, const char* path, FSDirHandlePtr hDir, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEOpenDirAsync(client, block, accountSlot, path, hDir, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -935,19 +939,20 @@ namespace save SAVEStatus SAVEOpenDirOtherApplication(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint64 titleId, uint8 accountSlot, const char* path, FSDirHandlePtr hDir, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEOpenDirOtherApplicationAsync(client, block, titleId, accountSlot, path, hDir, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1071,19 +1076,20 @@ namespace save SAVEStatus SAVEMakeDir(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, const char* path, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEMakeDirAsync(client, block, accountSlot, path, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1122,19 +1128,20 @@ namespace save SAVEStatus SAVEOpenFile(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, const char* path, const char* mode, FSFileHandleDepr_t* hFile, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParamsNew_t asyncParams; asyncParams.ioMsgQueue = nullptr; asyncParams.userCallback = PPCInterpreter_makeCallableExportDepr(AsyncCallback); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetPointer(); SAVEStatus status = SAVEOpenFileAsync(client, block, accountSlot, path, mode, hFile, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1182,19 +1189,20 @@ namespace save SAVEStatus SAVEGetStat(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, const char* path, FSStat_t* stat, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEGetStatAsync(client, block, accountSlot, path, stat, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1233,19 +1241,20 @@ namespace save SAVEStatus SAVEGetStatOtherApplication(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint64 titleId, uint8 accountSlot, const char* path, FSStat_t* stat, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEGetStatOtherApplicationAsync(client, block, titleId, accountSlot, path, stat, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1422,19 +1431,20 @@ namespace save SAVEStatus SAVEChangeDir(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, const char* path, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEChangeDirAsync(client, block, accountSlot, path, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } @@ -1491,19 +1501,20 @@ namespace save SAVEStatus SAVEFlushQuota(coreinit::FSClient_t* client, coreinit::FSCmdBlock_t* block, uint8 accountSlot, FS_ERROR_MASK errHandling) { + MEMPTR currentThread{coreinit::OSGetCurrentThread()}; FSAsyncParams_t asyncParams; asyncParams.ioMsgQueue = MPTR_NULL; asyncParams.userCallback = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(AsyncCallback)); StackAllocator param; - param->thread = coreinitThread_getCurrentThreadMPTRDepr(PPCInterpreter_getCurrentInstance()); + param->thread = currentThread; param->returnStatus = (FSStatus)FS_RESULT::SUCCESS; asyncParams.userContext = param.GetMPTRBE(); SAVEStatus status = SAVEFlushQuotaAsync(client, block, accountSlot, errHandling, &asyncParams); if (status == (FSStatus)FS_RESULT::SUCCESS) { - coreinit_suspendThread(coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()), 1000); + coreinit_suspendThread(currentThread, 1000); PPCCore_switchToScheduler(); return param->returnStatus; } diff --git a/src/Cafe/OS/libs/nsysnet/nsysnet.cpp b/src/Cafe/OS/libs/nsysnet/nsysnet.cpp index f39a24ea..e0224148 100644 --- a/src/Cafe/OS/libs/nsysnet/nsysnet.cpp +++ b/src/Cafe/OS/libs/nsysnet/nsysnet.cpp @@ -85,7 +85,7 @@ void nsysnetExport_socket_lib_finish(PPCInterpreter_t* hCPU) uint32* __gh_errno_ptr() { - OSThread_t* osThread = coreinitThread_getCurrentThreadDepr(PPCInterpreter_getCurrentInstance()); + OSThread_t* osThread = coreinit::OSGetCurrentThread(); return &osThread->context.error; } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c220c686..c0d975ec 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -14,8 +14,6 @@ #include "gui/canvas/VulkanCanvas.h" #include "Cafe/OS/libs/nn_nfp/nn_nfp.h" #include "Cafe/OS/libs/swkbd/swkbd.h" -#include "Cafe/IOSU/legacy/iosu_crypto.h" -#include "Cafe/GameProfile/GameProfile.h" #include "gui/debugger/DebuggerWindow2.h" #include "util/helpers/helpers.h" #include "config/CemuConfig.h" @@ -23,10 +21,8 @@ #include "util/ScreenSaver/ScreenSaver.h" #include "gui/GeneralSettings2.h" #include "gui/GraphicPacksWindow2.h" -#include "gui/GameProfileWindow.h" #include "gui/CemuApp.h" #include "gui/CemuUpdateWindow.h" -#include "gui/helpers/wxCustomData.h" #include "gui/LoggingWindow.h" #include "config/ActiveSettings.h" #include "config/LaunchSettings.h" @@ -36,9 +32,7 @@ #include "gui/TitleManager.h" #include "Cafe/CafeSystem.h" -#include "Cafe/TitleList/GameInfo.h" -#include #include "util/helpers/SystemException.h" #include "gui/DownloadGraphicPacksWindow.h" #include "gui/GettingStartedDialog.h" @@ -529,8 +523,8 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE } else //if (launchTitle.GetFormat() == TitleInfo::TitleDataFormat::INVALID_STRUCTURE ) { - // title is invalid, if its an RPX/ELF we can launch it directly - // otherwise its an error + // title is invalid, if it's an RPX/ELF we can launch it directly + // otherwise it's an error CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath); if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF) { @@ -1875,7 +1869,7 @@ public: { wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); lineSizer->Add(new wxStaticText(parent, -1, "zLib ("), 0); - lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "http://www.zlib.net", "http://www.zlib.net"), 0); + lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.zlib.net", "https://www.zlib.net"), 0); lineSizer->Add(new wxStaticText(parent, -1, ")"), 0); sizer->Add(lineSizer); } diff --git a/src/gui/TitleManager.h b/src/gui/TitleManager.h index 17a48976..2973618f 100644 --- a/src/gui/TitleManager.h +++ b/src/gui/TitleManager.h @@ -86,7 +86,6 @@ private: void OnDisconnect(wxCommandEvent& event); void OnDlFilterCheckbox(wxCommandEvent& event); - void OnDlCheckboxShowUpdates(wxCommandEvent& event); void SetConnected(bool state); diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/components/wxTitleManagerList.h index cab531c4..14721c57 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/components/wxTitleManagerList.h @@ -107,8 +107,7 @@ private: [[nodiscard]] boost::optional GetTitleEntry(long item); [[nodiscard]] boost::optional GetTitleEntry(const fs::path& path) const; [[nodiscard]] boost::optional GetTitleEntry(const fs::path& path); - - bool VerifyEntryFiles(TitleEntry& entry); + void OnConvertToCompressedFormat(uint64 titleId, uint64 rightClickedUID); bool DeleteEntry(long index, const TitleEntry& entry); diff --git a/src/gui/debugger/DisasmCtrl.cpp b/src/gui/debugger/DisasmCtrl.cpp index 21f6fc1d..c2cd5722 100644 --- a/src/gui/debugger/DisasmCtrl.cpp +++ b/src/gui/debugger/DisasmCtrl.cpp @@ -256,7 +256,7 @@ void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR { sint32 sImm = disasmInstr.operand[o].immS32; if (disasmInstr.operand[o].immWidth == 16 && (sImm & 0x8000)) - sImm |= 0xFFFF0000; + sImm |= (sint32)0xFFFF0000; if ((sImm > -10 && sImm < 10) || forceDecDisplay) string = wxString::Format("%d", sImm); diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index 0e13596b..dd77819c 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -1,7 +1,5 @@ #pragma once -#include - #if BOOST_OS_LINUX #include "xcb/xproto.h" #include From 757d458161180598e606ddddc6f2a9f157e956e0 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:52:54 +0200 Subject: [PATCH 053/101] Compatibility with fmtlib 10.1.x --- CMakeLists.txt | 2 +- src/Cafe/CafeSystem.cpp | 2 +- .../LatteDecompilerEmitGLSL.cpp | 5 ++++ src/Cemu/Logging/CemuLogging.h | 17 +----------- src/Common/MemPtr.h | 2 +- src/Common/precompiled.h | 26 +++++++++++++++++-- src/config/ConfigValue.h | 16 ------------ src/config/XMLConfig.h | 7 ++++- 8 files changed, 39 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5749acb..9dc1a6f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,7 +134,7 @@ find_package(ZLIB REQUIRED) find_package(zstd MODULE REQUIRED) # MODULE so that zstd::zstd is available find_package(OpenSSL COMPONENTS Crypto SSL REQUIRED) find_package(glm REQUIRED) -find_package(fmt 9.1.0...<10 REQUIRED) +find_package(fmt 9 REQUIRED) find_package(PNG REQUIRED) # glslang versions older than 11.11.0 define targets without a namespace diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 668def01..a3f42791 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -251,7 +251,7 @@ void InfoLog_PrintActiveSettings() if(!GetConfig().vk_accurate_barriers.GetValue()) cemuLog_log(LogType::Force, "Accurate barriers are disabled!"); } - cemuLog_log(LogType::Force, "Console language: {}", config.console_language); + cemuLog_log(LogType::Force, "Console language: {}", stdx::to_underlying(config.console_language.GetValue())); } struct SharedDataEntry diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index 486b7bf5..334b4855 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -908,6 +908,11 @@ void _emitOperandInputCode(LatteDecompilerShaderContext* shaderContext, LatteDec { char floatAsStr[32]; size_t floatAsStrLen = fmt::format_to_n(floatAsStr, 32, "{:#}", *(float*)&constVal).size; + if(floatAsStrLen > 0 && floatAsStr[floatAsStrLen-1] == '.') + { + floatAsStr[floatAsStrLen] = '0'; + floatAsStrLen++; + } cemu_assert_debug(floatAsStrLen >= 3); // shortest possible form is "0.0" src->add(std::string_view(floatAsStr, floatAsStrLen)); } diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index 8983c847..7d6499fe 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -70,21 +70,6 @@ bool cemuLog_log(LogType type, std::string_view text); bool cemuLog_log(LogType type, std::u8string_view text); void cemuLog_waitForFlush(); // wait until all log lines are written -template -auto ForwardEnum(T t) -{ - if constexpr (std::is_enum_v) - return fmt::underlying(t); - else - return std::forward(t); -} - -template -auto ForwardEnum(std::tuple t) -{ - return std::apply([](auto... x) { return std::make_tuple(ForwardEnum(x)...); }, t); -} - template bool cemuLog_log(LogType type, std::basic_string formatStr, TArgs&&... args) { @@ -98,7 +83,7 @@ bool cemuLog_log(LogType type, std::basic_string formatStr, TArgs&&... args) else { const auto format_view = fmt::basic_string_view(formatStr); - const auto text = fmt::vformat(format_view, fmt::make_format_args>(ForwardEnum(args)...)); + const auto text = fmt::vformat(format_view, fmt::make_format_args>(args...)); cemuLog_log(type, std::basic_string_view(text.data(), text.size())); } return true; diff --git a/src/Common/MemPtr.h b/src/Common/MemPtr.h index dc1ecd36..de787cc1 100644 --- a/src/Common/MemPtr.h +++ b/src/Common/MemPtr.h @@ -159,5 +159,5 @@ template struct fmt::formatter> : formatter { template - auto format(const MEMPTR& v, FormatContext& ctx) { return formatter::format(fmt::format("{:#x}", v.GetMPTR()), ctx); } + auto format(const MEMPTR& v, FormatContext& ctx) const -> format_context::iterator { return fmt::format_to(ctx.out(), "{:#x}", v.GetMPTR()); } }; diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index c55314d5..790a001a 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -552,6 +552,29 @@ inline uint32 GetTitleIdLow(uint64 titleId) #include "Cafe/HW/Espresso/PPCState.h" #include "Cafe/HW/Espresso/PPCCallback.h" +// generic formatter for enums (to underlying) +template + requires std::is_enum_v +struct fmt::formatter : fmt::formatter> +{ + auto format(const Enum& e, format_context& ctx) const + { + //return fmt::format_to(ctx.out(), "{}", fmt::underlying(e)); + + return formatter>::format(fmt::underlying(e), ctx); + } +}; + +// formatter for betype +template +struct fmt::formatter> : fmt::formatter +{ + auto format(const betype& e, format_context& ctx) const + { + return formatter::format(static_cast(e), ctx); + } +}; + // useful C++23 stuff that isn't yet widely supported // std::to_underlying @@ -561,5 +584,4 @@ namespace stdx constexpr std::underlying_type_t to_underlying(EnumT e) noexcept { return static_cast>(e); }; -} - +} \ No newline at end of file diff --git a/src/config/ConfigValue.h b/src/config/ConfigValue.h index 358af67a..43e2ad3b 100644 --- a/src/config/ConfigValue.h +++ b/src/config/ConfigValue.h @@ -232,19 +232,3 @@ private: const TType m_min_value; const TType m_max_value; }; - -template -struct fmt::formatter< ConfigValue > : formatter { - template - auto format(const ConfigValue& v, FormatContext& ctx) { - return formatter::format(v.GetValue(), ctx); - } -}; - -template -struct fmt::formatter< ConfigValueBounds > : formatter { - template - auto format(const ConfigValueBounds& v, FormatContext& ctx) { - return formatter::format(v.GetValue(), ctx); - } -}; \ No newline at end of file diff --git a/src/config/XMLConfig.h b/src/config/XMLConfig.h index 788dc9a7..2a32dc56 100644 --- a/src/config/XMLConfig.h +++ b/src/config/XMLConfig.h @@ -235,6 +235,12 @@ public: set(name, value.load()); } + template + void set(const char* name, const ConfigValue& value) + { + set(name, value.GetValue()); + } + void set(const char* name, uint64 value) { set(name, (sint64)value); @@ -462,4 +468,3 @@ public: private: T m_data; }; - From 29c823fa1fdba394b7fce21583f529d8941e653d Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:05:44 +0200 Subject: [PATCH 054/101] Latte: Fix uniform size limit being too low --- src/Cafe/HW/Latte/Core/Latte.h | 2 +- src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index f13abca0..861d7ddf 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -167,7 +167,7 @@ void LatteBufferCache_LoadRemappedUniforms(struct LatteDecompilerShader* shader, void LatteRenderTarget_updateViewport(); -#define LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE (1024) // maximum size for uniform blocks (in vec4s). On Nvidia hardware 4096 is the maximum (64K / 16 = 4096) all other vendors have much higher limits +#define LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE (4096) // maximum size for uniform blocks (in vec4s). On Nvidia hardware 4096 is the maximum (64K / 16 = 4096) all other vendors have much higher limits //static uint32 glTempError; //#define catchOpenGLError() glFinish(); if( (glTempError = glGetError()) != 0 ) { printf("OpenGL error 0x%x: %s : %d timestamp %08x\n", glTempError, __FILE__, __LINE__, GetTickCount()); __debugbreak(); } diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h index 92777844..1159614e 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h @@ -162,8 +162,8 @@ struct LatteDecompilerShader // compact resource lists for optimized access struct QuickBufferEntry { - uint8 index; - uint16 size; + uint32 index : 8; + uint32 size : 24; }; boost::container::static_vector list_quickBufferList; uint8 textureUnitList[LATTE_NUM_MAX_TEX_UNITS]; From db53f3b98020160e0890a1b237755d15c091bd83 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 2 Oct 2023 21:24:50 +0200 Subject: [PATCH 055/101] Fixes for titles in NUS format Symlinks were not handled correctly --- src/Cafe/Filesystem/FST/FST.cpp | 13 +++++++------ src/Cafe/Filesystem/FST/FST.h | 18 ++++++++++++------ src/Cafe/Filesystem/fscDeviceWud.cpp | 4 ++-- src/Cafe/TitleList/GameInfo.h | 4 ++-- .../Tools/DownloadManager/DownloadManager.cpp | 6 +++++- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/Cafe/Filesystem/FST/FST.cpp b/src/Cafe/Filesystem/FST/FST.cpp index 10ae659d..570671d4 100644 --- a/src/Cafe/Filesystem/FST/FST.cpp +++ b/src/Cafe/Filesystem/FST/FST.cpp @@ -686,25 +686,25 @@ bool FSTVolume::OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bo return true; } -bool FSTVolume::IsDirectory(FSTFileHandle& fileHandle) const +bool FSTVolume::IsDirectory(const FSTFileHandle& fileHandle) const { cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size()); return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::DIRECTORY; }; -bool FSTVolume::IsFile(FSTFileHandle& fileHandle) const +bool FSTVolume::IsFile(const FSTFileHandle& fileHandle) const { cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size()); return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::FILE; }; -bool FSTVolume::HasLinkFlag(FSTFileHandle& fileHandle) const +bool FSTVolume::HasLinkFlag(const FSTFileHandle& fileHandle) const { cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size()); return HAS_FLAG(m_entries[fileHandle.m_fstIndex].GetFlags(), FSTEntry::FLAGS::FLAG_LINK); }; -std::string_view FSTVolume::GetName(FSTFileHandle& fileHandle) const +std::string_view FSTVolume::GetName(const FSTFileHandle& fileHandle) const { if (fileHandle.m_fstIndex > m_entries.size()) return ""; @@ -712,7 +712,7 @@ std::string_view FSTVolume::GetName(FSTFileHandle& fileHandle) const return entryName; } -std::string FSTVolume::GetPath(FSTFileHandle& fileHandle) const +std::string FSTVolume::GetPath(const FSTFileHandle& fileHandle) const { std::string path; auto& entry = m_entries[fileHandle.m_fstIndex]; @@ -743,7 +743,7 @@ std::string FSTVolume::GetPath(FSTFileHandle& fileHandle) const return path; } -uint32 FSTVolume::GetFileSize(FSTFileHandle& fileHandle) const +uint32 FSTVolume::GetFileSize(const FSTFileHandle& fileHandle) const { if (m_entries[fileHandle.m_fstIndex].GetType() != FSTEntry::TYPE::FILE) return 0; @@ -994,6 +994,7 @@ bool FSTVolume::OpenDirectoryIterator(std::string_view path, FSTDirectoryIterato if (!IsDirectory(fileHandle)) return false; auto const& fstEntry = m_entries[fileHandle.m_fstIndex]; + directoryIteratorOut.dirHandle = fileHandle; directoryIteratorOut.startIndex = fileHandle.m_fstIndex + 1; directoryIteratorOut.endIndex = fstEntry.dirInfo.endIndex; directoryIteratorOut.currentIndex = directoryIteratorOut.startIndex; diff --git a/src/Cafe/Filesystem/FST/FST.h b/src/Cafe/Filesystem/FST/FST.h index 98bf1ae6..24fc39ea 100644 --- a/src/Cafe/Filesystem/FST/FST.h +++ b/src/Cafe/Filesystem/FST/FST.h @@ -11,7 +11,13 @@ private: struct FSTDirectoryIterator { friend class FSTVolume; + + const FSTFileHandle& GetDirHandle() const + { + return dirHandle; + } private: + FSTFileHandle dirHandle; uint32 startIndex; uint32 endIndex; uint32 currentIndex; @@ -43,15 +49,15 @@ public: bool OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bool openOnlyFiles = false); // file and directory functions - bool IsDirectory(FSTFileHandle& fileHandle) const; - bool IsFile(FSTFileHandle& fileHandle) const; - bool HasLinkFlag(FSTFileHandle& fileHandle) const; + bool IsDirectory(const FSTFileHandle& fileHandle) const; + bool IsFile(const FSTFileHandle& fileHandle) const; + bool HasLinkFlag(const FSTFileHandle& fileHandle) const; - std::string_view GetName(FSTFileHandle& fileHandle) const; - std::string GetPath(FSTFileHandle& fileHandle) const; + std::string_view GetName(const FSTFileHandle& fileHandle) const; + std::string GetPath(const FSTFileHandle& fileHandle) const; // file functions - uint32 GetFileSize(FSTFileHandle& fileHandle) const; + uint32 GetFileSize(const FSTFileHandle& fileHandle) const; uint32 ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size, void* dataOut); // directory iterator diff --git a/src/Cafe/Filesystem/fscDeviceWud.cpp b/src/Cafe/Filesystem/fscDeviceWud.cpp index bf43bf3e..517c8573 100644 --- a/src/Cafe/Filesystem/fscDeviceWud.cpp +++ b/src/Cafe/Filesystem/fscDeviceWud.cpp @@ -128,7 +128,7 @@ class fscDeviceWUDC : public fscDeviceC if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE)) { FSTFileHandle fstFileHandle; - if (mountedVolume->OpenFile(path, fstFileHandle, true)) + if (mountedVolume->OpenFile(path, fstFileHandle, true) && !mountedVolume->HasLinkFlag(fstFileHandle)) { *fscStatus = FSC_STATUS_OK; return new FSCDeviceWudFileCtx(mountedVolume, fstFileHandle); @@ -137,7 +137,7 @@ class fscDeviceWUDC : public fscDeviceC if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR)) { FSTDirectoryIterator dirIterator; - if (mountedVolume->OpenDirectoryIterator(path, dirIterator)) + if (mountedVolume->OpenDirectoryIterator(path, dirIterator) && !mountedVolume->HasLinkFlag(dirIterator.GetDirHandle())) { *fscStatus = FSC_STATUS_OK; return new FSCDeviceWudFileCtx(mountedVolume, dirIterator); diff --git a/src/Cafe/TitleList/GameInfo.h b/src/Cafe/TitleList/GameInfo.h index 8836d1e4..6e922b93 100644 --- a/src/Cafe/TitleList/GameInfo.h +++ b/src/Cafe/TitleList/GameInfo.h @@ -136,8 +136,8 @@ private: // this is to stay consistent with previous Cemu versions which did not support NUS format at all TitleInfo::TitleDataFormat currentFormat = currentTitle.GetFormat(); TitleInfo::TitleDataFormat newFormat = newTitle.GetFormat(); - if (currentFormat != newFormat && currentFormat == TitleInfo::TitleDataFormat::NUS) - return true; + if (currentFormat != TitleInfo::TitleDataFormat::NUS && newFormat == TitleInfo::TitleDataFormat::NUS) + return false; return true; }; diff --git a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp index 09093792..807a4e72 100644 --- a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp +++ b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp @@ -1286,7 +1286,11 @@ bool DownloadManager::asyncPackageInstallRecursiveExtractFiles(Package* package, setPackageError(package, "Internal error"); return false; } - + if (fstVolume->HasLinkFlag(dirItr.GetDirHandle())) + { + cemu_assert_suspicious(); + return true; + } FSTFileHandle itr; while (fstVolume->Next(dirItr, itr)) { From db44a2d130d6d4ff497c60d4c7a0d04a56c2779f Mon Sep 17 00:00:00 2001 From: Cemu-Language CI Date: Wed, 4 Oct 2023 21:39:01 +0000 Subject: [PATCH 056/101] Update translation files --- bin/resources/de/cemu.mo | Bin 27890 -> 65048 bytes bin/resources/he/cemu.mo | Bin 0 -> 23202 bytes bin/resources/it/cemu.mo | Bin 57658 -> 71398 bytes bin/resources/ko/cemu.mo | Bin 62866 -> 69116 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 bin/resources/he/cemu.mo diff --git a/bin/resources/de/cemu.mo b/bin/resources/de/cemu.mo index 70dbb2cb3863647139b90f81460f29f7a247566f..918fcd3546c99e9c385fe726bbd91c9e95b19270 100644 GIT binary patch literal 65048 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 Mk4=V*$t{1g0EF1O#sB~S diff --git a/bin/resources/he/cemu.mo b/bin/resources/he/cemu.mo new file mode 100644 index 0000000000000000000000000000000000000000..072e48c61e40f0228e3cc80d0ab8099a2fb8a09c GIT binary patch literal 23202 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 afRZW5kMlu^4ch7hXKRpmK{Ilo@h||zafsCb literal 0 HcmV?d00001 diff --git a/bin/resources/it/cemu.mo b/bin/resources/it/cemu.mo index 18ca9c9374e33d17a52d94daa179b262f8f7014d..20d5bb930bfa4255ed05075deb8485216e2520f7 100644 GIT binary patch delta 36944 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 T@;1K+u>dz8BmiENo5=tGfP!y| delta 23338 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 delta 17724 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 E0ChCV`2YX_ From d4a2a8e8de39821a22dd2ad82d2186796e566af5 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Mon, 16 Oct 2023 07:33:12 +0200 Subject: [PATCH 057/101] Vulkan: Cleanup image barrier code (#988) --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 47 ++++--------------- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 39 +++++++++------ 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index d084a399..052ca21a 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2801,47 +2801,18 @@ void VulkanRenderer::ClearColorImageRaw(VkImage image, uint32 sliceIndex, uint32 { draw_endRenderPass(); - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = inputLayout; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = mipIndex; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = sliceIndex; - barrier.subresourceRange.layerCount = 1; + VkImageSubresourceRange subresourceRange{}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = mipIndex; + subresourceRange.levelCount = 1; + subresourceRange.baseArrayLayer = sliceIndex; + subresourceRange.layerCount = 1; - VkPipelineStageFlags srcStages = 0; - VkPipelineStageFlags dstStages = 0; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = 0; - barrier_calcStageAndMask(srcStages, barrier.srcAccessMask); - barrier_calcStageAndMask(dstStages, barrier.dstAccessMask); + barrier_image(image, subresourceRange, inputLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdPipelineBarrier(m_state.currentCommandBuffer, srcStages, dstStages, 0, 0, nullptr, 0, nullptr, 1, &barrier); + vkCmdClearColorImage(m_state.currentCommandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &subresourceRange); - VkImageSubresourceRange imageRange{}; - imageRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageRange.baseArrayLayer = sliceIndex; - imageRange.layerCount = 1; - imageRange.baseMipLevel = mipIndex; - imageRange.levelCount = 1; - - vkCmdClearColorImage(m_state.currentCommandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &imageRange); - - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.newLayout = outputLayout; - - srcStages = 0; - dstStages = 0; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = 0; - barrier_calcStageAndMask(srcStages, barrier.srcAccessMask); - barrier_calcStageAndMask(dstStages, barrier.dstAccessMask); - vkCmdPipelineBarrier(m_state.currentCommandBuffer, srcStages, dstStages, 0, 0, nullptr, 0, nullptr, 1, &barrier); + barrier_image(image, subresourceRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, outputLayout); } void VulkanRenderer::ClearColorImage(LatteTextureVk* vkTexture, uint32 sliceIndex, uint32 mipIndex, const VkClearColorValue& color, VkImageLayout outputLayout) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 24008ee3..3d68f844 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -906,10 +906,8 @@ private: } template - void barrier_image(LatteTextureVk* vkTexture, VkImageSubresourceLayers& subresourceLayers, VkImageLayout newLayout) + void barrier_image(VkImage imageVk, VkImageSubresourceRange& subresourceRange, VkImageLayout oldLayout, VkImageLayout newLayout) { - VkImage imageVk = vkTexture->GetImageObj()->m_image; - VkPipelineStageFlags srcStages = 0; VkPipelineStageFlags dstStages = 0; @@ -922,22 +920,33 @@ private: barrier_calcStageAndMask(srcStages, imageMemBarrier.srcAccessMask); barrier_calcStageAndMask(dstStages, imageMemBarrier.dstAccessMask); imageMemBarrier.image = imageVk; - imageMemBarrier.subresourceRange.aspectMask = subresourceLayers.aspectMask; - imageMemBarrier.subresourceRange.baseArrayLayer = subresourceLayers.baseArrayLayer; - imageMemBarrier.subresourceRange.layerCount = subresourceLayers.layerCount; - imageMemBarrier.subresourceRange.baseMipLevel = subresourceLayers.mipLevel; - imageMemBarrier.subresourceRange.levelCount = 1; - imageMemBarrier.oldLayout = vkTexture->GetImageLayout(imageMemBarrier.subresourceRange); + imageMemBarrier.subresourceRange = subresourceRange; + imageMemBarrier.oldLayout = oldLayout; imageMemBarrier.newLayout = newLayout; vkCmdPipelineBarrier(m_state.currentCommandBuffer, - srcStages, dstStages, - 0, - 0, NULL, - 0, NULL, - 1, &imageMemBarrier); + srcStages, dstStages, + 0, + 0, NULL, + 0, NULL, + 1, &imageMemBarrier); + } - vkTexture->SetImageLayout(imageMemBarrier.subresourceRange, newLayout); + template + void barrier_image(LatteTextureVk* vkTexture, VkImageSubresourceLayers& subresourceLayers, VkImageLayout newLayout) + { + VkImage imageVk = vkTexture->GetImageObj()->m_image; + + VkImageSubresourceRange subresourceRange; + subresourceRange.aspectMask = subresourceLayers.aspectMask; + subresourceRange.baseArrayLayer = subresourceLayers.baseArrayLayer; + subresourceRange.layerCount = subresourceLayers.layerCount; + subresourceRange.baseMipLevel = subresourceLayers.mipLevel; + subresourceRange.levelCount = 1; + + barrier_image(imageVk, subresourceRange, vkTexture->GetImageLayout(subresourceRange), newLayout); + + vkTexture->SetImageLayout(subresourceRange, newLayout); } From 13a50a915e55a257388c369085b85bac0a4aa3cc Mon Sep 17 00:00:00 2001 From: Francesco Saltori Date: Mon, 16 Oct 2023 13:41:06 +0200 Subject: [PATCH 058/101] Fix several language selection issues (#994) --- src/gui/CemuApp.cpp | 77 +++++++++++++------------------ src/gui/CemuApp.h | 7 +-- src/gui/GeneralSettings2.cpp | 4 +- src/gui/components/wxGameList.cpp | 2 +- 4 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 04823ca4..53a42a10 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -99,29 +99,7 @@ bool CemuApp::OnInit() wxInitAllImageHandlers(); - - m_languages = GetAvailableLanguages(); - - const sint32 language = GetConfig().language; - const auto it = std::find_if(m_languages.begin(), m_languages.end(), [language](const wxLanguageInfo* info) { return info->Language == language; }); - if (it != m_languages.end() && wxLocale::IsAvailable(language)) - { - if (m_locale.Init(language)) - { - m_locale.AddCatalogLookupPathPrefix(ActiveSettings::GetDataPath("resources").generic_string()); - m_locale.AddCatalog("cemu"); - } - } - - if (!m_locale.IsOk()) - { - if (!wxLocale::IsAvailable(wxLANGUAGE_DEFAULT) || !m_locale.Init(wxLANGUAGE_DEFAULT)) - { - m_locale.Init(wxLANGUAGE_ENGLISH); - m_locale.AddCatalogLookupPathPrefix(ActiveSettings::GetDataPath("resources").generic_string()); - m_locale.AddCatalog("cemu"); - } - } + LocalizeUI(); // fill colour db wxTheColourDatabase->AddColour("ERROR", wxColour(0xCC, 0, 0)); @@ -231,33 +209,44 @@ int CemuApp::FilterEvent(wxEvent& event) return wxApp::FilterEvent(event); } -std::vector CemuApp::GetAvailableLanguages() +std::vector CemuApp::GetLanguages() const { + std::vector availableLanguages(m_availableTranslations); + availableLanguages.insert(availableLanguages.begin(), wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH)); + return availableLanguages; +} + +void CemuApp::LocalizeUI() { - const auto path = ActiveSettings::GetDataPath("resources"); - if (!exists(path)) - return {}; - - std::vector result; - for (const auto& p : fs::directory_iterator(path)) + std::unique_ptr translationsMgr(new wxTranslations()); + m_availableTranslations = GetAvailableTranslationLanguages(translationsMgr.get()); + + const sint32 configuredLanguage = GetConfig().language; + bool isTranslationAvailable = std::any_of(m_availableTranslations.begin(), m_availableTranslations.end(), + [configuredLanguage](const wxLanguageInfo* info) { return info->Language == configuredLanguage; }); + if (configuredLanguage == wxLANGUAGE_DEFAULT || isTranslationAvailable) { - if (!fs::is_directory(p)) - continue; + translationsMgr->SetLanguage(static_cast(configuredLanguage)); + translationsMgr->AddCatalog("cemu"); - const auto& path = p.path(); - auto filename = path.filename(); + if (translationsMgr->IsLoaded("cemu") && wxLocale::IsAvailable(configuredLanguage)) + m_locale.Init(configuredLanguage); - const auto* lang_info = wxLocale::FindLanguageInfo(filename.c_str()); - if (!lang_info) - continue; - - const auto language_file = path / "cemu.mo"; - if (!fs::exists(language_file)) - continue; - - result.emplace_back(lang_info); + // This must be run after wxLocale::Init, as the latter sets up its own wxTranslations instance which we want to override + wxTranslations::Set(translationsMgr.release()); } +} - return result; +std::vector CemuApp::GetAvailableTranslationLanguages(wxTranslations* translationsMgr) +{ + wxFileTranslationsLoader::AddCatalogLookupPathPrefix(wxHelper::FromPath(ActiveSettings::GetDataPath("resources"))); + std::vector languages; + for (const auto& langName : translationsMgr->GetAvailableTranslations("cemu")) + { + const auto* langInfo = wxLocale::FindLanguageInfo(langName); + if (langInfo) + languages.emplace_back(langInfo); + } + return languages; } void CemuApp::CreateDefaultFiles(bool first_start) diff --git a/src/gui/CemuApp.h b/src/gui/CemuApp.h index 1dac29f1..cfdab0a2 100644 --- a/src/gui/CemuApp.h +++ b/src/gui/CemuApp.h @@ -13,8 +13,7 @@ public: void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) override; int FilterEvent(wxEvent& event) override; - const std::vector& GetLanguages() const { return m_languages; } - static std::vector GetAvailableLanguages(); + std::vector GetLanguages() const; static void CreateDefaultFiles(bool first_start = false); static bool TrySelectMLCPath(fs::path path); @@ -22,11 +21,13 @@ public: private: void ActivateApp(wxActivateEvent& event); + void LocalizeUI(); + static std::vector GetAvailableTranslationLanguages(wxTranslations* translationsMgr); MainWindow* m_mainFrame = nullptr; wxLocale m_locale; - std::vector m_languages; + std::vector m_availableTranslations; }; wxDECLARE_APP(CemuApp); diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index e406c698..e33cfbf6 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -123,7 +123,7 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook) first_row->Add(new wxStaticText(box, wxID_ANY, _("Language"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - wxString language_choices[] = { _("Default"), "English" }; + wxString language_choices[] = { _("Default") }; m_language = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(language_choices), language_choices); m_language->SetSelection(0); m_language->SetToolTip(_("Changes the interface language of Cemu\nAvailable languages are stored in the translation directory\nA restart will be required after changing the language")); @@ -928,8 +928,6 @@ void GeneralSettings2::StoreConfig() auto selection = m_language->GetSelection(); if (selection == 0) GetConfig().language = wxLANGUAGE_DEFAULT; - else if (selection == 1) - GetConfig().language = wxLANGUAGE_ENGLISH; else { const auto language = m_language->GetStringSelection(); diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index a64b49bf..2c78ea3c 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1027,7 +1027,7 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) if (playTimeStat.last_played.year != 0) { const wxDateTime tmp((wxDateTime::wxDateTime_t)playTimeStat.last_played.day, (wxDateTime::Month)playTimeStat.last_played.month, (wxDateTime::wxDateTime_t)playTimeStat.last_played.year, 0, 0, 0, 0); - SetItem(index, ColumnGameStarted, tmp.FormatISODate()); + SetItem(index, ColumnGameStarted, tmp.FormatDate()); } else SetItem(index, ColumnGameStarted, _("never")); From 0d71885c881984978ab8e0375975be3053a0a864 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 17 Oct 2023 04:41:22 +0200 Subject: [PATCH 059/101] nn_fp: Full rework of friend service --- src/Cafe/CafeSystem.cpp | 24 +- .../Interpreter/PPCInterpreterHLE.cpp | 2 +- src/Cafe/HW/Espresso/PPCState.h | 2 +- src/Cafe/IOSU/iosu_types_common.h | 14 +- src/Cafe/IOSU/kernel/iosu_kernel.cpp | 232 +- src/Cafe/IOSU/kernel/iosu_kernel.h | 7 +- src/Cafe/IOSU/legacy/iosu_act.cpp | 9 + src/Cafe/IOSU/legacy/iosu_act.h | 4 + src/Cafe/IOSU/legacy/iosu_fpd.cpp | 2105 ++++++++++------- src/Cafe/IOSU/legacy/iosu_fpd.h | 417 ++-- src/Cafe/IOSU/nn/iosu_nn_service.cpp | 129 +- src/Cafe/IOSU/nn/iosu_nn_service.h | 67 + src/Cafe/OS/RPL/rpl.cpp | 2 +- src/Cafe/OS/common/OSCommon.cpp | 5 +- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 86 +- src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp | 17 - src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp | 23 +- src/Cafe/OS/libs/h264_avc/H264Dec.cpp | 14 +- src/Cafe/OS/libs/nn_act/nn_act.cpp | 4 +- src/Cafe/OS/libs/nn_fp/nn_fp.cpp | 1269 +++++----- .../nn_olv/nn_olv_DownloadCommunityTypes.cpp | 4 +- .../OS/libs/nn_olv/nn_olv_InitializeTypes.cpp | 4 +- src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp | 10 +- .../nn_olv/nn_olv_UploadCommunityTypes.cpp | 4 +- .../nn_olv/nn_olv_UploadFavoriteTypes.cpp | 4 +- src/Cemu/Logging/CemuLogging.cpp | 1 + src/Cemu/Logging/CemuLogging.h | 1 + src/Cemu/napi/napi_act.cpp | 2 +- src/Cemu/nex/nex.cpp | 6 +- src/Cemu/nex/nexFriends.cpp | 211 +- src/Cemu/nex/nexFriends.h | 57 +- src/Common/CMakeLists.txt | 1 + src/Common/CafeString.h | 73 + src/Common/StackAllocator.h | 31 +- src/gui/MainWindow.cpp | 1 + src/util/helpers/StringHelpers.h | 1 + .../highresolutiontimer/HighResolutionTimer.h | 10 + 37 files changed, 2862 insertions(+), 1991 deletions(-) create mode 100644 src/Common/CafeString.h diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index a3f42791..3d06281e 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -526,6 +526,13 @@ namespace CafeSystem cemuLog_log(LogType::Force, "Platform: {}", platform); } + static std::vector s_iosuModules = + { + // entries in this list are ordered by initialization order. Shutdown in reverse order + iosu::kernel::GetModule(), + iosu::fpd::GetModule() + }; + // initialize all subsystems which are persistent and don't depend on a game running void Initialize() { @@ -550,14 +557,15 @@ namespace CafeSystem // allocate memory for all SysAllocators // must happen before COS module init, but also before iosu::kernel::Initialize() SysAllocatorContainer::GetInstance().Initialize(); - // init IOSU + // init IOSU modules + for(auto& module : s_iosuModules) + module->SystemLaunch(); + // init IOSU (deprecated manual init) iosuCrypto_init(); - iosu::kernel::Initialize(); iosu::fsa::Initialize(); iosuIoctl_init(); iosuAct_init_depr(); iosu::act::Initialize(); - iosu::fpd::Initialize(); iosu::iosuMcp_init(); iosu::mcp::Init(); iosu::iosuAcp_init(); @@ -593,11 +601,14 @@ namespace CafeSystem // if a title is running, shut it down if (sSystemRunning) ShutdownTitle(); - // shutdown persistent subsystems + // shutdown persistent subsystems (deprecated manual shutdown) iosu::odm::Shutdown(); iosu::act::Stop(); iosu::mcp::Shutdown(); iosu::fsa::Shutdown(); + // shutdown IOSU modules + for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) + (*it)->SystemExit(); s_initialized = false; } @@ -821,7 +832,8 @@ namespace CafeSystem void _LaunchTitleThread() { - // init + for(auto& module : s_iosuModules) + module->TitleStart(); cemu_initForGame(); // enter scheduler if (ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler) @@ -956,6 +968,8 @@ namespace CafeSystem nn::save::ResetToDefaultState(); coreinit::__OSDeleteAllActivePPCThreads(); RPLLoader_ResetState(); + for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) + (*it)->TitleStop(); // stop time tracking iosu::pdm::Stop(); // reset Cemu subsystems diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp index 6aa1fcfa..24219e66 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp @@ -19,7 +19,7 @@ void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU) std::vector* sPPCHLETable{}; -HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall) +HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName) { if (!sPPCHLETable) sPPCHLETable = new std::vector(); diff --git a/src/Cafe/HW/Espresso/PPCState.h b/src/Cafe/HW/Espresso/PPCState.h index 85b2dc04..134f73a8 100644 --- a/src/Cafe/HW/Espresso/PPCState.h +++ b/src/Cafe/HW/Espresso/PPCState.h @@ -230,7 +230,7 @@ static inline float flushDenormalToZero(float f) typedef void(*HLECALL)(PPCInterpreter_t* hCPU); typedef sint32 HLEIDX; -HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall); +HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName); HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex); // HLE scheduler diff --git a/src/Cafe/IOSU/iosu_types_common.h b/src/Cafe/IOSU/iosu_types_common.h index 94c0cd2c..1f7f78f8 100644 --- a/src/Cafe/IOSU/iosu_types_common.h +++ b/src/Cafe/IOSU/iosu_types_common.h @@ -1,6 +1,9 @@ #pragma once using IOSMsgQueueId = uint32; +using IOSTimerId = uint32; + +static constexpr IOSTimerId IOSInvalidTimerId = 0xFFFFFFFF; // returned for syscalls // maybe also shared with IPC? @@ -19,4 +22,13 @@ enum IOS_ERROR : sint32 inline bool IOS_ResultIsError(const IOS_ERROR err) { return (err & 0x80000000) != 0; -} \ No newline at end of file +} + +class IOSUModule +{ + public: + virtual void SystemLaunch() {}; // CafeSystem is initialized + virtual void SystemExit() {}; // CafeSystem is shutdown + virtual void TitleStart() {}; // foreground title is launched + virtual void TitleStop() {}; // foreground title is closed +}; diff --git a/src/Cafe/IOSU/kernel/iosu_kernel.cpp b/src/Cafe/IOSU/kernel/iosu_kernel.cpp index 680170bc..52097698 100644 --- a/src/Cafe/IOSU/kernel/iosu_kernel.cpp +++ b/src/Cafe/IOSU/kernel/iosu_kernel.cpp @@ -1,6 +1,8 @@ #include "iosu_kernel.h" #include "util/helpers/fspinlock.h" +#include "util/helpers/helpers.h" #include "Cafe/OS/libs/coreinit/coreinit_IPC.h" +#include "util/highresolutiontimer/HighResolutionTimer.h" namespace iosu { @@ -8,6 +10,9 @@ namespace iosu { std::mutex sInternalMutex; + void IOS_DestroyResourceManagerForQueueId(IOSMsgQueueId msgQueueId); + void _IPCDestroyAllHandlesForMsgQueue(IOSMsgQueueId msgQueueId); + static void _assume_lock() { #ifdef CEMU_DEBUG_ASSERT @@ -84,6 +89,19 @@ namespace iosu return queueHandle; } + IOS_ERROR IOS_DestroyMessageQueue(IOSMsgQueueId msgQueueId) + { + std::unique_lock _l(sInternalMutex); + IOSMessageQueue* msgQueue = nullptr; + IOS_ERROR r = _IOS_GetMessageQueue(msgQueueId, msgQueue); + if (r != IOS_ERROR_OK) + return r; + msgQueue->msgArraySize = 0; + msgQueue->queueHandle = 0; + IOS_DestroyResourceManagerForQueueId(msgQueueId); + return IOS_ERROR_OK; + } + IOS_ERROR IOS_SendMessage(IOSMsgQueueId msgQueueId, IOSMessage message, uint32 flags) { std::unique_lock _l(sInternalMutex); @@ -146,6 +164,133 @@ namespace iosu return IOS_ERROR_OK; } + /* timer */ + + std::mutex sTimerMutex; + std::condition_variable sTimerCV; + std::atomic_bool sTimerThreadStop; + + struct IOSTimer + { + IOSMsgQueueId queueId; + uint32 message; + HRTick nextFire; + HRTick repeat; + bool isValid; + }; + + std::vector sTimers; + std::vector sTimersFreeHandles; + + auto sTimerSortComparator = [](const IOSTimerId& idA, const IOSTimerId& idB) + { + // order by nextFire, then by timerId to avoid duplicate keys + IOSTimer& timerA = sTimers[idA]; + IOSTimer& timerB = sTimers[idB]; + if (timerA.nextFire != timerB.nextFire) + return timerA.nextFire < timerB.nextFire; + return idA < idB; + }; + std::set sTimerByFireTime; + + IOSTimer& IOS_GetFreeTimer() + { + cemu_assert_debug(!sTimerMutex.try_lock()); // lock must be held by current thread + if (sTimersFreeHandles.empty()) + return sTimers.emplace_back(); + IOSTimerId timerId = sTimersFreeHandles.back(); + sTimersFreeHandles.pop_back(); + return sTimers[timerId]; + } + + void IOS_TimerSetNextFireTime(IOSTimer& timer, HRTick nextFire) + { + cemu_assert_debug(!sTimerMutex.try_lock()); // lock must be held by current thread + IOSTimerId timerId = &timer - sTimers.data(); + auto it = sTimerByFireTime.find(timerId); + if(it != sTimerByFireTime.end()) + sTimerByFireTime.erase(it); + timer.nextFire = nextFire; + if(nextFire != 0) + sTimerByFireTime.insert(timerId); + } + + void IOS_StopTimerInternal(IOSTimerId timerId) + { + cemu_assert_debug(!sTimerMutex.try_lock()); + IOS_TimerSetNextFireTime(sTimers[timerId], 0); + } + + IOS_ERROR IOS_CreateTimer(uint32 startMicroseconds, uint32 repeatMicroseconds, uint32 queueId, uint32 message) + { + std::unique_lock _l(sTimerMutex); + IOSTimer& timer = IOS_GetFreeTimer(); + timer.queueId = queueId; + timer.message = message; + HRTick nextFire = HighResolutionTimer::now().getTick() + HighResolutionTimer::microsecondsToTicks(startMicroseconds); + timer.repeat = HighResolutionTimer::microsecondsToTicks(repeatMicroseconds); + IOS_TimerSetNextFireTime(timer, nextFire); + timer.isValid = true; + sTimerCV.notify_one(); + return (IOS_ERROR)(&timer - sTimers.data()); + } + + IOS_ERROR IOS_StopTimer(IOSTimerId timerId) + { + std::unique_lock _l(sTimerMutex); + if (timerId >= sTimers.size() || !sTimers[timerId].isValid) + return IOS_ERROR_INVALID; + IOS_StopTimerInternal(timerId); + return IOS_ERROR_OK; + } + + IOS_ERROR IOS_DestroyTimer(IOSTimerId timerId) + { + std::unique_lock _l(sTimerMutex); + if (timerId >= sTimers.size() || !sTimers[timerId].isValid) + return IOS_ERROR_INVALID; + IOS_StopTimerInternal(timerId); + sTimers[timerId].isValid = false; + sTimersFreeHandles.push_back(timerId); + return IOS_ERROR_OK; + } + + void IOSTimerThread() + { + SetThreadName("IOS-Timer"); + std::unique_lock _l(sTimerMutex); + while (!sTimerThreadStop) + { + if (sTimerByFireTime.empty()) + { + sTimerCV.wait_for(_l, std::chrono::milliseconds(10000)); + continue; + } + IOSTimerId timerId = *sTimerByFireTime.begin(); + IOSTimer& timer = sTimers[timerId]; + HRTick now = HighResolutionTimer::now().getTick(); + if (now >= timer.nextFire) + { + if(timer.repeat == 0) + IOS_TimerSetNextFireTime(timer, 0); + else + IOS_TimerSetNextFireTime(timer, timer.nextFire + timer.repeat); + IOSMsgQueueId queueId = timer.queueId; + uint32 message = timer.message; + // fire timer + _l.unlock(); + IOSMessage msg; + IOS_SendMessage(queueId, message, 1); + _l.lock(); + continue; + } + else + { + sTimerCV.wait_for(_l, std::chrono::microseconds(HighResolutionTimer::ticksToMicroseconds(timer.nextFire - now))); + } + } + } + /* devices and IPC */ struct IOSResourceManager @@ -209,6 +354,23 @@ namespace iosu return IOS_ERROR_OK; } + void IOS_DestroyResourceManagerForQueueId(IOSMsgQueueId msgQueueId) + { + _assume_lock(); + // destroy all IPC handles associated with this queue + _IPCDestroyAllHandlesForMsgQueue(msgQueueId); + // destroy device resource manager + for (auto& it : sDeviceResources) + { + if (it.isSet && it.msgQueueId == msgQueueId) + { + it.isSet = false; + it.path.clear(); + it.msgQueueId = 0; + } + } + } + IOS_ERROR IOS_DeviceAssociateId(const char* devicePath, uint32 id) { // not yet implemented @@ -344,6 +506,22 @@ namespace iosu return IOS_ERROR_OK; } + void _IPCDestroyAllHandlesForMsgQueue(IOSMsgQueueId msgQueueId) + { + _assume_lock(); + for (auto& it : sActiveDeviceHandles) + { + if (it.isSet && it.msgQueueId == msgQueueId) + { + it.isSet = false; + it.path.clear(); + it.handleCheckValue = 0; + it.hasDispatchTargetHandle = false; + it.msgQueueId = 0; + } + } + } + IOS_ERROR _IPCAssignDispatchTargetHandle(IOSDevHandle devHandle, IOSDevHandle internalHandle) { std::unique_lock _lock(sInternalMutex); @@ -453,7 +631,6 @@ namespace iosu uint32 numIn = dispatchCmd->body.args[1]; uint32 numOut = dispatchCmd->body.args[2]; IPCIoctlVector* vec = MEMPTR(cmd.args[3]).GetPtr(); - // copy the vector array uint32 numVec = numIn + numOut; if (numVec <= 8) @@ -466,8 +643,23 @@ namespace iosu // reuse the original vector pointer cemuLog_log(LogType::Force, "Info: Ioctlv command with more than 8 vectors"); } - IOS_ERROR r = _IPCDispatchToResourceManager(dispatchCmd->body.devHandle, dispatchCmd); - return r; + return _IPCDispatchToResourceManager(dispatchCmd->body.devHandle, dispatchCmd); + } + + // normally COS kernel handles this, but currently we skip the IPC getting proxied through it + IOS_ERROR _IPCHandlerIn_TranslateVectorAddresses(IOSDispatchableCommand* dispatchCmd) + { + uint32 numIn = dispatchCmd->body.args[1]; + uint32 numOut = dispatchCmd->body.args[2]; + IPCIoctlVector* vec = MEMPTR(dispatchCmd->body.args[3]).GetPtr(); + for (uint32 i = 0; i < numIn + numOut; i++) + { + if (vec[i].baseVirt == nullptr && vec[i].size != 0) + return IOS_ERROR_INVALID; + // todo - check for valid pointer range + vec[i].basePhys = vec[i].baseVirt; + } + return IOS_ERROR_OK; } // called by COS directly @@ -494,7 +686,11 @@ namespace iosu r = _IPCHandlerIn_IOS_Ioctl(dispatchCmd); break; case IPCCommandId::IOS_IOCTLV: - r = _IPCHandlerIn_IOS_Ioctlv(dispatchCmd); + r = _IPCHandlerIn_TranslateVectorAddresses(dispatchCmd); + if(r < 0) + cemuLog_log(LogType::Force, "Ioctlv error"); + else + r = _IPCHandlerIn_IOS_Ioctlv(dispatchCmd); break; default: cemuLog_log(LogType::Force, "Invalid IPC command {}", (uint32)(IPCCommandId)cmd->cmdId); @@ -547,10 +743,34 @@ namespace iosu return IOS_ERROR_OK; } - void Initialize() + class : public ::IOSUModule { - _IPCInitDispatchablePool(); + void SystemLaunch() override + { + _IPCInitDispatchablePool(); + // start timer thread + sTimerThreadStop = false; + m_timerThread = std::thread(IOSTimerThread); + } + + void SystemExit() override + { + // stop timer thread + sTimerThreadStop = true; + sTimerCV.notify_one(); + m_timerThread.join(); + // reset resources + // todo + } + + std::thread m_timerThread; + }sIOSUModuleKernel; + + IOSUModule* GetModule() + { + return static_cast(&sIOSUModuleKernel); } + } } \ No newline at end of file diff --git a/src/Cafe/IOSU/kernel/iosu_kernel.h b/src/Cafe/IOSU/kernel/iosu_kernel.h index 2b82374e..0355c118 100644 --- a/src/Cafe/IOSU/kernel/iosu_kernel.h +++ b/src/Cafe/IOSU/kernel/iosu_kernel.h @@ -9,15 +9,20 @@ namespace iosu using IOSMessage = uint32; IOSMsgQueueId IOS_CreateMessageQueue(IOSMessage* messageArray, uint32 messageCount); + IOS_ERROR IOS_DestroyMessageQueue(IOSMsgQueueId msgQueueId); IOS_ERROR IOS_SendMessage(IOSMsgQueueId msgQueueId, IOSMessage message, uint32 flags); IOS_ERROR IOS_ReceiveMessage(IOSMsgQueueId msgQueueId, IOSMessage* messageOut, uint32 flags); + IOS_ERROR IOS_CreateTimer(uint32 startMicroseconds, uint32 repeatMicroseconds, uint32 queueId, uint32 message); + IOS_ERROR IOS_StopTimer(IOSTimerId timerId); + IOS_ERROR IOS_DestroyTimer(IOSTimerId timerId); + IOS_ERROR IOS_RegisterResourceManager(const char* devicePath, IOSMsgQueueId msgQueueId); IOS_ERROR IOS_DeviceAssociateId(const char* devicePath, uint32 id); IOS_ERROR IOS_ResourceReply(IPCCommandBody* cmd, IOS_ERROR result); void IPCSubmitFromCOS(uint32 ppcCoreIndex, IPCCommandBody* cmd); - void Initialize(); + IOSUModule* GetModule(); } } \ No newline at end of file diff --git a/src/Cafe/IOSU/legacy/iosu_act.cpp b/src/Cafe/IOSU/legacy/iosu_act.cpp index e7418e8f..ed3a69bd 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.cpp +++ b/src/Cafe/IOSU/legacy/iosu_act.cpp @@ -192,6 +192,15 @@ namespace iosu return true; } + // returns empty string if invalid + std::string getAccountId2(uint8 slot) + { + sint32 accountIndex = iosuAct_getAccountIndexBySlot(slot); + if (_actAccountData[accountIndex].isValid == false) + return {}; + return {_actAccountData[accountIndex].accountId}; + } + bool getMii(uint8 slot, FFLData_t* fflData) { sint32 accountIndex = iosuAct_getAccountIndexBySlot(slot); diff --git a/src/Cafe/IOSU/legacy/iosu_act.h b/src/Cafe/IOSU/legacy/iosu_act.h index 04dd579f..5336f519 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.h +++ b/src/Cafe/IOSU/legacy/iosu_act.h @@ -3,6 +3,8 @@ void iosuAct_init_depr(); bool iosuAct_isInitialized(); +#define ACT_ACCOUNTID_LENGTH (17) // includes '\0' + // Mii #define MII_FFL_STORAGE_SIZE (96) @@ -48,6 +50,8 @@ namespace iosu bool getScreenname(uint8 slot, uint16 screenname[ACT_NICKNAME_LENGTH]); bool getCountryIndex(uint8 slot, uint32* countryIndex); + std::string getAccountId2(uint8 slot); + const uint8 ACT_SLOT_CURRENT = 0xFE; void Initialize(); diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index 4457d602..75bf0463 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -1,4 +1,3 @@ -#include "iosu_ioctl.h" #include "iosu_act.h" #include "iosu_fpd.h" #include "Cemu/nex/nex.h" @@ -9,12 +8,10 @@ #include "config/ActiveSettings.h" #include "Cemu/napi/napi.h" #include "util/helpers/StringHelpers.h" -#include "Cafe/OS/libs/coreinit/coreinit.h" +#include "Cafe/IOSU/iosu_types_common.h" +#include "Cafe/IOSU/nn/iosu_nn_service.h" -uint32 memory_getVirtualOffsetFromPointer(void* ptr); // remove once we updated everything to MEMPTR - -SysAllocator _fpdAsyncLoginRetCode; -SysAllocator _fpdAsyncAddFriendRetCode; +#include "Common/CafeString.h" std::mutex g_friend_notification_mutex; std::vector< std::pair > g_friend_notifications; @@ -23,78 +20,114 @@ namespace iosu { namespace fpd { + using NotificationRunningId = uint64; + + struct NotificationEntry + { + NotificationEntry(uint64 index, NexFriends::NOTIFICATION_TYPE type, uint32 pid) : timestamp(std::chrono::steady_clock::now()), runningId(index), type(type), pid(pid) {} + std::chrono::steady_clock::time_point timestamp; + NotificationRunningId runningId; + NexFriends::NOTIFICATION_TYPE type; + uint32 pid; + }; + + class + { + public: + void TrackNotification(NexFriends::NOTIFICATION_TYPE type, uint32 pid) + { + std::unique_lock _l(m_mtxNotificationQueue); + m_notificationQueue.emplace_back(m_notificationQueueIndex++, type, pid); + } + + void RemoveExpired() + { + // remove entries older than 10 seconds + std::chrono::steady_clock::time_point expireTime = std::chrono::steady_clock::now() - std::chrono::seconds(10); + std::erase_if(m_notificationQueue, [expireTime](const auto& notification) { + return notification.timestamp < expireTime; + }); + } + + std::optional GetNextNotification(NotificationRunningId& previousRunningId) + { + std::unique_lock _l(m_mtxNotificationQueue); + auto it = std::lower_bound(m_notificationQueue.begin(), m_notificationQueue.end(), previousRunningId, [](const auto& notification, const auto& runningId) { + return notification.runningId <= runningId; + }); + size_t itIndex = it - m_notificationQueue.begin(); + if(it == m_notificationQueue.end()) + return std::nullopt; + previousRunningId = it->runningId; + return *it; + } + + private: + std::vector m_notificationQueue; + std::mutex m_mtxNotificationQueue; + std::atomic_uint64_t m_notificationQueueIndex{1}; + }g_NotificationQueue; struct { bool isThreadStarted; bool isInitialized2; NexFriends* nexFriendSession; - // notification handler - MPTR notificationFunc; - MPTR notificationCustomParam; - uint32 notificationMask; - // login callback - struct - { - MPTR func; - MPTR customParam; - }asyncLoginCallback; + std::mutex mtxFriendSession; + // session state + std::atomic_bool sessionStarted{false}; // current state nexPresenceV2 myPresence; }g_fpd = {}; - void notificationHandler(NexFriends::NOTIFICATION_TYPE type, uint32 pid) + void OverlayNotificationHandler(NexFriends::NOTIFICATION_TYPE type, uint32 pid) { cemuLog_logDebug(LogType::Force, "Friends::Notification {:02x} pid {:08x}", type, pid); - if(GetConfig().notification.friends) + if(!GetConfig().notification.friends) + return; + std::unique_lock lock(g_friend_notification_mutex); + std::string message; + if(type == NexFriends::NOTIFICATION_TYPE::NOTIFICATION_TYPE_ONLINE) { - std::unique_lock lock(g_friend_notification_mutex); - std::string message; - if(type == NexFriends::NOTIFICATION_TYPE::NOTIFICATION_TYPE_ONLINE) + g_friend_notifications.emplace_back("Connected to friend service", 5000); + if(g_fpd.nexFriendSession && g_fpd.nexFriendSession->getPendingFriendRequestCount() > 0) + g_friend_notifications.emplace_back(fmt::format("You have {} pending friend request(s)", g_fpd.nexFriendSession->getPendingFriendRequestCount()), 5000); + } + else + { + std::string msg_format; + switch(type) { - g_friend_notifications.emplace_back("Connected to friend service", 5000); - if(g_fpd.nexFriendSession && g_fpd.nexFriendSession->getPendingFriendRequestCount() > 0) - g_friend_notifications.emplace_back(fmt::format("You have {} pending friend request(s)", g_fpd.nexFriendSession->getPendingFriendRequestCount()), 5000); + case NexFriends::NOTIFICATION_TYPE_ONLINE: break; + case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGIN: msg_format = "{} is now online"; break; + case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGOFF: msg_format = "{} is now offline"; break; + case NexFriends::NOTIFICATION_TYPE_FRIEND_PRESENCE_CHANGE: break; + case NexFriends::NOTIFICATION_TYPE_ADDED_FRIEND: msg_format = "{} has been added to your friend list"; break; + case NexFriends::NOTIFICATION_TYPE_REMOVED_FRIEND: msg_format = "{} has been removed from your friend list"; break; + case NexFriends::NOTIFICATION_TYPE_ADDED_OUTGOING_REQUEST: break; + case NexFriends::NOTIFICATION_TYPE_REMOVED_OUTGOING_REQUEST: break; + case NexFriends::NOTIFICATION_TYPE_ADDED_INCOMING_REQUEST: msg_format = "{} wants to add you to his friend list"; break; + case NexFriends::NOTIFICATION_TYPE_REMOVED_INCOMING_REQUEST: break; + default: ; } - else + if (!msg_format.empty()) { - std::string msg_format; - switch(type) + std::string name = fmt::format("{:#x}", pid); + if (g_fpd.nexFriendSession) { - case NexFriends::NOTIFICATION_TYPE_ONLINE: break; - case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGIN: msg_format = "{} is now online"; break; - case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGOFF: msg_format = "{} is now offline"; break; - case NexFriends::NOTIFICATION_TYPE_FRIEND_PRESENCE_CHANGE: break; - case NexFriends::NOTIFICATION_TYPE_ADDED_FRIEND: msg_format = "{} has been added to your friend list"; break; - case NexFriends::NOTIFICATION_TYPE_REMOVED_FRIEND: msg_format = "{} has been removed from your friend list"; break; - case NexFriends::NOTIFICATION_TYPE_ADDED_OUTGOING_REQUEST: break; - case NexFriends::NOTIFICATION_TYPE_REMOVED_OUTGOING_REQUEST: break; - case NexFriends::NOTIFICATION_TYPE_ADDED_INCOMING_REQUEST: msg_format = "{} wants to add you to his friend list"; break; - case NexFriends::NOTIFICATION_TYPE_REMOVED_INCOMING_REQUEST: break; - default: ; - } - - if (!msg_format.empty()) - { - std::string name = fmt::format("{:#x}", pid); - if (g_fpd.nexFriendSession) - { - const std::string tmp = g_fpd.nexFriendSession->getAccountNameByPid(pid); - if (!tmp.empty()) - name = tmp; - } - - g_friend_notifications.emplace_back(fmt::format(fmt::runtime(msg_format), name), 5000); + const std::string tmp = g_fpd.nexFriendSession->getAccountNameByPid(pid); + if (!tmp.empty()) + name = tmp; } + g_friend_notifications.emplace_back(fmt::format(fmt::runtime(msg_format), name), 5000); } } + } - if (g_fpd.notificationFunc == MPTR_NULL) - return; - uint32 notificationFlag = 1 << (type - 1); - if ( (notificationFlag&g_fpd.notificationMask) == 0 ) - return; - coreinitAsyncCallback_add(g_fpd.notificationFunc, 3, type, pid, g_fpd.notificationCustomParam); + void NotificationHandler(NexFriends::NOTIFICATION_TYPE type, uint32 pid) + { + OverlayNotificationHandler(type, pid); + g_NotificationQueue.TrackNotification(type, pid); } void convertMultiByteStringToBigEndianWidechar(const char* input, uint16be* output, sint32 maxOutputLength) @@ -107,7 +140,7 @@ namespace iosu output[beStr.size()] = '\0'; } - void convertFPDTimestampToDate(uint64 timestamp, fpdDate_t* fpdDate) + void convertFPDTimestampToDate(uint64 timestamp, FPDDate* fpdDate) { // if the timestamp is zero then still return a valid date if (timestamp == 0) @@ -128,7 +161,7 @@ namespace iosu fpdDate->year = (uint16)((timestamp >> 26)); } - uint64 convertDateToFPDTimestamp(fpdDate_t* fpdDate) + uint64 convertDateToFPDTimestamp(FPDDate* fpdDate) { uint64 t = 0; t |= (uint64)fpdDate->second; @@ -140,111 +173,33 @@ namespace iosu return t; } - void startFriendSession() + void NexPresenceToGameMode(nexPresenceV2* presence, GameMode* gameMode) { - cemu_assert(!g_fpd.nexFriendSession); - - NAPI::AuthInfo authInfo; - NAPI::NAPI_MakeAuthInfoFromCurrentAccount(authInfo); - NAPI::ACTGetNexTokenResult nexTokenResult = NAPI::ACT_GetNexToken_WithCache(authInfo, 0x0005001010001C00, 0x0000, 0x00003200); - if (nexTokenResult.isValid()) - { - // get values needed for friend session - uint32 myPid; - uint8 currentSlot = iosu::act::getCurrentAccountSlot(); - iosu::act::getPrincipalId(currentSlot, &myPid); - char accountId[256] = { 0 }; - iosu::act::getAccountId(currentSlot, accountId); - FFLData_t miiData; - act::getMii(currentSlot, &miiData); - uint16 screenName[ACT_NICKNAME_LENGTH + 1] = { 0 }; - act::getScreenname(currentSlot, screenName); - uint32 countryCode = 0; - act::getCountryIndex(currentSlot, &countryCode); - // init presence - g_fpd.myPresence.isOnline = 1; - g_fpd.myPresence.gameKey.titleId = CafeSystem::GetForegroundTitleId(); - g_fpd.myPresence.gameKey.ukn = CafeSystem::GetForegroundTitleVersion(); - - // Resolve potential domain to IP address - struct addrinfo hints = {0}, *addrs; - hints.ai_family = AF_INET; - - const int status = getaddrinfo(nexTokenResult.nexToken.host, NULL, &hints, &addrs); - if (status != 0) { -#if BOOST_OS_WINDOWS - cemuLog_log(LogType::Force, "IOSU_FPD: Failed to resolve hostname {}, {}", nexTokenResult.nexToken.host, gai_strerrorA(status)); -#else - cemuLog_log(LogType::Force, "IOSU_FPD: Failed to resolve hostname {}, {}", nexTokenResult.nexToken.host, gai_strerror(status)); -#endif - return; - } - - char addrstr[NI_MAXHOST]; - getnameinfo(addrs->ai_addr, addrs->ai_addrlen, addrstr, sizeof addrstr, NULL, 0, NI_NUMERICHOST); - cemuLog_log(LogType::Force, "IOSU_FPD: Resolved IP for hostname {}, {}", nexTokenResult.nexToken.host, addrstr); - - // start session - const uint32_t hostIp = ((struct sockaddr_in*)addrs->ai_addr)->sin_addr.s_addr; - freeaddrinfo(addrs); - g_fpd.nexFriendSession = new NexFriends(hostIp, nexTokenResult.nexToken.port, "ridfebb9", myPid, nexTokenResult.nexToken.nexPassword, nexTokenResult.nexToken.token, accountId, (uint8*)&miiData, (wchar_t*)screenName, (uint8)countryCode, g_fpd.myPresence); - g_fpd.nexFriendSession->setNotificationHandler(notificationHandler); - cemuLog_log(LogType::Force, "IOSU_FPD: Created friend server session"); - } - else - { - cemuLog_logDebug(LogType::Force, "IOSU_FPD: Failed to acquire nex token for friend server"); - } + memset(gameMode, 0, sizeof(GameMode)); + gameMode->joinFlagMask = presence->joinFlagMask; + gameMode->matchmakeType = presence->joinAvailability; + gameMode->joinGameId = presence->gameId; + gameMode->joinGameMode = presence->gameMode; + gameMode->hostPid = presence->hostPid; + gameMode->groupId = presence->groupId; + memcpy(gameMode->appSpecificData, presence->appSpecificData, 0x14); } - void handleRequest_GetFriendList(iosuFpdCemuRequest_t* fpdCemuRequest, bool getAll) + void GameModeToNexPresence(GameMode* gameMode, nexPresenceV2* presence) { - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = 0; // zero entries returned - return; - } - - uint32 temporaryPidList[800]; - uint32 pidCount = 0; - g_fpd.nexFriendSession->getFriendPIDs(temporaryPidList, &pidCount, fpdCemuRequest->getFriendList.startIndex, std::min(sizeof(temporaryPidList) / sizeof(temporaryPidList[0]), fpdCemuRequest->getFriendList.maxCount), getAll); - uint32be* pidListOutput = fpdCemuRequest->getFriendList.pidList.GetPtr(); - if (pidListOutput) - { - for (uint32 i = 0; i < pidCount; i++) - pidListOutput[i] = temporaryPidList[i]; - } - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = pidCount; + memset(presence, 0, sizeof(nexPresenceV2)); + presence->joinFlagMask = gameMode->joinFlagMask; + presence->joinAvailability = (uint8)(uint32)gameMode->matchmakeType; + presence->gameId = gameMode->joinGameId; + presence->gameMode = gameMode->joinGameMode; + presence->hostPid = gameMode->hostPid; + presence->groupId = gameMode->groupId; + memcpy(presence->appSpecificData, gameMode->appSpecificData, 0x14); } - void handleRequest_GetFriendRequestList(iosuFpdCemuRequest_t* fpdCemuRequest) + void NexFriendToFPDFriendData(FriendData* friendData, nexFriend* frd) { - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = 0; // zero entries returned - return; - } - - uint32 temporaryPidList[800]; - uint32 pidCount = 0; - // get only incoming friend requests - g_fpd.nexFriendSession->getFriendRequestPIDs(temporaryPidList, &pidCount, fpdCemuRequest->getFriendList.startIndex, std::min(sizeof(temporaryPidList) / sizeof(temporaryPidList[0]), fpdCemuRequest->getFriendList.maxCount), true, false); - uint32be* pidListOutput = fpdCemuRequest->getFriendList.pidList.GetPtr(); - if (pidListOutput) - { - for (uint32 i = 0; i < pidCount; i++) - pidListOutput[i] = temporaryPidList[i]; - } - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = pidCount; - } - - void setFriendDataFromNexFriend(friendData_t* friendData, nexFriend* frd) - { - memset(friendData, 0, sizeof(friendData_t)); + memset(friendData, 0, sizeof(FriendData)); // setup friend data friendData->type = 1; // friend friendData->pid = frd->nnaInfo.principalInfo.principalId; @@ -253,13 +208,11 @@ namespace iosu // screenname convertMultiByteStringToBigEndianWidechar(frd->nnaInfo.principalInfo.mii.miiNickname, friendData->screenname, sizeof(friendData->screenname) / sizeof(uint16be)); - //friendData->friendExtraData.ukn0E4 = 0; friendData->friendExtraData.isOnline = frd->presence.isOnline != 0 ? 1 : 0; - friendData->friendExtraData.gameKeyTitleId = _swapEndianU64(frd->presence.gameKey.titleId); - friendData->friendExtraData.gameKeyUkn = frd->presence.gameKey.ukn; - - friendData->friendExtraData.statusMessage[0] = '\0'; + friendData->friendExtraData.gameKey.titleId = frd->presence.gameKey.titleId; + friendData->friendExtraData.gameKey.ukn08 = frd->presence.gameKey.ukn; + NexPresenceToGameMode(&frd->presence, &friendData->friendExtraData.gameMode); // set valid dates friendData->uknDate.year = 2018; @@ -269,19 +222,19 @@ namespace iosu friendData->uknDate.minute = 1; friendData->uknDate.second = 1; - friendData->friendExtraData.uknDate218.year = 2018; - friendData->friendExtraData.uknDate218.day = 1; - friendData->friendExtraData.uknDate218.month = 1; - friendData->friendExtraData.uknDate218.hour = 1; - friendData->friendExtraData.uknDate218.minute = 1; - friendData->friendExtraData.uknDate218.second = 1; + friendData->friendExtraData.approvalTime.year = 2018; + friendData->friendExtraData.approvalTime.day = 1; + friendData->friendExtraData.approvalTime.month = 1; + friendData->friendExtraData.approvalTime.hour = 1; + friendData->friendExtraData.approvalTime.minute = 1; + friendData->friendExtraData.approvalTime.second = 1; convertFPDTimestampToDate(frd->lastOnlineTimestamp, &friendData->friendExtraData.lastOnline); } - void setFriendDataFromNexFriendRequest(friendData_t* friendData, nexFriendRequest* frdReq, bool isIncoming) + void NexFriendRequestToFPDFriendData(FriendData* friendData, nexFriendRequest* frdReq, bool isIncoming) { - memset(friendData, 0, sizeof(friendData_t)); + memset(friendData, 0, sizeof(FriendData)); // setup friend data friendData->type = 0; // friend request friendData->pid = frdReq->principalInfo.principalId; @@ -292,7 +245,7 @@ namespace iosu convertMultiByteStringToBigEndianWidechar(frdReq->message.commentStr.c_str(), friendData->requestExtraData.comment, sizeof(friendData->requestExtraData.comment) / sizeof(uint16be)); - fpdDate_t expireDate; + FPDDate expireDate; convertFPDTimestampToDate(frdReq->message.expireTimestamp, &expireDate); bool isProvisional = frdReq->message.expireTimestamp == 0; @@ -301,10 +254,7 @@ namespace iosu //friendData->requestExtraData.ukn0A0 = 0; // if not set -> provisional friend request //friendData->requestExtraData.ukn0A4 = isProvisional ? 0 : 123; // no change? - friendData->requestExtraData.messageId = _swapEndianU64(frdReq->message.messageId); - - - //find the value for 'markedAsReceived' + friendData->requestExtraData.messageId = frdReq->message.messageId; ///* +0x0A8 */ uint8 ukn0A8; ///* +0x0A9 */ uint8 ukn0A9; // comment language? (guessed) @@ -332,9 +282,9 @@ namespace iosu convertFPDTimestampToDate(frdReq->message.expireTimestamp, &friendData->requestExtraData.uknData1); } - void setFriendRequestFromNexFriendRequest(friendRequest_t* friendRequest, nexFriendRequest* frdReq, bool isIncoming) + void NexFriendRequestToFPDFriendRequest(FriendRequest* friendRequest, nexFriendRequest* frdReq, bool isIncoming) { - memset(friendRequest, 0, sizeof(friendRequest_t)); + memset(friendRequest, 0, sizeof(FriendRequest)); friendRequest->pid = frdReq->principalInfo.principalId; @@ -355,715 +305,1170 @@ namespace iosu convertFPDTimestampToDate(frdReq->message.expireTimestamp, &friendRequest->expireDate); } - void handleRequest_GetFriendListEx(iosuFpdCemuRequest_t* fpdCemuRequest) + struct FPProfile { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - for (uint32 i = 0; i < fpdCemuRequest->getFriendListEx.count; i++) - { - uint32 pid = fpdCemuRequest->getFriendListEx.pidList[i]; - friendData_t* friendData = fpdCemuRequest->getFriendListEx.friendData.GetPtr() + i; - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - setFriendDataFromNexFriend(friendData, &frd); - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - setFriendDataFromNexFriendRequest(friendData, &frdReq, incoming); - continue; - } - fpdCemuRequest->returnCode = 0x80000000; - return; - } - } + uint8be country; + uint8be area; + uint16be unused; + }; + static_assert(sizeof(FPProfile) == 4); - void handleRequest_GetFriendRequestListEx(iosuFpdCemuRequest_t* fpdCemuRequest) + struct SelfPresence { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) + uint8be ukn[0x130]; // todo + }; + static_assert(sizeof(SelfPresence) == 0x130); + + struct SelfPlayingGame + { + uint8be ukn0[0x10]; + }; + static_assert(sizeof(SelfPlayingGame) == 0x10); + + static const auto FPResult_Ok = 0; + static const auto FPResult_InvalidIPCParam = BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_FP, 0x680); + static const auto FPResult_RequestFailed = BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // figure out proper error code + + class FPDService : public iosu::nn::IPCSimpleService + { + + struct NotificationAsyncRequest { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - for (uint32 i = 0; i < fpdCemuRequest->getFriendRequestListEx.count; i++) - { - uint32 pid = fpdCemuRequest->getFriendListEx.pidList[i]; - friendRequest_t* friendRequest = fpdCemuRequest->getFriendRequestListEx.friendRequest.GetPtr() + i; - nexFriendRequest frdReq; - bool incoming = false; - if (!g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + NotificationAsyncRequest(IPCCommandBody* cmd, uint32 maxNumEntries, FPDNotification* notificationsOut, uint32be* countOut) + : cmd(cmd), maxNumEntries(maxNumEntries), notificationsOut(notificationsOut), countOut(countOut) { - cemuLog_log(LogType::Force, "Failed to get friend request"); - fpdCemuRequest->returnCode = 0x80000000; + } + + IPCCommandBody* cmd; + uint32 maxNumEntries; + FPDNotification* notificationsOut; + uint32be* countOut; + }; + + struct FPDClient + { + bool hasLoggedIn{false}; + uint32 notificationMask{0}; + NotificationRunningId prevRunningId{0}; + std::vector notificationRequests; + }; + + // storage for async IPC requests + std::vector m_asyncLoginRequests; + std::vector m_clients; + + public: + FPDService() : iosu::nn::IPCSimpleService("/dev/fpd") {} + + std::string GetThreadName() override + { + return "IOSUModule::FPD"; + } + + void StartService() override + { + cemu_assert_debug(m_asyncLoginRequests.empty()); + } + + void StopService() override + { + m_asyncLoginRequests.clear(); + for(auto& it : m_clients) + delete it; + m_clients.clear(); + } + + void* CreateClientObject() override + { + FPDClient* client = new FPDClient(); + m_clients.push_back(client); + return client; + } + + void DestroyClientObject(void* clientObject) override + { + FPDClient* client = (FPDClient*)clientObject; + std::erase(m_clients, client); + delete client; + } + + void SendQueuedNotifications(FPDClient* client) + { + if (client->notificationRequests.empty()) return; + if (client->notificationRequests.size() > 1) + cemuLog_log(LogType::Force, "FPD: More than one simultanous notification query not supported"); + NotificationAsyncRequest& request = client->notificationRequests[0]; + uint32 numNotifications = 0; + while(numNotifications < request.maxNumEntries) + { + auto notification = g_NotificationQueue.GetNextNotification(client->prevRunningId); + if (!notification) + break; + uint32 flag = 1 << static_cast(notification->type); + if((client->notificationMask & flag) == 0) + continue; + request.notificationsOut[numNotifications].type = static_cast(notification->type); + request.notificationsOut[numNotifications].pid = notification->pid; + numNotifications++; } - setFriendRequestFromNexFriendRequest(friendRequest, &frdReq, incoming); - } - } - - typedef struct - { - MPTR funcMPTR; - MPTR customParam; - }fpAsyncCallback_t; - - typedef struct - { - nexPrincipalBasicInfo* principalBasicInfo; - uint32* pidList; - sint32 count; - friendBasicInfo_t* friendBasicInfo; - fpAsyncCallback_t fpCallback; - }getBasicInfoAsyncParams_t; - - SysAllocator _fpCallbackResultArray; // use a ring buffer of results to avoid overwriting the result when multiple callbacks are queued at the same time - sint32 fpCallbackResultIndex = 0; - - void handleFPCallback(fpAsyncCallback_t* fpCallback, uint32 resultCode) - { - fpCallbackResultIndex = (fpCallbackResultIndex + 1) % 32; - uint32* resultPtr = _fpCallbackResultArray.GetPtr() + fpCallbackResultIndex; - - *resultPtr = resultCode; - - coreinitAsyncCallback_add(fpCallback->funcMPTR, 2, memory_getVirtualOffsetFromPointer(resultPtr), fpCallback->customParam); - } - - void handleFPCallback2(MPTR funcMPTR, MPTR custom, uint32 resultCode) - { - fpCallbackResultIndex = (fpCallbackResultIndex + 1) % 32; - uint32* resultPtr = _fpCallbackResultArray.GetPtr() + fpCallbackResultIndex; - - *resultPtr = resultCode; - - coreinitAsyncCallback_add(funcMPTR, 2, memory_getVirtualOffsetFromPointer(resultPtr), custom); - } - - void handleResultCB_GetBasicInfoAsync(NexFriends* nexFriends, uint32 result, void* custom) - { - getBasicInfoAsyncParams_t* cbInfo = (getBasicInfoAsyncParams_t*)custom; - if (result != 0) - { - handleFPCallback(&cbInfo->fpCallback, 0x80000000); // todo - properly translate internal error to nn::fp error code - free(cbInfo->principalBasicInfo); - free(cbInfo->pidList); - free(cbInfo); - return; + if (numNotifications == 0) + return; + *request.countOut = numNotifications; + ServiceCallAsyncRespond(request.cmd, FPResult_Ok); + client->notificationRequests.erase(client->notificationRequests.begin()); } - // convert PrincipalBasicInfo into friendBasicInfo - for (sint32 i = 0; i < cbInfo->count; i++) + void TimerUpdate() override { - friendBasicInfo_t* basicInfo = cbInfo->friendBasicInfo + i; - nexPrincipalBasicInfo* principalBasicInfo = cbInfo->principalBasicInfo + i; + // called once a second while service is running + std::unique_lock _l(g_fpd.mtxFriendSession); + if (!g_fpd.nexFriendSession) + return; + g_fpd.nexFriendSession->update(); + while(!m_asyncLoginRequests.empty()) + { + if(g_fpd.nexFriendSession->isOnline()) + { + ServiceCallAsyncRespond(m_asyncLoginRequests.front(), FPResult_Ok); + m_asyncLoginRequests.erase(m_asyncLoginRequests.begin()); + } + else + break; + } + // handle notification responses + g_NotificationQueue.RemoveExpired(); + for(auto& client : m_clients) + SendQueuedNotifications(client); + } - memset(basicInfo, 0, sizeof(friendBasicInfo_t)); - basicInfo->pid = principalBasicInfo->principalId; - strcpy(basicInfo->nnid, principalBasicInfo->nnid); + uint32 ServiceCall(void* clientObject, uint32 requestId, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) override + { + // for /dev/fpd input and output vectors are swapped + std::swap(vecIn, vecOut); + std::swap(numVecIn, numVecOut); - convertMultiByteStringToBigEndianWidechar(principalBasicInfo->mii.miiNickname, basicInfo->screenname, sizeof(basicInfo->screenname) / sizeof(uint16be)); - memcpy(basicInfo->miiData, principalBasicInfo->mii.miiData, FFL_SIZE); + FPDClient* fpdClient = (FPDClient*)clientObject; + switch(static_cast(requestId)) + { + case FPD_REQUEST_ID::SetNotificationMask: + return CallHandler_SetNotificationMask(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetNotificationAsync: + return CallHandler_GetNotificationAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::SetLedEventMask: + cemuLog_logDebug(LogType::Force, "[/dev/fpd] SetLedEventMask is todo"); + return FPResult_Ok; + case FPD_REQUEST_ID::LoginAsync: + return CallHandler_LoginAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::HasLoggedIn: + return CallHandler_HasLoggedIn(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::IsOnline: + return CallHandler_IsOnline(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPrincipalId: + return CallHandler_GetMyPrincipalId(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyAccountId: + return CallHandler_GetMyAccountId(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyScreenName: + return CallHandler_GetMyScreenName(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyMii: + return CallHandler_GetMyMii(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyProfile: + return CallHandler_GetMyProfile(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPresence: + return CallHandler_GetMyPresence(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyComment: + return CallHandler_GetMyComment(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPreference: + return CallHandler_GetMyPreference(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPlayingGame: + return CallHandler_GetMyPlayingGame(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendAccountId: + return CallHandler_GetFriendAccountId(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendScreenName: + return CallHandler_GetFriendScreenName(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendMii: + return CallHandler_GetFriendMii(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendPresence: + return CallHandler_GetFriendPresence(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendRelationship: + return CallHandler_GetFriendRelationship(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendList: + return CallHandler_GetFriendList_GetFriendListAll(fpdClient, vecIn, numVecIn, vecOut, numVecOut, false); + case FPD_REQUEST_ID::GetFriendListAll: + return CallHandler_GetFriendList_GetFriendListAll(fpdClient, vecIn, numVecIn, vecOut, numVecOut, true); + case FPD_REQUEST_ID::GetFriendRequestList: + return CallHandler_GetFriendRequestList(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendRequestListEx: + return CallHandler_GetFriendRequestListEx(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetBlackList: + return CallHandler_GetBlackList(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendListEx: + return CallHandler_GetFriendListEx(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::UpdatePreferenceAsync: + return CallHandler_UpdatePreferenceAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::AddFriendRequestByPlayRecordAsync: + return CallHandler_AddFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::AcceptFriendRequestAsync: + return CallHandler_AcceptFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::DeleteFriendRequestAsync: + return CallHandler_DeleteFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::CancelFriendRequestAsync: + return CallHandler_CancelFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::MarkFriendRequestsAsReceivedAsync: + return CallHandler_MarkFriendRequestsAsReceivedAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::RemoveFriendAsync: + return CallHandler_RemoveFriendAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::DeleteFriendFlagsAsync: + return CallHandler_DeleteFriendFlagsAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetBasicInfoAsync: + return CallHandler_GetBasicInfoAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::CheckSettingStatusAsync: + return CallHandler_CheckSettingStatusAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::IsPreferenceValid: + return CallHandler_IsPreferenceValid(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetRequestBlockSettingAsync: + return CallHandler_GetRequestBlockSettingAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::AddFriendAsyncByPid: + return CallHandler_AddFriendAsyncByPid(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::UpdateGameModeVariation1: + case FPD_REQUEST_ID::UpdateGameModeVariation2: + return CallHandler_UpdateGameMode(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + default: + cemuLog_log(LogType::Force, "Unsupported service call {} to /dev/fpd", requestId); + return BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); + } + } - basicInfo->uknDate90.day = 1; - basicInfo->uknDate90.month = 1; - basicInfo->uknDate90.hour = 1; - basicInfo->uknDate90.minute = 1; - basicInfo->uknDate90.second = 1; + #define DeclareInputPtr(__Name, __T, __count, __vecIndex) if(sizeof(__T)*(__count) != vecIn[__vecIndex].size) { cemuLog_log(LogType::Force, "FPD: IPC buffer has incorrect size"); return FPResult_InvalidIPCParam;}; __T* __Name = ((__T*)vecIn[__vecIndex].basePhys.GetPtr()) + #define DeclareInput(__Name, __T, __vecIndex) if(sizeof(__T) != vecIn[__vecIndex].size) { cemuLog_log(LogType::Force, "FPD: IPC buffer has incorrect size"); return FPResult_InvalidIPCParam;}; __T __Name = *((__T*)vecIn[__vecIndex].basePhys.GetPtr()) + #define DeclareOutputPtr(__Name, __T, __count, __vecIndex) if(sizeof(__T)*(__count) != vecOut[__vecIndex].size) { cemuLog_log(LogType::Force, "FPD: IPC buffer has incorrect size"); return FPResult_InvalidIPCParam;}; __T* __Name = ((__T*)vecOut[__vecIndex].basePhys.GetPtr()) + + template + static nnResult WriteValueOutput(IPCIoctlVector* vec, const T& value) + { + if(vec->size != sizeof(T)) + return FPResult_InvalidIPCParam; + *(T*)vec->basePhys.GetPtr() = value; + return FPResult_Ok; + } + + nnResult CallHandler_SetNotificationMask(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + DeclareInput(notificationMask, uint32be, 0); + fpdClient->notificationMask = notificationMask; + return FPResult_Ok; + } + + nnResult CallHandler_GetNotificationAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 2) + return FPResult_InvalidIPCParam; + if((vecOut[0].size % sizeof(FPDNotification)) != 0 || vecOut[0].size < sizeof(FPDNotification)) + { + cemuLog_log(LogType::Force, "FPD GetNotificationAsync: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + DeclareOutputPtr(countOut, uint32be, 1, 1); + uint32 maxCount = vecOut[0].size / sizeof(FPDNotification); + DeclareOutputPtr(notificationList, FPDNotification, maxCount, 0); + fpdClient->notificationRequests.emplace_back(cmd, maxCount, notificationList, countOut); + SendQueuedNotifications(fpdClient); // if any notifications are queued, send them immediately + return FPResult_Ok; + } + + nnResult CallHandler_LoginAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!ActiveSettings::IsOnlineEnabled()) + { + // not online, fail immediately + return BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // todo + } + StartFriendSession(); + fpdClient->hasLoggedIn = true; + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + m_asyncLoginRequests.emplace_back(cmd); + return FPResult_Ok; + } + + nnResult CallHandler_HasLoggedIn(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + return WriteValueOutput(vecOut, fpdClient->hasLoggedIn ? 1 : 0); + } + + nnResult CallHandler_IsOnline(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + bool isOnline = g_fpd.nexFriendSession ? g_fpd.nexFriendSession->isOnline() : false; + return WriteValueOutput(vecOut, isOnline?1:0); + } + + nnResult CallHandler_GetMyPrincipalId(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + uint32 pid = 0; + iosu::act::getPrincipalId(slot, &pid); + return WriteValueOutput(vecOut, pid); + } + + nnResult CallHandler_GetMyAccountId(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + std::string accountId = iosu::act::getAccountId2(slot); + if(vecOut->size != ACT_ACCOUNTID_LENGTH) + { + cemuLog_log(LogType::Force, "GetMyAccountId: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + if(accountId.length() > ACT_ACCOUNTID_LENGTH-1) + { + cemuLog_log(LogType::Force, "GetMyAccountId: AccountID is too long"); + return FPResult_InvalidIPCParam; + } + if(accountId.empty()) + { + cemuLog_log(LogType::Force, "GetMyAccountId: AccountID is empty"); + return FPResult_InvalidIPCParam; // should return 0xC0C00800 ? + } + char* outputStr = (char*)vecOut->basePhys.GetPtr(); + memset(outputStr, 0, ACT_ACCOUNTID_LENGTH); + memcpy(outputStr, accountId.data(), accountId.length()); + return FPResult_Ok; + } + + nnResult CallHandler_GetMyScreenName(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + if(vecOut->size != ACT_NICKNAME_SIZE*sizeof(uint16be)) + { + cemuLog_log(LogType::Force, "GetMyScreenName: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + uint16 screenname[ACT_NICKNAME_SIZE]{0}; + bool r = iosu::act::getScreenname(slot, screenname); + if (!r) + { + cemuLog_log(LogType::Force, "GetMyScreenName: Screenname is empty"); + return FPResult_InvalidIPCParam; // should return 0xC0C00800 ? + } + uint16be* outputStr = (uint16be*)vecOut->basePhys.GetPtr(); + for(sint32 i = 0; i < ACT_NICKNAME_SIZE; i++) + outputStr[i] = screenname[i]; + return FPResult_Ok; + } + + nnResult CallHandler_GetMyMii(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + if(vecOut->size != FFL_SIZE) + { + cemuLog_log(LogType::Force, "GetMyMii: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + bool r = iosu::act::getMii(slot, (FFLData_t*)vecOut->basePhys.GetPtr()); + if (!r) + { + cemuLog_log(LogType::Force, "GetMyMii: Mii is empty"); + return FPResult_InvalidIPCParam; // should return 0xC0C00800 ? + } + return FPResult_Ok; + } + + nnResult CallHandler_GetMyProfile(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + FPProfile profile{0}; + // todo + cemuLog_log(LogType::Force, "GetMyProfile is todo"); + return WriteValueOutput(vecOut, profile); + } + + nnResult CallHandler_GetMyPresence(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + SelfPresence selfPresence{0}; + cemuLog_log(LogType::Force, "GetMyPresence is todo"); + return WriteValueOutput(vecOut, selfPresence); + } + + nnResult CallHandler_GetMyComment(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + static constexpr uint32 MY_COMMENT_LENGTH = 0x12; // are comments utf16? Buffer length is 0x24 + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + if(vecOut->size != MY_COMMENT_LENGTH*sizeof(uint16be)) + { + cemuLog_log(LogType::Force, "GetMyComment: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + std::basic_string myComment; + myComment.resize(MY_COMMENT_LENGTH); + memcpy(vecOut->basePhys.GetPtr(), myComment.data(), MY_COMMENT_LENGTH*sizeof(uint16be)); + return 0; + } + + nnResult CallHandler_GetMyPreference(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + FPDPreference selfPreference{0}; + if(g_fpd.nexFriendSession) + { + nexPrincipalPreference nexPreference; + g_fpd.nexFriendSession->getMyPreference(nexPreference); + selfPreference.showOnline = nexPreference.showOnline; + selfPreference.showGame = nexPreference.showGame; + selfPreference.blockFriendRequests = nexPreference.blockFriendRequests; + selfPreference.ukn = 0; + } + else + memset(&selfPreference, 0, sizeof(FPDPreference)); + return WriteValueOutput(vecOut, selfPreference); + } + + nnResult CallHandler_GetMyPlayingGame(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + SelfPlayingGame selfPlayingGame{0}; + cemuLog_log(LogType::Force, "GetMyPlayingGame is todo"); + return WriteValueOutput(vecOut, selfPlayingGame); + } + + nnResult CallHandler_GetFriendAccountId(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + // todo - online check + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(accountId, CafeString, count, 0); + memset(accountId, 0, ACT_ACCOUNTID_LENGTH * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + const uint32 pid = pidList[i]; + auto& nnidOutput = accountId[i]; + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + nnidOutput.assign(frd.nnaInfo.principalInfo.nnid); + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + nnidOutput.assign(frdReq.principalInfo.nnid); + continue; + } + cemuLog_log(LogType::Force, "GetFriendAccountId: PID {} not found", pid); + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendScreenName(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + static_assert(sizeof(CafeWideString) == 11*2); + if(numVecIn != 3 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareInput(replaceNonAscii, uint8be, 2); + DeclareOutputPtr(nameList, CafeWideString, count, 0); + uint8be* languageList = nullptr; + if(vecOut[1].size > 0) // languageList is optional + { + DeclareOutputPtr(_languageList, uint8be, count, 1); + languageList = _languageList; + } + memset(nameList, 0, ACT_NICKNAME_SIZE * sizeof(CafeWideString)); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + const uint32 pid = pidList[i]; + CafeWideString& screennameOutput = nameList[i]; + if (languageList) + languageList[i] = 0; // unknown + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + screennameOutput.assignFromUTF8(frd.nnaInfo.principalInfo.mii.miiNickname); + if (languageList) + languageList[i] = frd.nnaInfo.principalInfo.regionGuessed; + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + screennameOutput.assignFromUTF8(frdReq.principalInfo.mii.miiNickname); + if (languageList) + languageList[i] = frdReq.principalInfo.regionGuessed; + continue; + } + cemuLog_log(LogType::Force, "GetFriendScreenName: PID {} not found", pid); + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendMii(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(miiList, FFLData_t, count, 0); + memset(miiList, 0, sizeof(FFLData_t) * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + const uint32 pid = pidList[i]; + FFLData_t& miiOutput = miiList[i]; + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + memcpy(&miiOutput, frd.nnaInfo.principalInfo.mii.miiData, FFL_SIZE); + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + memcpy(&miiOutput, frdReq.principalInfo.mii.miiData, FFL_SIZE); + continue; + } + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendPresence(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(presenceList, FriendPresence, count, 0); + memset(presenceList, 0, sizeof(FriendPresence) * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + FriendPresence& presenceOutput = presenceList[i]; + const uint32 pid = pidList[i]; + nexFriend frd; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + presenceOutput.isOnline = frd.presence.isOnline ? 1 : 0; + presenceOutput.isValid = 1; + // todo - region and subregion + presenceOutput.gameMode.joinFlagMask = frd.presence.joinFlagMask; + presenceOutput.gameMode.matchmakeType = frd.presence.joinAvailability; + presenceOutput.gameMode.joinGameId = frd.presence.gameId; + presenceOutput.gameMode.joinGameMode = frd.presence.gameMode; + presenceOutput.gameMode.hostPid = frd.presence.hostPid; + presenceOutput.gameMode.groupId = frd.presence.groupId; + + memcpy(presenceOutput.gameMode.appSpecificData, frd.presence.appSpecificData, 0x14); + } + else + { + cemuLog_log(LogType::Force, "GetFriendPresence: PID {} not found", pid); + } + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendRelationship(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + // todo - check for valid session (same for all GetFriend* functions) + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(relationshipList, uint8be, count, 0); // correct? + for(uint32 i=0; igetFriendByPID(frd, pid)) + { + relationshipOutput = RELATIONSHIP_FRIEND; + continue; + } + else if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + if (incoming) + relationshipOutput = RELATIONSHIP_FRIENDREQUEST_IN; + else + relationshipOutput = RELATIONSHIP_FRIENDREQUEST_OUT; + } + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendList_GetFriendListAll(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut, bool isAll) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(startIndex, uint32be, 0); + DeclareInput(maxCount, uint32be, 1); + if (maxCount * sizeof(FriendPID) != vecOut[0].size || vecOut[0].basePhys.IsNull()) + { + cemuLog_log(LogType::Force, "GetFriendListAll: pid list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if (!g_fpd.nexFriendSession) + return WriteValueOutput(vecOut+1, 0); + betype* pidList = (betype*)vecOut[0].basePhys.GetPtr(); + std::vector temporaryPidList; + temporaryPidList.resize(std::min(maxCount, 500)); + uint32 pidCount = 0; + g_fpd.nexFriendSession->getFriendPIDs(temporaryPidList.data(), &pidCount, startIndex, temporaryPidList.size(), isAll); + std::copy(temporaryPidList.begin(), temporaryPidList.begin() + pidCount, pidList); + return WriteValueOutput(vecOut+1, pidCount); + } + + nnResult CallHandler_GetFriendRequestList(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(startIndex, uint32be, 0); + DeclareInput(maxCount, uint32be, 1); + if(maxCount * sizeof(FriendPID) != vecOut[0].size || vecOut[0].basePhys.IsNull()) + { + cemuLog_log(LogType::Force, "GetFriendRequestList: pid list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if (!g_fpd.nexFriendSession) + return WriteValueOutput(vecOut+1, 0); + betype* pidList = (betype*)vecOut[0].basePhys.GetPtr(); + std::vector temporaryPidList; + temporaryPidList.resize(std::min(maxCount, 500)); + uint32 pidCount = 0; + g_fpd.nexFriendSession->getFriendRequestPIDs(temporaryPidList.data(), &pidCount, startIndex, temporaryPidList.size(), true, false); + std::copy(temporaryPidList.begin(), temporaryPidList.begin() + pidCount, pidList); + return WriteValueOutput(vecOut+1, pidCount); + } + + nnResult CallHandler_GetFriendRequestListEx(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(friendRequests, FriendRequest, count, 0); + memset(friendRequests, 0, sizeof(FriendRequest) * count); + if (!g_fpd.nexFriendSession) + return FPResult_Ok; + for(uint32 i=0; igetFriendRequestByPID(frdReq, &incoming, pidList[i])) + { + cemuLog_log(LogType::Force, "GetFriendRequestListEx: Failed to get friend request"); + return FPResult_RequestFailed; + } + NexFriendRequestToFPDFriendRequest(friendRequests + i, &frdReq, incoming); + } + return FPResult_Ok; + } + + nnResult CallHandler_GetBlackList(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(startIndex, uint32be, 0); + DeclareInput(maxCount, uint32be, 1); + if(maxCount * sizeof(FriendPID) != vecOut[0].size) + { + cemuLog_log(LogType::Force, "GetBlackList: pid list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if (!g_fpd.nexFriendSession) + return WriteValueOutput(vecOut+1, 0); + betype* pidList = (betype*)vecOut[0].basePhys.GetPtr(); + // todo! + cemuLog_logDebug(LogType::Force, "GetBlackList is todo"); + uint32 countOut = 0; + + return WriteValueOutput(vecOut+1, countOut); + } + + nnResult CallHandler_GetFriendListEx(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, betype, count, 0); + if(count * sizeof(FriendPID) != vecIn[0].size) + { + cemuLog_log(LogType::Force, "GetFriendListEx: pid input list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if(count * sizeof(FriendData) != vecOut[0].size) + { + cemuLog_log(LogType::Force, "GetFriendListEx: Friend output list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + FriendData* friendOutput = (FriendData*)vecOut[0].basePhys.GetPtr(); + memset(friendOutput, 0, sizeof(FriendData) * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + uint32 pid = pidList[i]; + FriendData* friendData = friendOutput + i; + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + NexFriendToFPDFriendData(friendData, &frd); + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + NexFriendRequestToFPDFriendData(friendData, &frdReq, incoming); + continue; + } + cemuLog_logDebug(LogType::Force, "GetFriendListEx: Failed to find friend or request with pid {}", pid); + memset(friendData, 0, sizeof(FriendData)); + } + } + return FPResult_Ok; + } + + static void NexBasicInfoToBasicInfo(const nexPrincipalBasicInfo& nexBasicInfo, FriendBasicInfo& basicInfo) + { + memset(&basicInfo, 0, sizeof(FriendBasicInfo)); + basicInfo.pid = nexBasicInfo.principalId; + strcpy(basicInfo.nnid, nexBasicInfo.nnid); + + convertMultiByteStringToBigEndianWidechar(nexBasicInfo.mii.miiNickname, basicInfo.screenname, sizeof(basicInfo.screenname) / sizeof(uint16be)); + memcpy(basicInfo.miiData, nexBasicInfo.mii.miiData, FFL_SIZE); + + basicInfo.uknDate90.day = 1; + basicInfo.uknDate90.month = 1; + basicInfo.uknDate90.hour = 1; + basicInfo.uknDate90.minute = 1; + basicInfo.uknDate90.second = 1; // unknown values not set: // ukn15 // ukn2E // ukn2F } - // success - handleFPCallback(&cbInfo->fpCallback, 0x00000000); - free(cbInfo->principalBasicInfo); - free(cbInfo->pidList); - free(cbInfo); - } - - void handleRequest_GetBasicInfoAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) + nnResult CallHandler_GetBasicInfoAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - sint32 count = fpdCemuRequest->getBasicInfo.count; - - nexPrincipalBasicInfo* principalBasicInfo = new nexPrincipalBasicInfo[count]; - uint32* pidList = (uint32*)malloc(sizeof(uint32)*count); - for (sint32 i = 0; i < count; i++) - pidList[i] = fpdCemuRequest->getBasicInfo.pidList[i]; - getBasicInfoAsyncParams_t* cbInfo = (getBasicInfoAsyncParams_t*)malloc(sizeof(getBasicInfoAsyncParams_t)); - cbInfo->principalBasicInfo = principalBasicInfo; - cbInfo->pidList = pidList; - cbInfo->count = count; - cbInfo->friendBasicInfo = fpdCemuRequest->getBasicInfo.basicInfo.GetPtr(); - cbInfo->fpCallback.funcMPTR = fpdCemuRequest->getBasicInfo.funcPtr; - cbInfo->fpCallback.customParam = fpdCemuRequest->getBasicInfo.custom; - g_fpd.nexFriendSession->requestPrincipleBaseInfoByPID(principalBasicInfo, pidList, count, handleResultCB_GetBasicInfoAsync, cbInfo); - } - - void handleResponse_addOrRemoveFriend(uint32 errorCode, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - g_fpd.nexFriendSession->requestGetAllInformation(); // refresh list - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_RemoveFriendAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - g_fpd.nexFriendSession->removeFriend(fpdCemuRequest->addOrRemoveFriend.pid, std::bind(handleResponse_addOrRemoveFriend, std::placeholders::_1, fpdCemuRequest->addOrRemoveFriend.funcPtr, fpdCemuRequest->addOrRemoveFriend.custom)); - } - - void handleResponse_MarkFriendRequestAsReceivedAsync(uint32 errorCode, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - handleFPCallback2(funcMPTR, custom, 0); - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_MarkFriendRequestAsReceivedAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - // convert messageId list to little endian - uint64 messageIds[100]; - sint32 count = 0; - for (uint32 i = 0; i < fpdCemuRequest->markFriendRequest.count; i++) - { - uint64 mid = _swapEndianU64(fpdCemuRequest->markFriendRequest.messageIdList.GetPtr()[i]); - if (mid == 0) + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidListBE, betype, count, 0); + DeclareOutputPtr(basicInfoList, FriendBasicInfo, count, 0); + if (!g_fpd.nexFriendSession) { - cemuLog_logDebug(LogType::Force, "MarkFriendRequestAsReceivedAsync - Invalid messageId"); - continue; + memset(basicInfoList, 0, sizeof(FriendBasicInfo) * sizeof(count)); + return FPResult_Ok; } - messageIds[count] = mid; - count++; - if (count >= sizeof(messageIds)/sizeof(messageIds[0])) - break; - } - // skipped for now - g_fpd.nexFriendSession->markFriendRequestsAsReceived(messageIds, count, std::bind(handleResponse_MarkFriendRequestAsReceivedAsync, std::placeholders::_1, fpdCemuRequest->markFriendRequest.funcPtr, fpdCemuRequest->markFriendRequest.custom)); - } - - void handleResponse_cancelMyFriendRequest(uint32 errorCode, uint32 pid, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_CancelFriendRequestAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - // find friend request with matching pid - nexFriendRequest frq; - bool isIncoming; - if (g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, fpdCemuRequest->cancelOrAcceptFriendRequest.messageId)) - { - g_fpd.nexFriendSession->removeFriend(frq.principalInfo.principalId, std::bind(handleResponse_cancelMyFriendRequest, std::placeholders::_1, frq.principalInfo.principalId, fpdCemuRequest->cancelOrAcceptFriendRequest.funcPtr, fpdCemuRequest->cancelOrAcceptFriendRequest.custom)); - } - else - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - } - - void handleResponse_acceptFriendRequest(uint32 errorCode, uint32 pid, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - g_fpd.nexFriendSession->requestGetAllInformation(); // refresh list - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_AcceptFriendRequestAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - // find friend request with matching pid - nexFriendRequest frq; - bool isIncoming; - if (g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, fpdCemuRequest->cancelOrAcceptFriendRequest.messageId)) - { - g_fpd.nexFriendSession->acceptFriendRequest(fpdCemuRequest->cancelOrAcceptFriendRequest.messageId, std::bind(handleResponse_acceptFriendRequest, std::placeholders::_1, frq.principalInfo.principalId, fpdCemuRequest->cancelOrAcceptFriendRequest.funcPtr, fpdCemuRequest->cancelOrAcceptFriendRequest.custom)); - } - else - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - } - - void handleResponse_addFriendRequest(uint32 errorCode, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - g_fpd.nexFriendSession->requestGetAllInformation(); // refresh list - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_AddFriendRequest(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + std::vector pidList; + std::copy(pidListBE, pidListBE + count, std::back_inserter(pidList)); + g_fpd.nexFriendSession->requestPrincipleBaseInfoByPID(pidList.data(), count, [cmd, basicInfoList, count](NexFriends::RpcErrorCode result, std::span basicInfo) -> void { + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + cemu_assert_debug(basicInfo.size() == count); + for(uint32 i = 0; i < count; i++) + NexBasicInfoToBasicInfo(basicInfo[i], basicInfoList[i]); + ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; } - uint16be* input = fpdCemuRequest->addFriendRequest.message.GetPtr(); - size_t inputLen = 0; - while (input[inputLen] != 0) - inputLen++; - std::string msg = StringHelpers::ToUtf8({ input, inputLen }); - - g_fpd.nexFriendSession->addFriendRequest(fpdCemuRequest->addFriendRequest.pid, msg.data(), std::bind(handleResponse_addFriendRequest, std::placeholders::_1, fpdCemuRequest->addFriendRequest.funcPtr, fpdCemuRequest->addFriendRequest.custom)); - } - - // called once a second to handle state checking and updates of the friends service - void iosuFpd_updateFriendsService() - { - if (g_fpd.nexFriendSession) + nnResult CallHandler_UpdatePreferenceAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) { - g_fpd.nexFriendSession->update(); + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInputPtr(newPreference, FPDPreference, 1, 0); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->updatePreferencesAsync(nexPrincipalPreference(newPreference->showOnline != 0 ? 1 : 0, newPreference->showGame != 0 ? 1 : 0, newPreference->blockFriendRequests != 0 ? 1 : 0), [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } - if (g_fpd.asyncLoginCallback.func != MPTR_NULL) + nnResult CallHandler_AddFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 2 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInputPtr(playRecord, RecentPlayRecordEx, 1, 0); + uint32 msgLength = vecIn[1].size/sizeof(uint16be); + DeclareInputPtr(msgBE, uint16be, msgLength, 1); + if(msgLength == 0 || msgBE[msgLength-1] != 0) { - if (g_fpd.nexFriendSession->isOnline()) - { - *_fpdAsyncLoginRetCode.GetPtr() = 0x00000000; - coreinitAsyncCallback_add(g_fpd.asyncLoginCallback.func, 2, _fpdAsyncLoginRetCode.GetMPTR(), g_fpd.asyncLoginCallback.customParam); - g_fpd.asyncLoginCallback.func = MPTR_NULL; - g_fpd.asyncLoginCallback.customParam = MPTR_NULL; - } + cemuLog_log(LogType::Force, "AddFriendRequestAsync: Message must contain at least a null-termination character and end with one"); + return FPResult_InvalidIPCParam; } + std::string msg = StringHelpers::ToUtf8({ msgBE, msgLength-1 }); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->addFriendRequest(playRecord->pid, msg.data(), [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; } - } - void iosuFpd_thread() - { - SetThreadName("iosuFpd_thread"); - while (true) + nnResult CallHandler_AcceptFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) { - uint32 returnValue = 0; // Ioctl return value - ioQueueEntry_t* ioQueueEntry = iosuIoctl_getNextWithTimeout(IOS_DEVICE_FPD, 1000); - if (ioQueueEntry == nullptr) + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(requestId, uint64be, 0); + nexFriendRequest frq; + bool isIncoming; + if (!g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, requestId)) + return FPResult_RequestFailed; + if(!isIncoming) { - iosuFpd_updateFriendsService(); - continue; + cemuLog_log(LogType::Force, "AcceptFriendRequestAsync: Trying to accept outgoing friend request"); + return FPResult_RequestFailed; } - if (ioQueueEntry->request == IOSU_FPD_REQUEST_CEMU) - { - iosuFpdCemuRequest_t* fpdCemuRequest = (iosuFpdCemuRequest_t*)ioQueueEntry->bufferVectors[0].buffer.GetPtr(); - if (fpdCemuRequest->requestCode == IOSU_FPD_INITIALIZE) - { - if (g_fpd.isInitialized2 == false) - { - if(ActiveSettings::IsOnlineEnabled()) - startFriendSession(); - - g_fpd.isInitialized2 = true; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_SET_NOTIFICATION_HANDLER) - { - g_fpd.notificationFunc = fpdCemuRequest->setNotificationHandler.funcPtr; - g_fpd.notificationCustomParam = fpdCemuRequest->setNotificationHandler.custom; - g_fpd.notificationMask = fpdCemuRequest->setNotificationHandler.notificationMask; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_LOGIN_ASYNC) - { - if (g_fpd.nexFriendSession) - { - g_fpd.asyncLoginCallback.func = fpdCemuRequest->loginAsync.funcPtr; - g_fpd.asyncLoginCallback.customParam = fpdCemuRequest->loginAsync.custom; - } - else - { - // offline mode - *_fpdAsyncLoginRetCode.GetPtr() = 0; // if we return 0x80000000 here then Splatoon softlocks during boot - coreinitAsyncCallback_add(fpdCemuRequest->loginAsync.funcPtr, 2, _fpdAsyncLoginRetCode.GetMPTR(), fpdCemuRequest->loginAsync.custom); - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_IS_ONLINE) - { - fpdCemuRequest->resultU32.u32 = g_fpd.nexFriendSession ? g_fpd.nexFriendSession->isOnline() : 0; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_IS_PREFERENCE_VALID) - { - fpdCemuRequest->resultU32.u32 = 1; // todo (if this returns 0, the friend app will show the first-time-setup screen and ask the user to configure preferences) - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_PRINCIPAL_ID) - { - uint8 slot = iosu::act::getCurrentAccountSlot(); - iosu::act::getPrincipalId(slot, &fpdCemuRequest->resultU32.u32); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_ACCOUNT_ID) - { - char* accountId = (char*)fpdCemuRequest->common.ptr.GetPtr(); - uint8 slot = iosu::act::getCurrentAccountSlot(); - if (iosu::act::getAccountId(slot, accountId) == false) - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - else - fpdCemuRequest->returnCode = 0; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_MII) - { - FFLData_t* fflData = (FFLData_t*)fpdCemuRequest->common.ptr.GetPtr(); - uint8 slot = iosu::act::getCurrentAccountSlot(); - if (iosu::act::getMii(slot, fflData) == false) - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - else - fpdCemuRequest->returnCode = 0; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_SCREENNAME) - { - uint16be* screennameOutput = (uint16be*)fpdCemuRequest->common.ptr.GetPtr(); - uint8 slot = iosu::act::getCurrentAccountSlot(); - uint16 screennameTemp[ACT_NICKNAME_LENGTH]; - if (iosu::act::getScreenname(slot, screennameTemp)) - { - for (sint32 i = 0; i < ACT_NICKNAME_LENGTH; i++) - { - screennameOutput[i] = screennameTemp[i]; - } - screennameOutput[ACT_NICKNAME_LENGTH] = '\0'; // length is ACT_NICKNAME_LENGTH+1 - fpdCemuRequest->returnCode = 0; - } - else - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - screennameOutput[0] = '\0'; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_ACCOUNT_ID) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendAccountId.count; i++) - { - char* nnidOutput = fpdCemuRequest->getFriendAccountId.accountIds.GetPtr()+i*17; - uint32 pid = fpdCemuRequest->getFriendAccountId.pidList[i]; - if (g_fpd.nexFriendSession == nullptr) - { - nnidOutput[0] = '\0'; - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - strcpy(nnidOutput, frd.nnaInfo.principalInfo.nnid); - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - strcpy(nnidOutput, frdReq.principalInfo.nnid); - continue; - } - nnidOutput[0] = '\0'; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_SCREENNAME) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendScreenname.count; i++) - { - uint16be* screennameOutput = fpdCemuRequest->getFriendScreenname.nameList.GetPtr()+i*11; - uint32 pid = fpdCemuRequest->getFriendScreenname.pidList[i]; - if(fpdCemuRequest->getFriendScreenname.languageList.IsNull() == false) - fpdCemuRequest->getFriendScreenname.languageList.GetPtr()[i] = 0; - if (g_fpd.nexFriendSession == nullptr) - { - screennameOutput[0] = '\0'; - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - convertMultiByteStringToBigEndianWidechar(frd.nnaInfo.principalInfo.mii.miiNickname, screennameOutput, 11); - if (fpdCemuRequest->getFriendScreenname.languageList.IsNull() == false) - fpdCemuRequest->getFriendScreenname.languageList.GetPtr()[i] = frd.nnaInfo.principalInfo.regionGuessed; - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - convertMultiByteStringToBigEndianWidechar(frdReq.principalInfo.mii.miiNickname, screennameOutput, 11); - if (fpdCemuRequest->getFriendScreenname.languageList.IsNull() == false) - fpdCemuRequest->getFriendScreenname.languageList.GetPtr()[i] = frdReq.principalInfo.regionGuessed; - continue; - } - screennameOutput[0] = '\0'; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_MII) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendMii.count; i++) - { - uint8* miiOutput = fpdCemuRequest->getFriendMii.miiList + i * FFL_SIZE; - uint32 pid = fpdCemuRequest->getFriendMii.pidList[i]; - if (g_fpd.nexFriendSession == nullptr) - { - memset(miiOutput, 0, FFL_SIZE); - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - memcpy(miiOutput, frd.nnaInfo.principalInfo.mii.miiData, FFL_SIZE); - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - memcpy(miiOutput, frdReq.principalInfo.mii.miiData, FFL_SIZE); - continue; - } - memset(miiOutput, 0, FFL_SIZE); - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_PRESENCE) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendPresence.count; i++) - { - friendPresence_t* presenceOutput = (friendPresence_t*)(fpdCemuRequest->getFriendPresence.presenceList + i * sizeof(friendPresence_t)); - memset(presenceOutput, 0, sizeof(friendPresence_t)); - uint32 pid = fpdCemuRequest->getFriendPresence.pidList[i]; - if (g_fpd.nexFriendSession == nullptr) - { - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - presenceOutput->isOnline = frd.presence.isOnline ? 1 : 0; - presenceOutput->isValid = 1; - - presenceOutput->gameMode.joinFlagMask = frd.presence.joinFlagMask; - presenceOutput->gameMode.matchmakeType = frd.presence.joinAvailability; - presenceOutput->gameMode.joinGameId = frd.presence.gameId; - presenceOutput->gameMode.joinGameMode = frd.presence.gameMode; - presenceOutput->gameMode.hostPid = frd.presence.hostPid; - presenceOutput->gameMode.groupId = frd.presence.groupId; - - memcpy(presenceOutput->gameMode.appSpecificData, frd.presence.appSpecificData, 0x14); - - continue; - } - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_RELATIONSHIP) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendRelationship.count; i++) - { - uint8* relationshipOutput = (fpdCemuRequest->getFriendRelationship.relationshipList + i); - uint32 pid = fpdCemuRequest->getFriendRelationship.pidList[i]; - *relationshipOutput = RELATIONSHIP_INVALID; - if (g_fpd.nexFriendSession == nullptr) - { - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - bool incoming; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - *relationshipOutput = RELATIONSHIP_FRIEND; - continue; - } - else if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - if(incoming) - *relationshipOutput = RELATIONSHIP_FRIENDREQUEST_IN; - else - *relationshipOutput = RELATIONSHIP_FRIENDREQUEST_OUT; - } - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_LIST) - { - handleRequest_GetFriendList(fpdCemuRequest, false); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIENDREQUEST_LIST) - { - handleRequest_GetFriendRequestList(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_LIST_ALL) - { - handleRequest_GetFriendList(fpdCemuRequest, true); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_LIST_EX) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - handleRequest_GetFriendListEx(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIENDREQUEST_LIST_EX) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - handleRequest_GetFriendRequestListEx(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_ADD_FRIEND) - { - // todo - figure out how this works - *_fpdAsyncAddFriendRetCode.GetPtr() = 0; - coreinitAsyncCallback_add(fpdCemuRequest->addOrRemoveFriend.funcPtr, 2, _fpdAsyncAddFriendRetCode.GetMPTR(), fpdCemuRequest->addOrRemoveFriend.custom); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_ADD_FRIEND_REQUEST) - { - handleRequest_AddFriendRequest(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_REMOVE_FRIEND_ASYNC) - { - handleRequest_RemoveFriendAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_MARK_FRIEND_REQUEST_AS_RECEIVED_ASYNC) - { - handleRequest_MarkFriendRequestAsReceivedAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_CANCEL_FRIEND_REQUEST_ASYNC) - { - handleRequest_CancelFriendRequestAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_ACCEPT_FRIEND_REQUEST_ASYNC) - { - handleRequest_AcceptFriendRequestAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_BASIC_INFO_ASYNC) - { - handleRequest_GetBasicInfoAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_UPDATE_GAMEMODE) - { - gameMode_t* gameMode = fpdCemuRequest->updateGameMode.gameMode.GetPtr(); - uint16be* gameModeMessage = fpdCemuRequest->updateGameMode.gameModeMessage.GetPtr(); - - g_fpd.myPresence.joinFlagMask = gameMode->joinFlagMask; - - g_fpd.myPresence.joinAvailability = (uint8)(uint32)gameMode->matchmakeType; - g_fpd.myPresence.gameId = gameMode->joinGameId; - g_fpd.myPresence.gameMode = gameMode->joinGameMode; - g_fpd.myPresence.hostPid = gameMode->hostPid; - g_fpd.myPresence.groupId = gameMode->groupId; - memcpy(g_fpd.myPresence.appSpecificData, gameMode->appSpecificData, 0x14); - - if (g_fpd.nexFriendSession) - { - g_fpd.nexFriendSession->updateMyPresence(g_fpd.myPresence); - } - - fpdCemuRequest->returnCode = 0; - } - else - cemu_assert_unimplemented(); - } - else - assert_dbg(); - iosuIoctl_completeRequest(ioQueueEntry, returnValue); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->acceptFriendRequest(requestId, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; } - return; + + nnResult CallHandler_DeleteFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + // reject incoming friend request + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(requestId, uint64be, 0); + nexFriendRequest frq; + bool isIncoming; + if (!g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, requestId)) + return FPResult_RequestFailed; + if(!isIncoming) + { + cemuLog_log(LogType::Force, "CancelFriendRequestAsync: Trying to block outgoing friend request"); + return FPResult_RequestFailed; + } + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->deleteFriendRequest(requestId, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_CancelFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + // retract outgoing friend request + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(requestId, uint64be, 0); + nexFriendRequest frq; + bool isIncoming; + if (!g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, requestId)) + return FPResult_RequestFailed; + if(isIncoming) + { + cemuLog_log(LogType::Force, "CancelFriendRequestAsync: Trying to cancel incoming friend request"); + return FPResult_RequestFailed; + } + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->removeFriend(frq.principalInfo.principalId, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_MarkFriendRequestsAsReceivedAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 2 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(requestIdsBE, uint64be, count, 0); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + // endian convert + std::vector requestIds; + std::copy(requestIdsBE, requestIdsBE + count, std::back_inserter(requestIds)); + g_fpd.nexFriendSession->markFriendRequestsAsReceived(requestIds.data(), requestIds.size(), [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_RemoveFriendAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(pid, uint32be, 0); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->removeFriend(pid, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_DeleteFriendFlagsAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 3 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(pid, uint32be, 0); + cemuLog_logDebug(LogType::Force, "DeleteFriendFlagsAsync is todo"); + return FPResult_Ok; + } + + nnResult CallHandler_CheckSettingStatusAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + if (vecOut[0].size != sizeof(uint8be)) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + + // for now we respond immediately + uint8 settingsStatus = 1; // todo - figure out what this status means + auto r = WriteValueOutput(vecOut, settingsStatus); + ServiceCallAsyncRespond(cmd, r); + cemuLog_log(LogType::Force, "CheckSettingStatusAsync is todo"); + + return FPResult_Ok; + } + + nnResult CallHandler_IsPreferenceValid(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 0 || numVecOut != 1) + return 0; + if (!g_fpd.nexFriendSession) + return 0; + // we currently automatically put the preferences into a valid state on session creation if they are not set yet + return WriteValueOutput(vecOut, 1); // if we return 0, the friend app will show the first time setup screen + } + + nnResult CallHandler_GetRequestBlockSettingAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) // todo + { + if (numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, betype, count, 0); + DeclareOutputPtr(settingList, uint8be, count, 0); + cemuLog_log(LogType::Force, "GetRequestBlockSettingAsync is todo"); + + for (uint32 i = 0; i < count; i++) + settingList[i] = 0; + // implementation is todo. Used by friend list app when adding a friend + // 0 means not blocked. Friend app will continue with GetBasicInformation() + // 1 means blocked. Friend app will continue with AddFriendAsync to add the user as a provisional friend + + return FPResult_Ok; + } + + nnResult CallHandler_AddFriendAsyncByPid(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(pid, uint32be, 0); + cemuLog_log(LogType::Force, "AddFriendAsyncByPid is todo"); + return FPResult_Ok; + } + + nnResult CallHandler_UpdateGameMode(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 2 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInputPtr(gameMode, iosu::fpd::GameMode, 1, 0); + uint32 messageLength = vecIn[1].size / sizeof(uint16be); + if(messageLength == 0 || (vecIn[1].size%sizeof(uint16be)) != 0) + { + cemuLog_log(LogType::Force, "UpdateGameMode: Message must contain at least a null-termination character"); + return FPResult_InvalidIPCParam; + } + DeclareInputPtr(gameModeMessage, uint16be, messageLength, 1); + messageLength--; + GameModeToNexPresence(gameMode, &g_fpd.myPresence); + g_fpd.nexFriendSession->updateMyPresence(g_fpd.myPresence); + // todo - message + return FPResult_Ok; + } + + void StartFriendSession() + { + bool expected = false; + if (!g_fpd.sessionStarted.compare_exchange_strong(expected, true)) + return; + cemu_assert(!g_fpd.nexFriendSession); + NAPI::AuthInfo authInfo; + NAPI::NAPI_MakeAuthInfoFromCurrentAccount(authInfo); + NAPI::ACTGetNexTokenResult nexTokenResult = NAPI::ACT_GetNexToken_WithCache(authInfo, 0x0005001010001C00, 0x0000, 0x00003200); + if (!nexTokenResult.isValid()) + { + cemuLog_logDebug(LogType::Force, "IOSU_FPD: Failed to acquire nex token for friend server"); + g_fpd.myPresence.isOnline = 0; + return; + } + // get values needed for friend session + uint32 myPid; + uint8 currentSlot = iosu::act::getCurrentAccountSlot(); + iosu::act::getPrincipalId(currentSlot, &myPid); + char accountId[256] = { 0 }; + iosu::act::getAccountId(currentSlot, accountId); + FFLData_t miiData; + act::getMii(currentSlot, &miiData); + uint16 screenName[ACT_NICKNAME_LENGTH + 1] = { 0 }; + act::getScreenname(currentSlot, screenName); + uint32 countryCode = 0; + act::getCountryIndex(currentSlot, &countryCode); + // init presence + g_fpd.myPresence.isOnline = 1; + g_fpd.myPresence.gameKey.titleId = CafeSystem::GetForegroundTitleId(); + g_fpd.myPresence.gameKey.ukn = CafeSystem::GetForegroundTitleVersion(); + // resolve potential domain to IP address + struct addrinfo hints = {0}, *addrs; + hints.ai_family = AF_INET; + const int status = getaddrinfo(nexTokenResult.nexToken.host, NULL, &hints, &addrs); + if (status != 0) + { + cemuLog_log(LogType::Force, "IOSU_FPD: Failed to resolve hostname {}", nexTokenResult.nexToken.host); + return; + } + char addrstr[NI_MAXHOST]; + getnameinfo(addrs->ai_addr, addrs->ai_addrlen, addrstr, sizeof addrstr, NULL, 0, NI_NUMERICHOST); + cemuLog_log(LogType::Force, "IOSU_FPD: Resolved IP for hostname {}, {}", nexTokenResult.nexToken.host, addrstr); + // start session + const uint32_t hostIp = ((struct sockaddr_in*)addrs->ai_addr)->sin_addr.s_addr; + freeaddrinfo(addrs); + g_fpd.mtxFriendSession.lock(); + g_fpd.nexFriendSession = new NexFriends(hostIp, nexTokenResult.nexToken.port, "ridfebb9", myPid, nexTokenResult.nexToken.nexPassword, nexTokenResult.nexToken.token, accountId, (uint8*)&miiData, (wchar_t*)screenName, (uint8)countryCode, g_fpd.myPresence); + g_fpd.nexFriendSession->setNotificationHandler(NotificationHandler); + g_fpd.mtxFriendSession.unlock(); + cemuLog_log(LogType::Force, "IOSU_FPD: Created friend server session"); + } + + void StopFriendSession() + { + std::unique_lock _l(g_fpd.mtxFriendSession); + bool expected = true; + if (!g_fpd.sessionStarted.compare_exchange_strong(expected, false) ) + return; + delete g_fpd.nexFriendSession; + g_fpd.nexFriendSession = nullptr; + } + + private: + + + }; + + FPDService gFPDService; + + class : public ::IOSUModule + { + void TitleStart() override + { + gFPDService.Start(); + gFPDService.SetTimerUpdate(1000); // call TimerUpdate() once a second + } + void TitleStop() override + { + gFPDService.StopFriendSession(); + gFPDService.Stop(); + } + }sIOSUModuleNNFPD; + + IOSUModule* GetModule() + { + return static_cast(&sIOSUModuleNNFPD); } - void Initialize() - { - if (g_fpd.isThreadStarted) - return; - std::thread t1(iosuFpd_thread); - t1.detach(); - g_fpd.isThreadStarted = true; - } } } + diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.h b/src/Cafe/IOSU/legacy/iosu_fpd.h index bd52035c..79f524d6 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.h +++ b/src/Cafe/IOSU/legacy/iosu_fpd.h @@ -1,10 +1,12 @@ #pragma once +#include "Cafe/IOSU/iosu_types_common.h" +#include "Common/CafeString.h" namespace iosu { namespace fpd { - typedef struct + struct FPDDate { /* +0x0 */ uint16be year; /* +0x2 */ uint8 month; @@ -13,13 +15,61 @@ namespace iosu /* +0x5 */ uint8 minute; /* +0x6 */ uint8 second; /* +0x7 */ uint8 padding; - }fpdDate_t; + }; - static_assert(sizeof(fpdDate_t) == 8); + static_assert(sizeof(FPDDate) == 8); - typedef struct + struct RecentPlayRecordEx { - /* +0x000 */ uint8 type; // type(Non-Zero -> Friend, 0 -> Friend request ? ) + /* +0x00 */ uint32be pid; + /* +0x04 */ uint8 ukn04; + /* +0x05 */ uint8 ukn05; + /* +0x06 */ uint8 ukn06[0x22]; + /* +0x28 */ uint8 ukn28[0x22]; + /* +0x4A */ uint8 _uknOrPadding4A[6]; + /* +0x50 */ uint32be ukn50; + /* +0x54 */ uint32be ukn54; + /* +0x58 */ uint16be ukn58; + /* +0x5C */ uint8 _padding5C[4]; + /* +0x60 */ iosu::fpd::FPDDate date; + }; + + static_assert(sizeof(RecentPlayRecordEx) == 0x68, ""); + static_assert(offsetof(RecentPlayRecordEx, ukn06) == 0x06, ""); + static_assert(offsetof(RecentPlayRecordEx, ukn50) == 0x50, ""); + + struct GameKey + { + /* +0x00 */ uint64be titleId; + /* +0x08 */ uint16be ukn08; + /* +0x0A */ uint8 _padding0A[6]; + }; + static_assert(sizeof(GameKey) == 0x10); + + struct Profile + { + uint8be region; + uint8be regionSubcode; + uint8be platform; + uint8be ukn3; + }; + static_assert(sizeof(Profile) == 0x4); + + struct GameMode + { + /* +0x00 */ uint32be joinFlagMask; + /* +0x04 */ uint32be matchmakeType; + /* +0x08 */ uint32be joinGameId; + /* +0x0C */ uint32be joinGameMode; + /* +0x10 */ uint32be hostPid; + /* +0x14 */ uint32be groupId; + /* +0x18 */ uint8 appSpecificData[0x14]; + }; + static_assert(sizeof(GameMode) == 0x2C); + + struct FriendData + { + /* +0x000 */ uint8 type; // type (Non-Zero -> Friend, 0 -> Friend request ? ) /* +0x001 */ uint8 _padding1[7]; /* +0x008 */ uint32be pid; /* +0x00C */ char nnid[0x10 + 1]; @@ -29,43 +79,29 @@ namespace iosu /* +0x036 */ uint8 ukn036; /* +0x037 */ uint8 _padding037; /* +0x038 */ uint8 mii[0x60]; - /* +0x098 */ fpdDate_t uknDate; + /* +0x098 */ FPDDate uknDate; // sub struct (the part above seems to be shared with friend requests) union { struct { - /* +0x0A0 */ uint8 ukn0A0; // country code? - /* +0x0A1 */ uint8 ukn0A1; // country subcode? - /* +0x0A2 */ uint8 _paddingA2[2]; + /* +0x0A0 */ Profile profile; // this is returned for nn_fp.GetFriendProfile /* +0x0A4 */ uint32be ukn0A4; - /* +0x0A8 */ uint64 gameKeyTitleId; - /* +0x0B0 */ uint16be gameKeyUkn; - /* +0x0B2 */ uint8 _paddingB2[6]; - /* +0x0B8 */ uint32 ukn0B8; - /* +0x0BC */ uint32 ukn0BC; - /* +0x0C0 */ uint32 ukn0C0; - /* +0x0C4 */ uint32 ukn0C4; - /* +0x0C8 */ uint32 ukn0C8; - /* +0x0CC */ uint32 ukn0CC; - /* +0x0D0 */ uint8 appSpecificData[0x14]; - /* +0x0E4 */ uint8 ukn0E4; - /* +0x0E5 */ uint8 _paddingE5; - /* +0x0E6 */ uint16 uknStr[0x80]; // game mode description (could be larger) - /* +0x1E6 */ uint8 _padding1E6[0x1EC - 0x1E6]; + /* +0x0A8 */ GameKey gameKey; + /* +0x0B8 */ GameMode gameMode; + /* +0x0E4 */ CafeWideString<0x82> gameModeDescription; + /* +0x1E8 */ Profile profile1E8; // how does it differ from the one at 0xA0? Returned by GetFriendPresence /* +0x1EC */ uint8 isOnline; /* +0x1ED */ uint8 _padding1ED[3]; // some other sub struct? - /* +0x1F0 */ uint8 ukn1F0; - /* +0x1F1 */ uint8 _padding1F1; - /* +0x1F2 */ uint16be statusMessage[16 + 1]; // pops up every few seconds in friend list (ingame character name?) - /* +0x214 */ uint8 _padding214[4]; - /* +0x218 */ fpdDate_t uknDate218; - /* +0x220 */ fpdDate_t lastOnline; + /* +0x1F0 */ char comment[36]; // pops up every few seconds in friend list + /* +0x214 */ uint32be _padding214; + /* +0x218 */ FPDDate approvalTime; + /* +0x220 */ FPDDate lastOnline; }friendExtraData; struct { - /* +0x0A0 */ uint64 messageId; // guessed + /* +0x0A0 */ uint64be messageId; // guessed. If 0, then relationship is FRIENDSHIP_REQUEST_OUT, otherwise FRIENDSHIP_REQUEST_IN /* +0x0A8 */ uint8 ukn0A8; /* +0x0A9 */ uint8 ukn0A9; // comment language? (guessed) /* +0x0AA */ uint16be comment[0x40]; @@ -75,26 +111,23 @@ namespace iosu /* +0x150 */ uint64 gameKeyTitleId; /* +0x158 */ uint16be gameKeyUkn; /* +0x15A */ uint8 _padding[6]; - /* +0x160 */ fpdDate_t uknData0; - /* +0x168 */ fpdDate_t uknData1; + /* +0x160 */ FPDDate uknData0; + /* +0x168 */ FPDDate uknData1; }requestExtraData; }; - }friendData_t; - - static_assert(sizeof(friendData_t) == 0x228, ""); - static_assert(offsetof(friendData_t, nnid) == 0x00C, ""); - static_assert(offsetof(friendData_t, friendExtraData.gameKeyTitleId) == 0x0A8, ""); - static_assert(offsetof(friendData_t, friendExtraData.appSpecificData) == 0x0D0, ""); - static_assert(offsetof(friendData_t, friendExtraData.uknStr) == 0x0E6, ""); - static_assert(offsetof(friendData_t, friendExtraData.ukn1F0) == 0x1F0, ""); + }; + static_assert(sizeof(FriendData) == 0x228); + static_assert(offsetof(FriendData, friendExtraData.gameKey) == 0x0A8); + static_assert(offsetof(FriendData, friendExtraData.gameModeDescription) == 0x0E4); + static_assert(offsetof(FriendData, friendExtraData.comment) == 0x1F0); - static_assert(offsetof(friendData_t, requestExtraData.messageId) == 0x0A0, ""); - static_assert(offsetof(friendData_t, requestExtraData.comment) == 0x0AA, ""); - static_assert(offsetof(friendData_t, requestExtraData.uknMessage) == 0x12C, ""); - static_assert(offsetof(friendData_t, requestExtraData.gameKeyTitleId) == 0x150, ""); - static_assert(offsetof(friendData_t, requestExtraData.uknData1) == 0x168, ""); + static_assert(offsetof(FriendData, requestExtraData.messageId) == 0x0A0); + static_assert(offsetof(FriendData, requestExtraData.comment) == 0x0AA); + static_assert(offsetof(FriendData, requestExtraData.uknMessage) == 0x12C); + static_assert(offsetof(FriendData, requestExtraData.gameKeyTitleId) == 0x150); + static_assert(offsetof(FriendData, requestExtraData.uknData1) == 0x168); - typedef struct + struct FriendBasicInfo { /* +0x00 */ uint32be pid; /* +0x04 */ char nnid[0x11]; @@ -104,17 +137,17 @@ namespace iosu /* +0x2E */ uint8 ukn2E; // bool option /* +0x2F */ uint8 ukn2F; /* +0x30 */ uint8 miiData[0x60]; - /* +0x90 */ fpdDate_t uknDate90; - }friendBasicInfo_t; // size is 0x98 + /* +0x90 */ FPDDate uknDate90; + }; - static_assert(sizeof(friendBasicInfo_t) == 0x98, ""); - static_assert(offsetof(friendBasicInfo_t, nnid) == 0x04, ""); - static_assert(offsetof(friendBasicInfo_t, ukn15) == 0x15, ""); - static_assert(offsetof(friendBasicInfo_t, screenname) == 0x18, ""); - static_assert(offsetof(friendBasicInfo_t, ukn2E) == 0x2E, ""); - static_assert(offsetof(friendBasicInfo_t, miiData) == 0x30, ""); + static_assert(sizeof(FriendBasicInfo) == 0x98); + static_assert(offsetof(FriendBasicInfo, nnid) == 0x04); + static_assert(offsetof(FriendBasicInfo, ukn15) == 0x15); + static_assert(offsetof(FriendBasicInfo, screenname) == 0x18); + static_assert(offsetof(FriendBasicInfo, ukn2E) == 0x2E); + static_assert(offsetof(FriendBasicInfo, miiData) == 0x30); - typedef struct + struct FriendRequest { /* +0x000 */ uint32be pid; /* +0x004 */ uint8 nnid[17]; // guessed type @@ -125,7 +158,7 @@ namespace iosu /* +0x02E */ uint8 ukn2E; // bool option /* +0x02F */ uint8 ukn2F; // ukn /* +0x030 */ uint8 miiData[0x60]; - /* +0x090 */ fpdDate_t uknDate; + /* +0x090 */ FPDDate uknDate; /* +0x098 */ uint64 ukn98; /* +0x0A0 */ uint8 isMarkedAsReceived; /* +0x0A1 */ uint8 uknA1; @@ -136,216 +169,98 @@ namespace iosu /* +0x148 */ uint64 gameKeyTitleId; /* +0x150 */ uint16be gameKeyUkn; /* +0x152 */ uint8 _padding152[6]; - /* +0x158 */ fpdDate_t uknDate2; - /* +0x160 */ fpdDate_t expireDate; - }friendRequest_t; + /* +0x158 */ FPDDate uknDate2; + /* +0x160 */ FPDDate expireDate; + }; - static_assert(sizeof(friendRequest_t) == 0x168, ""); - static_assert(offsetof(friendRequest_t, uknDate) == 0x090, ""); - static_assert(offsetof(friendRequest_t, message) == 0x0A2, ""); - static_assert(offsetof(friendRequest_t, uknString2) == 0x124, ""); - static_assert(offsetof(friendRequest_t, gameKeyTitleId) == 0x148, ""); + static_assert(sizeof(FriendRequest) == 0x168); + static_assert(offsetof(FriendRequest, uknDate) == 0x090); + static_assert(offsetof(FriendRequest, message) == 0x0A2); + static_assert(offsetof(FriendRequest, uknString2) == 0x124); + static_assert(offsetof(FriendRequest, gameKeyTitleId) == 0x148); - typedef struct + struct FriendPresence { - /* +0x00 */ uint32be joinFlagMask; - /* +0x04 */ uint32be matchmakeType; - /* +0x08 */ uint32be joinGameId; - /* +0x0C */ uint32be joinGameMode; - /* +0x10 */ uint32be hostPid; - /* +0x14 */ uint32be groupId; - /* +0x18 */ uint8 appSpecificData[0x14]; - }gameMode_t; - - static_assert(sizeof(gameMode_t) == 0x2C, ""); - - typedef struct - { - gameMode_t gameMode; - /* +0x2C */ uint8 region; - /* +0x2D */ uint8 regionSubcode; - /* +0x2E */ uint8 platform; - /* +0x2F */ uint8 _padding2F; + GameMode gameMode; + /* +0x2C */ Profile profile; /* +0x30 */ uint8 isOnline; /* +0x31 */ uint8 isValid; /* +0x32 */ uint8 padding[2]; // guessed - }friendPresence_t; + }; + static_assert(sizeof(FriendPresence) == 0x34); + static_assert(offsetof(FriendPresence, isOnline) == 0x30); - static_assert(sizeof(friendPresence_t) == 0x34, ""); - static_assert(offsetof(friendPresence_t, region) == 0x2C, ""); - static_assert(offsetof(friendPresence_t, isOnline) == 0x30, ""); + struct FPDNotification + { + betype type; + betype pid; + }; + static_assert(sizeof(FPDNotification) == 8); + + struct FPDPreference + { + uint8be showOnline; // show online status to others + uint8be showGame; // show played game to others + uint8be blockFriendRequests; // block friend requests + uint8be ukn; // probably padding? + }; + static_assert(sizeof(FPDPreference) == 4); static const int RELATIONSHIP_INVALID = 0; static const int RELATIONSHIP_FRIENDREQUEST_OUT = 1; static const int RELATIONSHIP_FRIENDREQUEST_IN = 2; static const int RELATIONSHIP_FRIEND = 3; - typedef struct + static const int GAMEMODE_MAX_MESSAGE_LENGTH = 0x80; // limit includes null-terminator character, so only 0x7F actual characters can be used + + enum class FPD_REQUEST_ID { - uint32 requestCode; - union - { - struct - { - MEMPTR ptr; - }common; - struct - { - MPTR funcPtr; - MPTR custom; - }loginAsync; - struct - { - MEMPTR pidList; - uint32 startIndex; - uint32 maxCount; - }getFriendList; - struct - { - MEMPTR friendData; - MEMPTR pidList; - uint32 count; - }getFriendListEx; - struct - { - MEMPTR friendRequest; - MEMPTR pidList; - uint32 count; - }getFriendRequestListEx; - struct - { - uint32 pid; - MPTR funcPtr; - MPTR custom; - }addOrRemoveFriend; - struct - { - uint64 messageId; - MPTR funcPtr; - MPTR custom; - }cancelOrAcceptFriendRequest; - struct - { - uint32 pid; - MEMPTR message; - MPTR funcPtr; - MPTR custom; - }addFriendRequest; - struct - { - MEMPTR messageIdList; - uint32 count; - MPTR funcPtr; - MPTR custom; - }markFriendRequest; - struct - { - MEMPTR basicInfo; - MEMPTR pidList; - sint32 count; - MPTR funcPtr; - MPTR custom; - }getBasicInfo; - struct - { - uint32 notificationMask; - MPTR funcPtr; - MPTR custom; - }setNotificationHandler; - struct - { - MEMPTR accountIds; - MEMPTR pidList; - sint32 count; - }getFriendAccountId; - struct - { - MEMPTR nameList; - MEMPTR pidList; - sint32 count; - bool replaceNonAscii; - MEMPTR languageList; - }getFriendScreenname; - struct - { - uint8* miiList; - MEMPTR pidList; - sint32 count; - }getFriendMii; - struct - { - uint8* presenceList; - MEMPTR pidList; - sint32 count; - }getFriendPresence; - struct - { - uint8* relationshipList; - MEMPTR pidList; - sint32 count; - }getFriendRelationship; - struct - { - MEMPTR gameMode; - MEMPTR gameModeMessage; - }updateGameMode; - }; - - // output - uint32 returnCode; // return value - union - { - struct - { - uint32 numReturnedCount; - }resultGetFriendList; - struct - { - uint32 u32; - }resultU32; - }; - }iosuFpdCemuRequest_t; - - // custom dev/fpd protocol (Cemu only) - #define IOSU_FPD_REQUEST_CEMU (0xEE) - - // FPD request Cemu subcodes - enum - { - _IOSU_FPD_NONE, - IOSU_FPD_INITIALIZE, - IOSU_FPD_SET_NOTIFICATION_HANDLER, - IOSU_FPD_LOGIN_ASYNC, - IOSU_FPD_IS_ONLINE, - IOSU_FPD_IS_PREFERENCE_VALID, - - IOSU_FPD_UPDATE_GAMEMODE, - - IOSU_FPD_GET_MY_PRINCIPAL_ID, - IOSU_FPD_GET_MY_ACCOUNT_ID, - IOSU_FPD_GET_MY_MII, - IOSU_FPD_GET_MY_SCREENNAME, - - IOSU_FPD_GET_FRIEND_ACCOUNT_ID, - IOSU_FPD_GET_FRIEND_SCREENNAME, - IOSU_FPD_GET_FRIEND_MII, - IOSU_FPD_GET_FRIEND_PRESENCE, - IOSU_FPD_GET_FRIEND_RELATIONSHIP, - - IOSU_FPD_GET_FRIEND_LIST, - IOSU_FPD_GET_FRIENDREQUEST_LIST, - IOSU_FPD_GET_FRIEND_LIST_ALL, - IOSU_FPD_GET_FRIEND_LIST_EX, - IOSU_FPD_GET_FRIENDREQUEST_LIST_EX, - IOSU_FPD_ADD_FRIEND, - IOSU_FPD_ADD_FRIEND_REQUEST, - IOSU_FPD_REMOVE_FRIEND_ASYNC, - IOSU_FPD_CANCEL_FRIEND_REQUEST_ASYNC, - IOSU_FPD_ACCEPT_FRIEND_REQUEST_ASYNC, - IOSU_FPD_MARK_FRIEND_REQUEST_AS_RECEIVED_ASYNC, - IOSU_FPD_GET_BASIC_INFO_ASYNC, + LoginAsync = 0x2775, + HasLoggedIn = 0x2777, + IsOnline = 0x2778, + GetMyPrincipalId = 0x27D9, + GetMyAccountId = 0x27DA, + GetMyScreenName = 0x27DB, + GetMyMii = 0x27DC, + GetMyProfile = 0x27DD, + GetMyPreference = 0x27DE, + GetMyPresence = 0x27DF, + IsPreferenceValid = 0x27E0, + GetFriendList = 0x283D, + GetFriendListAll = 0x283E, + GetFriendAccountId = 0x283F, + GetFriendScreenName = 0x2840, + GetFriendPresence = 0x2845, + GetFriendRelationship = 0x2846, + GetFriendMii = 0x2841, + GetBlackList = 0x28A1, + GetFriendRequestList = 0x2905, + UpdateGameModeVariation1 = 0x2969, // there seem to be two different requestIds for the 2-param and 3-param version of UpdateGameMode, + UpdateGameModeVariation2 = 0x296A, // but the third parameter is never used and the same handler is used for both + AddFriendAsyncByPid = 0x29CD, + AddFriendAsyncByXXX = 0x29CE, // probably by name? + GetRequestBlockSettingAsync = 0x2B5D, + GetMyComment = 0x4EE9, + GetMyPlayingGame = 0x4EEA, + CheckSettingStatusAsync = 0x7596, + GetFriendListEx = 0x75F9, + GetFriendRequestListEx = 0x76C1, + UpdatePreferenceAsync = 0x7727, + RemoveFriendAsync = 0x7789, + DeleteFriendFlagsAsync = 0x778A, + AddFriendRequestByPlayRecordAsync = 0x778B, + CancelFriendRequestAsync = 0x778C, + AcceptFriendRequestAsync = 0x7851, + DeleteFriendRequestAsync = 0x7852, + MarkFriendRequestsAsReceivedAsync = 0x7854, + GetBasicInfoAsync = 0x7919, + SetLedEventMask = 0x9D0B, + SetNotificationMask = 0x15ff5, + GetNotificationAsync = 0x15FF6, }; - void Initialize(); + using FriendPID = uint32; + + IOSUModule* GetModule(); } } \ No newline at end of file diff --git a/src/Cafe/IOSU/nn/iosu_nn_service.cpp b/src/Cafe/IOSU/nn/iosu_nn_service.cpp index ade1fa2b..b3b2d4c9 100644 --- a/src/Cafe/IOSU/nn/iosu_nn_service.cpp +++ b/src/Cafe/IOSU/nn/iosu_nn_service.cpp @@ -1,5 +1,6 @@ #include "iosu_nn_service.h" #include "../kernel/iosu_kernel.h" +#include "util/helpers/helpers.h" using namespace iosu::kernel; @@ -7,6 +8,132 @@ namespace iosu { namespace nn { + /* IPCSimpleService */ + void IPCSimpleService::Start() + { + if (m_isRunning.exchange(true)) + return; + m_threadInitialized = false; + m_requestStop = false; + m_serviceThread = std::thread(&IPCSimpleService::ServiceThread, this); + while (!m_threadInitialized) std::this_thread::sleep_for(std::chrono::milliseconds(10)); + StartService(); + } + + void IPCSimpleService::Stop() + { + if (!m_isRunning.exchange(false)) + return; + m_requestStop = true; + StopService(); + if(m_timerId != IOSInvalidTimerId) + IOS_DestroyTimer(m_timerId); + m_timerId = IOSInvalidTimerId; + IOS_SendMessage(m_msgQueueId, 0, 0); // wake up thread + m_serviceThread.join(); + } + + void IPCSimpleService::ServiceThread() + { + if(!GetThreadName().empty()) + SetThreadName(GetThreadName().c_str()); + m_msgQueueId = IOS_CreateMessageQueue(_m_msgBuffer.GetPtr(), _m_msgBuffer.GetCount()); + cemu_assert(!IOS_ResultIsError((IOS_ERROR)m_msgQueueId)); + IOS_ERROR r = IOS_RegisterResourceManager(m_devicePath.c_str(), m_msgQueueId); + cemu_assert(!IOS_ResultIsError(r)); + m_threadInitialized = true; + while (true) + { + IOSMessage msg; + r = IOS_ReceiveMessage(m_msgQueueId, &msg, 0); + cemu_assert(!IOS_ResultIsError(r)); + if (msg == 0) + { + cemu_assert_debug(m_requestStop); + break; + } + else if(msg == 1) + { + TimerUpdate(); + continue; + } + IPCCommandBody* cmd = MEMPTR(msg).GetPtr(); + if (cmd->cmdId == IPCCommandId::IOS_OPEN) + { + void* clientObject = CreateClientObject(); + if(clientObject == nullptr) + { + cemuLog_log(LogType::Force, "IPCSimpleService[{}]: Maximum handle count reached or handle rejected", m_devicePath); + IOS_ResourceReply(cmd, IOS_ERROR_MAXIMUM_REACHED); + continue; + } + IOSDevHandle newHandle = GetFreeHandle(); + m_clientObjects[newHandle] = clientObject; + IOS_ResourceReply(cmd, (IOS_ERROR)newHandle); + continue; + } + else if (cmd->cmdId == IPCCommandId::IOS_CLOSE) + { + void* clientObject = GetClientObjectByHandle(cmd->devHandle); + if (clientObject) + DestroyClientObject(clientObject); + IOS_ResourceReply(cmd, IOS_ERROR_OK); + continue; + } + else if (cmd->cmdId == IPCCommandId::IOS_IOCTLV) + { + void* clientObject = GetClientObjectByHandle(cmd->devHandle); + if (!clientObject) + { + cemuLog_log(LogType::Force, "IPCSimpleService[{}]: Invalid IPC handle", m_devicePath); + IOS_ResourceReply(cmd, IOS_ERROR_INVALID); + continue; + } + uint32 requestId = cmd->args[0]; + uint32 numIn = cmd->args[1]; + uint32 numOut = cmd->args[2]; + IPCIoctlVector* vec = MEMPTR{ cmd->args[3] }.GetPtr(); + IPCIoctlVector* vecIn = vec + 0; // the ordering of vecIn/vecOut differs from IPCService + IPCIoctlVector* vecOut = vec + numIn; + m_delayResponse = false; + m_activeCmd = cmd; + uint32 result = ServiceCall(clientObject, requestId, vecIn, numIn, vecOut, numOut); + if (!m_delayResponse) + IOS_ResourceReply(cmd, (IOS_ERROR)result); + m_activeCmd = nullptr; + continue; + } + else + { + cemuLog_log(LogType::Force, "IPCSimpleService[{}]: Unsupported IPC cmdId {}", m_devicePath, (uint32)cmd->cmdId.value()); + cemu_assert_unimplemented(); + IOS_ResourceReply(cmd, IOS_ERROR_INVALID); + } + } + IOS_DestroyMessageQueue(m_msgQueueId); + m_threadInitialized = false; + } + + void IPCSimpleService::SetTimerUpdate(uint32 milliseconds) + { + if(m_timerId != IOSInvalidTimerId) + IOS_DestroyTimer(m_timerId); + m_timerId = IOS_CreateTimer(milliseconds * 1000, milliseconds * 1000, m_msgQueueId, 1); + } + + IPCCommandBody* IPCSimpleService::ServiceCallDelayCurrentResponse() + { + cemu_assert_debug(m_activeCmd); + m_delayResponse = true; + return m_activeCmd; + } + + void IPCSimpleService::ServiceCallAsyncRespond(IPCCommandBody* response, uint32 r) + { + IOS_ResourceReply(response, (IOS_ERROR)r); + } + + /* IPCService */ void IPCService::Start() { if (m_isRunning.exchange(true)) @@ -83,4 +210,4 @@ namespace iosu m_threadInitialized = false; } }; -}; \ No newline at end of file +}; diff --git a/src/Cafe/IOSU/nn/iosu_nn_service.h b/src/Cafe/IOSU/nn/iosu_nn_service.h index 7f06139f..d50a0794 100644 --- a/src/Cafe/IOSU/nn/iosu_nn_service.h +++ b/src/Cafe/IOSU/nn/iosu_nn_service.h @@ -8,6 +8,71 @@ namespace iosu { namespace nn { + // a simple service interface which wraps handle management and Ioctlv/IoctlvAsync + class IPCSimpleService + { + public: + IPCSimpleService(std::string_view devicePath) : m_devicePath(devicePath) {}; + virtual ~IPCSimpleService() {}; + + virtual void StartService() {}; + virtual void StopService() {}; + + virtual std::string GetThreadName() = 0; + + virtual void* CreateClientObject() = 0; + virtual void DestroyClientObject(void* clientObject) = 0; + virtual uint32 ServiceCall(void* clientObject, uint32 requestId, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) = 0; + virtual void TimerUpdate() {}; + + IPCCommandBody* ServiceCallDelayCurrentResponse(); + static void ServiceCallAsyncRespond(IPCCommandBody* response, uint32 r); + + void Start(); + void Stop(); + + void SetTimerUpdate(uint32 milliseconds); + + private: + void ServiceThread(); + + IOSDevHandle GetFreeHandle() + { + while(m_clientObjects.find(m_nextHandle) != m_clientObjects.end() || m_nextHandle == 0) + { + m_nextHandle++; + m_nextHandle &= 0x7FFFFFFF; + } + IOSDevHandle newHandle = m_nextHandle; + m_nextHandle++; + m_nextHandle &= 0x7FFFFFFF; + return newHandle; + } + + void* GetClientObjectByHandle(IOSDevHandle handle) const + { + auto it = m_clientObjects.find(handle); + if(it == m_clientObjects.end()) + return nullptr; + return it->second; + } + + std::string m_devicePath; + std::thread m_serviceThread; + std::atomic_bool m_requestStop{false}; + std::atomic_bool m_isRunning{false}; + std::atomic_bool m_threadInitialized{ false }; + std::unordered_map m_clientObjects; + IOSDevHandle m_nextHandle{1}; + IOSTimerId m_timerId{IOSInvalidTimerId}; + + IPCCommandBody* m_activeCmd{nullptr}; + bool m_delayResponse{false}; + + IOSMsgQueueId m_msgQueueId; + SysAllocator _m_msgBuffer; + }; + struct IPCServiceRequest { uint32be ukn00; @@ -23,6 +88,7 @@ namespace iosu uint32be nnResultCode; }; + // a complex service interface which wraps Ioctlv and adds an additional service channel, used by /dev/act, ? class IPCService { public: @@ -60,5 +126,6 @@ namespace iosu IOSMsgQueueId m_msgQueueId; SysAllocator _m_msgBuffer; }; + }; }; \ No newline at end of file diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index 0e6d153f..f0703290 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -724,7 +724,7 @@ uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(PPCInterpreter_t* hCPU if (it != g_map_callableExports.end()) return it->second; // get HLE function index - sint32 functionIndex = PPCInterpreter_registerHLECall(ppcCallableExport); + sint32 functionIndex = PPCInterpreter_registerHLECall(ppcCallableExport, fmt::format("PPCCallback{:x}", (uintptr_t)ppcCallableExport)); MPTR codeAddr = memory_getVirtualOffsetFromPointer(RPLLoader_AllocateTrampolineCodeSpace(4)); uint32 opcode = (1 << 26) | functionIndex; memory_write(codeAddr, opcode); diff --git a/src/Cafe/OS/common/OSCommon.cpp b/src/Cafe/OS/common/OSCommon.cpp index 7e11ea13..5aedd197 100644 --- a/src/Cafe/OS/common/OSCommon.cpp +++ b/src/Cafe/OS/common/OSCommon.cpp @@ -85,6 +85,7 @@ void osLib_addFunctionInternal(const char* libraryName, const char* functionName uint32 funcHashA, funcHashB; osLib_generateHashFromName(libraryName, &libHashA, &libHashB); osLib_generateHashFromName(functionName, &funcHashA, &funcHashB); + std::string hleName = fmt::format("{}.{}", libraryName, functionName); // if entry already exists, update it for (auto& it : *s_osFunctionTable) { @@ -93,11 +94,11 @@ void osLib_addFunctionInternal(const char* libraryName, const char* functionName it.funcHashA == funcHashA && it.funcHashB == funcHashB) { - it.hleFunc = PPCInterpreter_registerHLECall(osFunction); + it.hleFunc = PPCInterpreter_registerHLECall(osFunction, hleName); return; } } - s_osFunctionTable->emplace_back(libHashA, libHashB, funcHashA, funcHashB, fmt::format("{}.{}", libraryName, functionName), PPCInterpreter_registerHLECall(osFunction)); + s_osFunctionTable->emplace_back(libHashA, libHashB, funcHashA, funcHashB, hleName, PPCInterpreter_registerHLECall(osFunction, hleName)); } extern "C" DLLEXPORT void osLib_registerHLEFunction(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t * hCPU)) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 916563c8..1e6eb92b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -912,8 +912,8 @@ namespace coreinit sint32 FSOpenFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, FSFileHandleDepr_t* fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSOpenFileAsync(fsClient, fsCmdBlock, path, mode, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSOpenFileAsync(fsClient, fsCmdBlock, path, mode, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -941,8 +941,8 @@ namespace coreinit sint32 FSOpenFileEx(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, uint32 createMode, uint32 openFlag, uint32 preallocSize, FSFileHandleDepr_t* fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSOpenFileExAsync(fsClient, fsCmdBlock, path, mode, createMode, openFlag, preallocSize, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSOpenFileExAsync(fsClient, fsCmdBlock, path, mode, createMode, openFlag, preallocSize, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -975,8 +975,8 @@ namespace coreinit sint32 FSCloseFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSCloseFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSCloseFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -1009,8 +1009,8 @@ namespace coreinit sint32 FSFlushFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSFlushFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSFlushFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -1090,7 +1090,7 @@ namespace coreinit sint32 FSReadFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSReadFileAsync(fsClient, fsCmdBlock, dst, size, count, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1105,7 +1105,7 @@ namespace coreinit sint32 FSReadFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSReadFileWithPosAsync(fsClient, fsCmdBlock, dst, size, count, filePos, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1183,7 +1183,7 @@ namespace coreinit sint32 FSWriteFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSWriteFileAsync(fsClient, fsCmdBlock, src, size, count, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1196,7 +1196,7 @@ namespace coreinit sint32 FSWriteFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSWriteFileWithPosAsync(fsClient, fsCmdBlock, src, size, count, filePos, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1228,7 +1228,7 @@ namespace coreinit { // used by games: Mario Kart 8 StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSSetPosFileAsync(fsClient, fsCmdBlock, fileHandle, filePos, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1259,7 +1259,7 @@ namespace coreinit sint32 FSGetPosFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32be* returnedFilePos, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSGetPosFileAsync(fsClient, fsCmdBlock, fileHandle, returnedFilePos, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1307,8 +1307,8 @@ namespace coreinit sint32 FSOpenDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, FSDirHandlePtr dirHandleOut, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSOpenDirAsync(fsClient, fsCmdBlock, path, dirHandleOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSOpenDirAsync(fsClient, fsCmdBlock, path, dirHandleOut, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1337,8 +1337,8 @@ namespace coreinit sint32 FSReadDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSReadDirAsync(fsClient, fsCmdBlock, dirHandle, dirEntryOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSReadDirAsync(fsClient, fsCmdBlock, dirHandle, dirEntryOut, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1367,8 +1367,8 @@ namespace coreinit sint32 FSCloseDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSCloseDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSCloseDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1400,8 +1400,8 @@ namespace coreinit sint32 FSRewindDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSRewindDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSRewindDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1435,7 +1435,7 @@ namespace coreinit sint32 FSAppendFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 size, uint32 count, uint32 fileHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSAppendFileAsync(fsClient, fsCmdBlock, size, count, fileHandle, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1467,7 +1467,7 @@ namespace coreinit sint32 FSTruncateFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSFileHandle2 fileHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSTruncateFileAsync(fsClient, fsCmdBlock, fileHandle, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1531,7 +1531,7 @@ namespace coreinit sint32 FSRename(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* srcPath, char* dstPath, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSRenameAsync(fsClient, fsCmdBlock, srcPath, dstPath, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1582,8 +1582,8 @@ namespace coreinit sint32 FSRemove(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* filePath, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSRemoveAsync(fsClient, fsCmdBlock, filePath, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSRemoveAsync(fsClient, fsCmdBlock, filePath, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1634,8 +1634,8 @@ namespace coreinit sint32 FSMakeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSMakeDirAsync(fsClient, fsCmdBlock, path, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSMakeDirAsync(fsClient, fsCmdBlock, path, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1683,8 +1683,8 @@ namespace coreinit sint32 FSChangeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSChangeDirAsync(fsClient, fsCmdBlock, path, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSChangeDirAsync(fsClient, fsCmdBlock, path, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1718,8 +1718,8 @@ namespace coreinit sint32 FSGetCwd(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* dirPathOut, sint32 dirPathMaxLen, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetCwdAsync(fsClient, fsCmdBlock, dirPathOut, dirPathMaxLen, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetCwdAsync(fsClient, fsCmdBlock, dirPathOut, dirPathMaxLen, errorMask, &asyncParams); auto r = __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); return r; } @@ -1763,8 +1763,8 @@ namespace coreinit sint32 FSFlushQuota(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSFlushQuotaAsync(fsClient, fsCmdBlock, path, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSFlushQuotaAsync(fsClient, fsCmdBlock, path, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1821,8 +1821,8 @@ namespace coreinit sint32 FSGetStat(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSStat_t* statOut, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetStatAsync(fsClient, fsCmdBlock, path, statOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetStatAsync(fsClient, fsCmdBlock, path, statOut, errorMask, &asyncParams); sint32 ret = __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); return ret; } @@ -1858,8 +1858,8 @@ namespace coreinit sint32 FSGetStatFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSFileHandle2 fileHandle, FSStat_t* statOut, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetStatFileAsync(fsClient, fsCmdBlock, fileHandle, statOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetStatFileAsync(fsClient, fsCmdBlock, fileHandle, statOut, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1873,8 +1873,8 @@ namespace coreinit sint32 FSGetFreeSpaceSize(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSLargeSize* returnedFreeSize, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetFreeSpaceSizeAsync(fsClient, fsCmdBlock, path, returnedFreeSize, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetFreeSpaceSizeAsync(fsClient, fsCmdBlock, path, returnedFreeSize, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1908,8 +1908,8 @@ namespace coreinit sint32 FSIsEof(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSIsEofAsync(fsClient, fsCmdBlock, fileHandle, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSIsEofAsync(fsClient, fsCmdBlock, fileHandle, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp b/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp index ef847f26..be3cb300 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp @@ -355,23 +355,6 @@ namespace coreinit IOS_ERROR _IPCDriver_SetupCmd_IOSIoctlv(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec) { IPCCommandBody& cmdBody = requestDescriptor->resourcePtr->commandBody; - // verify input and output vectors - IPCIoctlVector* vecIn = vec; - IPCIoctlVector* vecOut = vec + numIn; - for (uint32 i = 0; i < numIn; i++) - { - if (vecIn[i].baseVirt == nullptr && vecIn[i].size != 0) - return IOS_ERROR_INVALID_ARG; - vecIn[i].basePhys = vecIn[i].baseVirt; - vecIn[i].baseVirt = nullptr; - } - for (uint32 i = 0; i < numOut; i++) - { - if (vecOut[i].baseVirt == nullptr && vecOut[i].size != 0) - return IOS_ERROR_INVALID_ARG; - vecOut[i].basePhys = vecOut[i].baseVirt; - vecOut[i].baseVirt = nullptr; - } // set args cmdBody.ppcVirt0 = MEMPTR(vec).GetMPTR(); cmdBody.args[0] = requestId; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp index d5cd0018..2d7468cf 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp @@ -195,7 +195,28 @@ namespace coreinit else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && (formatStr[2] == 'x' || formatStr[2] == 'X'))) { formatStr += 3; - // number (64bit) + // double (64bit) + strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); + if ((formatStr - formatStart) < sizeof(tempFormat)) + tempFormat[(formatStr - formatStart)] = '\0'; + else + tempFormat[sizeof(tempFormat) - 1] = '\0'; + if (integerParamIndex & 1) + integerParamIndex++; + sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU64(hCPU, integerParamIndex)); + integerParamIndex += 2; + for (sint32 i = 0; i < tempLen; i++) + { + if (writeIndex >= maxLength) + break; + strOut[writeIndex] = tempStr[i]; + writeIndex++; + } + } + else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && formatStr[2] == 'd')) + { + formatStr += 3; + // signed integer (64bit) strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); if ((formatStr - formatStart) < sizeof(tempFormat)) tempFormat[(formatStr - formatStart)] = '\0'; diff --git a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp index d88a29d4..024965fd 100644 --- a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp +++ b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp @@ -859,10 +859,10 @@ namespace H264 return H264DEC_STATUS::SUCCESS; } StackAllocator executeDoneEvent; - coreinit::OSInitEvent(executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::vector results; auto asyncTask = std::async(std::launch::async, _async_H264DECEnd, executeDoneEvent.GetPointer(), session, ctx, &results); - coreinit::OSWaitEvent(executeDoneEvent); + coreinit::OSWaitEvent(&executeDoneEvent); _ReleaseDecoderSession(session); if (!results.empty()) { @@ -977,9 +977,9 @@ namespace H264 StackAllocator stack_decodedFrameResult; for (sint32 i = 0; i < outputFrameCount; i++) - stack_resultPtrArray[i] = stack_decodedFrameResult + i; + stack_resultPtrArray[i] = &stack_decodedFrameResult + i; - H264DECFrameOutput* frameOutput = stack_decodedFrameResult + 0; + H264DECFrameOutput* frameOutput = &stack_decodedFrameResult + 0; memset(frameOutput, 0x00, sizeof(H264DECFrameOutput)); frameOutput->imagePtr = (uint8*)decodeResult.imageOutput; frameOutput->result = 100; @@ -1022,10 +1022,10 @@ namespace H264 return 0; } StackAllocator executeDoneEvent; - coreinit::OSInitEvent(executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); H264AVCDecoder::DecodeResult decodeResult; - auto asyncTask = std::async(std::launch::async, _async_H264DECExecute, executeDoneEvent.GetPointer(), session, ctx, imageOutput , &decodeResult); - coreinit::OSWaitEvent(executeDoneEvent); + auto asyncTask = std::async(std::launch::async, _async_H264DECExecute, &executeDoneEvent, session, ctx, imageOutput , &decodeResult); + coreinit::OSWaitEvent(&executeDoneEvent); _ReleaseDecoderSession(session); if(decodeResult.frameReady) H264DoFrameOutputCallback(ctx, decodeResult); diff --git a/src/Cafe/OS/libs/nn_act/nn_act.cpp b/src/Cafe/OS/libs/nn_act/nn_act.cpp index 68109586..0fd9df5a 100644 --- a/src/Cafe/OS/libs/nn_act/nn_act.cpp +++ b/src/Cafe/OS/libs/nn_act/nn_act.cpp @@ -391,7 +391,7 @@ void nnActExport_GetMiiName(PPCInterpreter_t* hCPU) StackAllocator miiData; - uint32 r = nn::act::GetMiiEx(miiData, iosu::act::ACT_SLOT_CURRENT); + uint32 r = nn::act::GetMiiEx(&miiData, iosu::act::ACT_SLOT_CURRENT); // extract name sint32 miiNameLength = 0; for (sint32 i = 0; i < MII_FFL_NAME_LENGTH; i++) @@ -414,7 +414,7 @@ void nnActExport_GetMiiNameEx(PPCInterpreter_t* hCPU) StackAllocator miiData; - uint32 r = nn::act::GetMiiEx(miiData, slot); + uint32 r = nn::act::GetMiiEx(&miiData, slot); // extract name sint32 miiNameLength = 0; for (sint32 i = 0; i < MII_FFL_NAME_LENGTH; i++) diff --git a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp index e33b6369..53ab3eef 100644 --- a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp +++ b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp @@ -1,426 +1,554 @@ #include "Cafe/OS/common/OSCommon.h" -#include "Cafe/IOSU/legacy/iosu_ioctl.h" #include "Cafe/IOSU/legacy/iosu_act.h" #include "Cafe/IOSU/legacy/iosu_fpd.h" +#include "Cafe/IOSU/legacy/iosu_ioctl.h" // deprecated +#include "Cafe/IOSU/iosu_ipc_common.h" #include "Cafe/OS/libs/coreinit/coreinit_IOS.h" - -#define fpdPrepareRequest() \ -StackAllocator _buf_fpdRequest; \ -StackAllocator _buf_bufferVector; \ -iosu::fpd::iosuFpdCemuRequest_t* fpdRequest = _buf_fpdRequest.GetPointer(); \ -ioBufferVector_t* fpdBufferVector = _buf_bufferVector.GetPointer(); \ -memset(fpdRequest, 0, sizeof(iosu::fpd::iosuFpdCemuRequest_t)); \ -memset(fpdBufferVector, 0, sizeof(ioBufferVector_t)); \ -fpdBufferVector->buffer = (uint8*)fpdRequest; +#include "Cafe/OS/libs/coreinit/coreinit_IPC.h" +#include "Cafe/OS/libs/nn_common.h" +#include "util/ChunkedHeap/ChunkedHeap.h" +#include "Common/CafeString.h" namespace nn { namespace fp { + static const auto FPResult_OkZero = 0; + static const auto FPResult_Ok = BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_FP, 0); + static const auto FPResult_InvalidIPCParam = BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_FP, 0x680); + static const auto FPResult_RequestFailed = BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // figure out proper error code - struct + struct { - bool isInitialized; + uint32 initCounter; bool isAdminMode; + bool isLoggedIn; + IOSDevHandle fpdHandle; + SysAllocator fpMutex; + SysAllocator g_fpdAllocatorSpace; + VHeap* fpBufferHeap{nullptr}; + // PPC buffers for async notification query + SysAllocator notificationCount; + SysAllocator notificationBuffer; + bool getNotificationCalled{false}; + // notification handler + MEMPTR notificationHandler{nullptr}; + MEMPTR notificationHandlerParam{nullptr}; }g_fp = { }; - void Initialize() + class { - if (g_fp.isInitialized == false) + public: + void Init() { - g_fp.isInitialized = true; - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_INITIALIZE; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); + std::unique_lock _l(m_mtx); + g_fp.fpBufferHeap = new VHeap(g_fp.g_fpdAllocatorSpace.GetPtr(), g_fp.g_fpdAllocatorSpace.GetByteSize()); } - } - - - - void export_IsInitialized(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "Called nn_fp.IsInitialized"); - osLib_returnFromFunction(hCPU, g_fp.isInitialized ? 1 : 0); - } - - void export_Initialize(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "Called nn_fp.Initialize"); - - Initialize(); - - osLib_returnFromFunction(hCPU, 0); - } - - void export_InitializeAdmin(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "Called nn_fp.InitializeAdmin"); - Initialize(); - g_fp.isAdminMode = true; - osLib_returnFromFunction(hCPU, 0); - } - - void export_IsInitializedAdmin(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "nn_fp.IsInitializedAdmin()"); - osLib_returnFromFunction(hCPU, g_fp.isInitialized ? 1 : 0); - } - - void export_SetNotificationHandler(PPCInterpreter_t* hCPU) - { - ppcDefineParamU32(notificationMask, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamMPTR(customParam, 2); - - cemuLog_logDebug(LogType::Force, "nn_fp.SetNotificationHandler(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_SET_NOTIFICATION_HANDLER; - fpdRequest->setNotificationHandler.notificationMask = notificationMask; - fpdRequest->setNotificationHandler.funcPtr = funcMPTR; - fpdRequest->setNotificationHandler.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, 0); - } - - void export_LoginAsync(PPCInterpreter_t* hCPU) - { - ppcDefineParamMPTR(funcPtr, 0); - ppcDefineParamMPTR(custom, 1); - cemuLog_logDebug(LogType::Force, "nn_fp.LoginAsync(0x{:08x},0x{:08x})", funcPtr, custom); - if (g_fp.isInitialized == false) + void Destroy() { - osLib_returnFromFunction(hCPU, 0xC0C00580); + std::unique_lock _l(m_mtx); + delete g_fp.fpBufferHeap; + } + + void* Allocate(uint32 size, uint32 alignment) + { + std::unique_lock _l(m_mtx); + void* p = g_fp.fpBufferHeap->alloc(size, 32); + uint32 heapSize, allocationSize, allocNum; + g_fp.fpBufferHeap->getStats(heapSize, allocationSize, allocNum); + return p; + } + + void Free(void* ptr) + { + std::unique_lock _l(m_mtx); + g_fp.fpBufferHeap->free(ptr); + } + + private: + std::mutex m_mtx; + }FPIpcBufferAllocator; + + class FPIpcContext { + static inline constexpr uint32 MAX_VEC_COUNT = 8; + public: + // use FP heap for this class + static void* operator new(size_t size) + { + return FPIpcBufferAllocator.Allocate(size, (uint32)alignof(FPIpcContext)); + } + + static void operator delete(void* ptr) + { + FPIpcBufferAllocator.Free(ptr); + } + + FPIpcContext(iosu::fpd::FPD_REQUEST_ID requestId) : m_requestId(requestId) + { + } + + ~FPIpcContext() + { + if(m_dataBuffer) + FPIpcBufferAllocator.Free(m_dataBuffer); + } + + void AddInput(void* ptr, uint32 size) + { + size_t vecIndex = GetVecInIndex(m_numVecIn); + m_vec[vecIndex].baseVirt = ptr; + m_vec[vecIndex].size = size; + m_numVecIn = m_numVecIn + 1; + } + + void AddOutput(void* ptr, uint32 size) + { + cemu_assert_debug(m_numVecIn == 0); // all outputs need to be added before any inputs + size_t vecIndex = GetVecOutIndex(m_numVecOut); + m_vec[vecIndex].baseVirt = ptr; + m_vec[vecIndex].size = size; + m_numVecOut = m_numVecOut + 1; + } + + uint32 Submit(std::unique_ptr owner) + { + InitSubmissionBuffer(); + // note: While generally, Ioctlv() usage has the order as input (app->IOSU) followed by output (IOSU->app), FP uses it the other way around + nnResult r = coreinit::IOS_Ioctlv(g_fp.fpdHandle, (uint32)m_requestId.value(), m_numVecOut, m_numVecIn, m_vec); + CopyBackOutputs(); + owner.reset(); + return r; + } + + nnResult SubmitAsync(std::unique_ptr owner, MEMPTR callbackFunc, MEMPTR callbackParam) + { + InitSubmissionBuffer(); + this->m_callbackFunc = callbackFunc; + this->m_callbackParam = callbackParam; + nnResult r = coreinit::IOS_IoctlvAsync(g_fp.fpdHandle, (uint32)m_requestId.value(), m_numVecOut, m_numVecIn, m_vec, MEMPTR(PPCInterpreter_makeCallableExportDepr(AsyncHandler)), MEMPTR(this)); + owner.release(); + return r; + } + + private: + size_t GetVecInIndex(uint8 inIndex) + { + return m_numVecOut + inIndex; + } + + size_t GetVecOutIndex(uint8 outIndex) + { + return outIndex; + } + + void InitSubmissionBuffer() + { + // allocate a chunk of memory to hold the input/output vectors and their data + uint32 vecOffset[MAX_VEC_COUNT]; + uint32 totalBufferSize = 0; + for(uint8 i=0; i(m_vec[vecIndex].baseVirt).GetPtr(), MEMPTR(m_vecOriginalAddress[vecIndex]).GetPtr(), m_vec[vecIndex].size); + } + } + + static void AsyncHandler(PPCInterpreter_t* hCPU) + { + ppcDefineParamU32(result, 0); + ppcDefineParamPtr(ipcCtx, FPIpcContext, 1); + ipcCtx->m_asyncResult = result; // store result in variable since FP callbacks pass a pointer to nnResult and not the value directly + ipcCtx->CopyBackOutputs(); + cemuLog_logDebug(LogType::Force, "[DBG] AsyncHandler BeforeCallback"); + PPCCoreCallback(ipcCtx->m_callbackFunc, &ipcCtx->m_asyncResult, ipcCtx->m_callbackParam); + cemuLog_logDebug(LogType::Force, "[DBG] AsyncHandler AfterCallback"); + delete ipcCtx; + osLib_returnFromFunction(hCPU, 0); + } + + void CopyBackOutputs() + { + if(m_numVecOut > 0) + { + // copy output from temporary output buffers to the original addresses + for(uint8 i=0; i m_requestId; + uint8be m_numVecIn{0}; + uint8be m_numVecOut{0}; + IPCIoctlVector m_vec[MAX_VEC_COUNT]; + MEMPTR m_vecOriginalAddress[MAX_VEC_COUNT]{}; + MEMPTR m_dataBuffer{nullptr}; + MEMPTR m_callbackFunc{nullptr}; + MEMPTR m_callbackParam{nullptr}; + betype m_asyncResult; + }; + + struct FPGlobalLock + { + FPGlobalLock() + { + coreinit::OSLockMutex(&g_fp.fpMutex); + } + ~FPGlobalLock() + { + coreinit::OSUnlockMutex(&g_fp.fpMutex); + } + }; + #define FP_API_BASE() if (g_fp.initCounter == 0) return 0xC0C00580; FPGlobalLock _fpLock; + #define FP_API_BASE_ZeroOnError() if (g_fp.initCounter == 0) return 0; FPGlobalLock _fpLock; + + nnResult Initialize() + { + FPGlobalLock _fpLock; + if (g_fp.initCounter == 0) + { + g_fp.fpdHandle = coreinit::IOS_Open("/dev/fpd", 0); + } + g_fp.initCounter++; + return FPResult_OkZero; + } + + uint32 IsInitialized() + { + FPGlobalLock _fpLock; + return g_fp.initCounter > 0 ? 1 : 0; + } + + nnResult InitializeAdmin(PPCInterpreter_t* hCPU) + { + FPGlobalLock _fpLock; + g_fp.isAdminMode = true; + return Initialize(); + } + + uint32 IsInitializedAdmin() + { + FPGlobalLock _fpLock; + return g_fp.initCounter > 0 ? 1 : 0; + } + + nnResult Finalize() + { + FPGlobalLock _fpLock; + if (g_fp.initCounter == 1) + { + g_fp.initCounter = 0; + g_fp.isAdminMode = false; + g_fp.isLoggedIn = false; + coreinit::IOS_Close(g_fp.fpdHandle); + g_fp.getNotificationCalled = false; + } + else if (g_fp.initCounter > 0) + g_fp.initCounter--; + return FPResult_OkZero; + } + + nnResult FinalizeAdmin() + { + return Finalize(); + } + + void GetNextNotificationAsync(); + + nnResult SetNotificationHandler(uint32 notificationMask, void* funcPtr, void* userParam) + { + FP_API_BASE(); + g_fp.notificationHandler = funcPtr; + g_fp.notificationHandlerParam = userParam; + StackAllocator notificationMaskBuf; notificationMaskBuf = notificationMask; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::SetNotificationMask); + ipcCtx->AddInput(¬ificationMaskBuf, sizeof(uint32be)); + nnResult r = ipcCtx->Submit(std::move(ipcCtx)); + if (NN_RESULT_IS_SUCCESS(r)) + { + // async query for notifications + GetNextNotificationAsync(); + } + return r; + } + + void GetNextNotificationAsyncHandler(PPCInterpreter_t* hCPU) + { + coreinit::OSLockMutex(&g_fp.fpMutex); + cemu_assert_debug(g_fp.getNotificationCalled); + g_fp.getNotificationCalled = false; + auto bufPtr = g_fp.notificationBuffer.GetPtr(); + uint32 count = g_fp.notificationCount->value(); + if (count == 0) + { + GetNextNotificationAsync(); + coreinit::OSUnlockMutex(&g_fp.fpMutex); + osLib_returnFromFunction(hCPU, 0); return; } - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_LOGIN_ASYNC; - fpdRequest->loginAsync.funcPtr = funcPtr; - fpdRequest->loginAsync.custom = custom; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); + // copy notifications to temporary buffer using std::copy + iosu::fpd::FPDNotification tempBuffer[256]; + std::copy(g_fp.notificationBuffer.GetPtr(), g_fp.notificationBuffer.GetPtr() + count, tempBuffer); + // call handler for each notification, but do it outside of the lock + void* notificationHandler = g_fp.notificationHandler; + void* notificationHandlerParam = g_fp.notificationHandlerParam; + coreinit::OSUnlockMutex(&g_fp.fpMutex); + iosu::fpd::FPDNotification* notificationBuffer = g_fp.notificationBuffer.GetPtr(); + for (uint32 i = 0; i < count; i++) + PPCCoreCallback(notificationHandler, (uint32)notificationBuffer[i].type, notificationBuffer[i].pid, notificationHandlerParam); + coreinit::OSLockMutex(&g_fp.fpMutex); + // query more notifications + GetNextNotificationAsync(); + coreinit::OSUnlockMutex(&g_fp.fpMutex); osLib_returnFromFunction(hCPU, 0); } - void export_HasLoggedIn(PPCInterpreter_t* hCPU) + void GetNextNotificationAsync() { - // Sonic All Star Racing needs this - cemuLog_logDebug(LogType::Force, "nn_fp.HasLoggedIn()"); - osLib_returnFromFunction(hCPU, 1); + if (g_fp.getNotificationCalled) + return; + g_fp.getNotificationCalled = true; + g_fp.notificationCount = 0; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetNotificationAsync); + ipcCtx->AddOutput(g_fp.notificationBuffer.GetPtr(), g_fp.notificationBuffer.GetByteSize()); + ipcCtx->AddOutput(g_fp.notificationCount.GetPtr(), sizeof(uint32be)); + cemu_assert_debug(g_fp.notificationBuffer.GetByteSize() == 0x800); + nnResult r = ipcCtx->SubmitAsync(std::move(ipcCtx), MEMPTR(PPCInterpreter_makeCallableExportDepr(GetNextNotificationAsyncHandler)), nullptr); } - void export_IsOnline(PPCInterpreter_t* hCPU) + nnResult LoginAsync(void* funcPtr, void* userParam) { - //cemuLog_logDebug(LogType::Force, "nn_fp.IsOnline();"); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_IS_ONLINE; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - cemuLog_logDebug(LogType::Force, "nn_fp.IsOnline() -> {}", fpdRequest->resultU32.u32); - - osLib_returnFromFunction(hCPU, fpdRequest->resultU32.u32); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::LoginAsync); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, userParam); } - void export_GetFriendList(PPCInterpreter_t* hCPU) + uint32 HasLoggedIn() { - ppcDefineParamMEMPTR(pidList, uint32be, 0); - ppcDefineParamMEMPTR(returnedCount, uint32be, 1); - ppcDefineParamU32(startIndex, 2); - ppcDefineParamU32(maxCount, 3); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendList(...)"); - //debug_printf("nn_fp.GetFriendList(0x%08x, 0x%08x, %d, %d)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_LIST; - - fpdRequest->getFriendList.pidList = pidList; - fpdRequest->getFriendList.startIndex = startIndex; - fpdRequest->getFriendList.maxCount = maxCount; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - *returnedCount = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE_ZeroOnError(); + // Sonic All Star Racing uses this + // and Monster Hunter 3 Ultimate needs this to return false at least once to initiate login and not get stuck + // this returns false until LoginAsync was called and has completed (?) even if the user is already logged in + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::HasLoggedIn); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf != 0 ? 1 : 0; } - void export_GetFriendRequestList(PPCInterpreter_t* hCPU) + uint32 IsOnline() { - // GetFriendRequestList__Q2_2nn2fpFPUiT1UiT3 - ppcDefineParamMEMPTR(pidList, uint32be, 0); - ppcDefineParamMEMPTR(returnedCount, uint32be, 1); - ppcDefineParamU32(startIndex, 2); - ppcDefineParamU32(maxCount, 3); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendRequestList(...)"); - //debug_printf("nn_fp.GetFriendList(0x%08x, 0x%08x, %d, %d)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIENDREQUEST_LIST; - - fpdRequest->getFriendList.pidList = pidList; - fpdRequest->getFriendList.startIndex = startIndex; - fpdRequest->getFriendList.maxCount = maxCount; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - *returnedCount = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE_ZeroOnError(); + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::IsOnline); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf != 0 ? 1 : 0; } - void export_GetFriendListAll(PPCInterpreter_t* hCPU) + nnResult GetFriendList(uint32be* pidList, uint32be* returnedCount, uint32 startIndex, uint32 maxCount) { - ppcDefineParamMEMPTR(pidList, uint32be, 0); - ppcDefineParamMEMPTR(returnedCount, uint32be, 1); - ppcDefineParamU32(startIndex, 2); - ppcDefineParamU32(maxCount, 3); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendListAll(...)"); - - //debug_printf("nn_fp.GetFriendListAll(0x%08x, 0x%08x, %d, %d)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_LIST_ALL; - - fpdRequest->getFriendList.pidList = pidList; - fpdRequest->getFriendList.startIndex = startIndex; - fpdRequest->getFriendList.maxCount = maxCount; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - *returnedCount = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator startIndexBuf; startIndexBuf = startIndex; + StackAllocator maxCountBuf; maxCountBuf = maxCount; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendList); + ipcCtx->AddOutput(pidList, sizeof(uint32be) * maxCount); + ipcCtx->AddOutput(returnedCount, sizeof(uint32be)); + ipcCtx->AddInput(&startIndexBuf, sizeof(uint32be)); + ipcCtx->AddInput(&maxCountBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendListEx(PPCInterpreter_t* hCPU) + nnResult GetFriendRequestList(uint32be* pidList, uint32be* returnedCount, uint32 startIndex, uint32 maxCount) { - ppcDefineParamMEMPTR(friendData, iosu::fpd::friendData_t, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendListEx(...)"); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_LIST_EX; - - fpdRequest->getFriendListEx.friendData = friendData; - fpdRequest->getFriendListEx.pidList = pidList; - fpdRequest->getFriendListEx.count = count; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator startIndexBuf; startIndexBuf = startIndex; + StackAllocator maxCountBuf; maxCountBuf = maxCount; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendRequestList); + ipcCtx->AddOutput(pidList, sizeof(uint32be) * maxCount); + ipcCtx->AddOutput(returnedCount, sizeof(uint32be)); + ipcCtx->AddInput(&startIndexBuf, sizeof(uint32be)); + ipcCtx->AddInput(&maxCountBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendRequestListEx(PPCInterpreter_t* hCPU) + nnResult GetFriendListAll(uint32be* pidList, uint32be* returnedCount, uint32 startIndex, uint32 maxCount) { - ppcDefineParamMEMPTR(friendRequest, iosu::fpd::friendRequest_t, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendRequestListEx(...)"); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIENDREQUEST_LIST_EX; - - fpdRequest->getFriendRequestListEx.friendRequest = friendRequest; - fpdRequest->getFriendRequestListEx.pidList = pidList; - fpdRequest->getFriendRequestListEx.count = count; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator startIndexBuf; startIndexBuf = startIndex; + StackAllocator maxCountBuf; maxCountBuf = maxCount; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendListAll); + ipcCtx->AddOutput(pidList, sizeof(uint32be) * maxCount); + ipcCtx->AddOutput(returnedCount, sizeof(uint32be)); + ipcCtx->AddInput(&startIndexBuf, sizeof(uint32be)); + ipcCtx->AddInput(&maxCountBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetBasicInfoAsync(PPCInterpreter_t* hCPU) + nnResult GetFriendListEx(iosu::fpd::FriendData* friendData, uint32be* pidList, uint32 count) { - ppcDefineParamMEMPTR(basicInfo, iosu::fpd::friendBasicInfo_t, 0); - ppcDefineParamTypePtr(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - ppcDefineParamMPTR(funcMPTR, 3); - ppcDefineParamU32(customParam, 4); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_BASIC_INFO_ASYNC; - fpdRequest->getBasicInfo.basicInfo = basicInfo; - fpdRequest->getBasicInfo.pidList = pidList; - fpdRequest->getBasicInfo.count = count; - fpdRequest->getBasicInfo.funcPtr = funcMPTR; - fpdRequest->getBasicInfo.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector);; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendListEx); + ipcCtx->AddOutput(friendData, sizeof(iosu::fpd::FriendData) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetMyPrincipalId(PPCInterpreter_t* hCPU) + nnResult GetFriendRequestListEx(iosu::fpd::FriendRequest* friendRequest, uint32be* pidList, uint32 count) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyPrincipalId()"); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_PRINCIPAL_ID; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - uint32 principalId = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, principalId); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendRequestListEx); + ipcCtx->AddOutput(friendRequest, sizeof(iosu::fpd::FriendRequest) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetMyAccountId(PPCInterpreter_t* hCPU) + nnResult GetBasicInfoAsync(iosu::fpd::FriendBasicInfo* basicInfo, uint32be* pidList, uint32 count, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyAccountId(0x{:08x})", hCPU->gpr[3]); - ppcDefineParamTypePtr(accountId, uint8, 0); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_ACCOUNT_ID; - fpdRequest->common.ptr = (void*)accountId; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetBasicInfoAsync); + ipcCtx->AddOutput(basicInfo, sizeof(iosu::fpd::FriendBasicInfo) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_GetMyScreenName(PPCInterpreter_t* hCPU) + uint32 GetMyPrincipalId() { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyScreenName(0x{:08x})", hCPU->gpr[3]); - ppcDefineParamTypePtr(screenname, uint16be, 0); - - screenname[0] = '\0'; - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_SCREENNAME; - fpdRequest->common.ptr = (void*)screenname; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE_ZeroOnError(); + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyPrincipalId); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf->value(); } - typedef struct + nnResult GetMyAccountId(uint8be* accountId) { - uint8 showOnline; // show online status to others - uint8 showGame; // show played game to others - uint8 blockFriendRequests; // block friend requests - }fpPerference_t; - - void export_GetMyPreference(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyPreference(0x{:08x}) - placeholder", hCPU->gpr[3]); - ppcDefineParamTypePtr(pref, fpPerference_t, 0); - - pref->showOnline = 1; - pref->showGame = 1; - pref->blockFriendRequests = 0; - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyAccountId); + ipcCtx->AddOutput(accountId, ACT_ACCOUNTID_LENGTH); + return ipcCtx->Submit(std::move(ipcCtx)); } - // GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference - - void export_GetMyMii(PPCInterpreter_t* hCPU) + nnResult GetMyScreenName(uint16be* screenname) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyMii(0x{:08x})", hCPU->gpr[3]); - ppcDefineParamTypePtr(fflData, FFLData_t, 0); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_MII; - fpdRequest->common.ptr = (void*)fflData; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyScreenName); + ipcCtx->AddOutput(screenname, ACT_NICKNAME_SIZE*sizeof(uint16)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendAccountId(PPCInterpreter_t* hCPU) + nnResult GetMyPreference(iosu::fpd::FPDPreference* myPreference) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendAccountId(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(accountIds, char, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_ACCOUNT_ID; - fpdRequest->getFriendAccountId.accountIds = accountIds; - fpdRequest->getFriendAccountId.pidList = pidList; - fpdRequest->getFriendAccountId.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyPreference); + ipcCtx->AddOutput(myPreference, sizeof(iosu::fpd::FPDPreference)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendScreenName(PPCInterpreter_t* hCPU) + nnResult GetMyMii(FFLData_t* fflData) { - // GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendScreenName(0x{:08x},0x{:08x},0x{:08x},{},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); - ppcDefineParamMEMPTR(nameList, uint16be, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - ppcDefineParamU32(replaceNonAscii, 3); - ppcDefineParamMEMPTR(languageList, uint8, 4); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_SCREENNAME; - fpdRequest->getFriendScreenname.nameList = nameList; - fpdRequest->getFriendScreenname.pidList = pidList; - fpdRequest->getFriendScreenname.count = count; - fpdRequest->getFriendScreenname.replaceNonAscii = replaceNonAscii != 0; - fpdRequest->getFriendScreenname.languageList = languageList; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyMii); + ipcCtx->AddOutput(fflData, sizeof(FFLData_t)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendMii(PPCInterpreter_t* hCPU) + nnResult GetFriendAccountId(uint8be* accountIdArray, uint32be* pidList, uint32 count) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendMii(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(miiList, FFLData_t, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_MII; - fpdRequest->getFriendMii.miiList = (uint8*)miiList.GetPtr(); - fpdRequest->getFriendMii.pidList = pidList; - fpdRequest->getFriendMii.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + if (count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendAccountId); + ipcCtx->AddOutput(accountIdArray, ACT_ACCOUNTID_LENGTH * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendPresence(PPCInterpreter_t* hCPU) + nnResult GetFriendScreenName(uint16be* nameList, uint32be* pidList, uint32 count, uint8 replaceNonAscii, uint8be* languageList) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendPresence(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(presenceList, uint8, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_PRESENCE; - fpdRequest->getFriendPresence.presenceList = (uint8*)presenceList.GetPtr(); - fpdRequest->getFriendPresence.pidList = pidList; - fpdRequest->getFriendPresence.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + if (count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + StackAllocator replaceNonAsciiBuf; replaceNonAsciiBuf = replaceNonAscii; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendScreenName); + ipcCtx->AddOutput(nameList, ACT_NICKNAME_SIZE * sizeof(uint16be) * count); + ipcCtx->AddOutput(languageList, languageList ? sizeof(uint8be) * count : 0); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + ipcCtx->AddInput(&replaceNonAsciiBuf, sizeof(uint8be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendRelationship(PPCInterpreter_t* hCPU) + nnResult GetFriendMii(FFLData_t* miiList, uint32be* pidList, uint32 count) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendRelationship(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(relationshipList, uint8, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_RELATIONSHIP; - fpdRequest->getFriendRelationship.relationshipList = (uint8*)relationshipList.GetPtr(); - fpdRequest->getFriendRelationship.pidList = pidList; - fpdRequest->getFriendRelationship.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + if(count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendMii); + ipcCtx->AddOutput(miiList, sizeof(FFLData_t) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_IsJoinable(PPCInterpreter_t* hCPU) + nnResult GetFriendPresence(iosu::fpd::FriendPresence* presenceList, uint32be* pidList, uint32 count) { - ppcDefineParamTypePtr(presence, iosu::fpd::friendPresence_t, 0); - ppcDefineParamU64(joinMask, 2); + FP_API_BASE(); + if(count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendPresence); + ipcCtx->AddOutput(presenceList, sizeof(iosu::fpd::FriendPresence) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); + } + nnResult GetFriendRelationship(uint8* relationshipList, uint32be* pidList, uint32 count) + { + FP_API_BASE(); + if(count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendRelationship); + ipcCtx->AddOutput(relationshipList, sizeof(uint8) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); + } + + uint32 IsJoinable(iosu::fpd::FriendPresence* presence, uint64 joinMask) + { if (presence->isValid == 0 || presence->isOnline == 0 || presence->gameMode.joinGameId == 0 || @@ -428,368 +556,237 @@ namespace nn presence->gameMode.groupId == 0 || presence->gameMode.joinGameMode >= 64 ) { - osLib_returnFromFunction(hCPU, 0); - return; + return 0; } uint32 joinGameMode = presence->gameMode.joinGameMode; uint64 joinModeMask = (1ULL<gameMode.joinFlagMask; if (joinFlagMask == 0) - { - osLib_returnFromFunction(hCPU, 0); - return; - } + return 0; if (joinFlagMask == 1) - { - osLib_returnFromFunction(hCPU, 1); - return; - } + return 1; if (joinFlagMask == 2) { - // check relationship + // check relationship uint8 relationship[1] = { 0 }; StackAllocator pidList; - pidList[0] = presence->gameMode.hostPid; - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_RELATIONSHIP; - fpdRequest->getFriendRelationship.relationshipList = relationship; - fpdRequest->getFriendRelationship.pidList = pidList.GetPointer(); - fpdRequest->getFriendRelationship.count = 1; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - + pidList = presence->gameMode.hostPid; + GetFriendRelationship(relationship, &pidList, 1); if(relationship[0] == iosu::fpd::RELATIONSHIP_FRIEND) - osLib_returnFromFunction(hCPU, 1); - else - osLib_returnFromFunction(hCPU, 0); - return; + return 1; + return 0; } if (joinFlagMask == 0x65 || joinFlagMask == 0x66) { cemuLog_log(LogType::Force, "Unsupported friend invite"); } - - osLib_returnFromFunction(hCPU, 0); + return 0; } - void export_CheckSettingStatusAsync(PPCInterpreter_t* hCPU) + nnResult CheckSettingStatusAsync(uint8* status, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.CheckSettingStatusAsync(0x{:08x},0x{:08x},0x{:08x}) - placeholder", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamTypePtr(uknR3, uint8, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamU32(customParam, 2); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::CheckSettingStatusAsync); + ipcCtx->AddOutput(status, sizeof(uint8be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); + } - if (g_fp.isAdminMode == false) + uint32 IsPreferenceValid() + { + FP_API_BASE_ZeroOnError(); + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::IsPreferenceValid); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf != 0 ? 1 : 0; + } + + nnResult UpdatePreferenceAsync(iosu::fpd::FPDPreference* newPreference, void* funcPtr, void* customParam) + { + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::UpdatePreferenceAsync); + ipcCtx->AddInput(newPreference, sizeof(iosu::fpd::FPDPreference)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); + } + + nnResult UpdateGameModeWithUnusedParam(iosu::fpd::GameMode* gameMode, uint16be* gameModeMessage, uint32 unusedParam) + { + FP_API_BASE(); + uint32 messageLen = CafeStringHelpers::Length(gameModeMessage, iosu::fpd::GAMEMODE_MAX_MESSAGE_LENGTH); + if(messageLen >= iosu::fpd::GAMEMODE_MAX_MESSAGE_LENGTH) { - - osLib_returnFromFunction(hCPU, 0xC0C00800); - return; + cemuLog_log(LogType::Force, "UpdateGameMode: message too long"); + return FPResult_InvalidIPCParam; } - - *uknR3 = 1; - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::UpdateGameModeVariation2); + ipcCtx->AddInput(gameMode, sizeof(iosu::fpd::GameMode)); + ipcCtx->AddInput(gameModeMessage, sizeof(uint16be) * (messageLen + 1)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_IsPreferenceValid(PPCInterpreter_t* hCPU) + nnResult UpdateGameMode(iosu::fpd::GameMode* gameMode, uint16be* gameModeMessage) { - cemuLog_logDebug(LogType::Force, "nn_fp.IsPreferenceValid()"); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_IS_PREFERENCE_VALID; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->resultU32.u32); + return UpdateGameModeWithUnusedParam(gameMode, gameModeMessage, 0); } - void export_UpdatePreferenceAsync(PPCInterpreter_t* hCPU) + nnResult GetRequestBlockSettingAsync(uint8* blockSettingList, uint32be* pidList, uint32 count, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.UpdatePreferenceAsync(0x{:08x},0x{:08x},0x{:08x}) - placeholder", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamTypePtr(uknR3, uint8, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamU32(customParam, 2); - - if (g_fp.isAdminMode == false) - { - - osLib_returnFromFunction(hCPU, 0xC0C00800); - return; - } - - //*uknR3 = 0; // seems to be 3 bytes (nn::fp::Preference const *) - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetRequestBlockSettingAsync); + ipcCtx->AddOutput(blockSettingList, sizeof(uint8be) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_UpdateGameMode(PPCInterpreter_t* hCPU) + // overload of AddFriendAsync + nnResult AddFriendAsyncByPid(uint32 pid, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.UpdateGameMode(0x{:08x},0x{:08x},{})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(gameMode, iosu::fpd::gameMode_t, 0); - ppcDefineParamMEMPTR(gameModeMessage, uint16be, 1); - ppcDefineParamU32(uknR5, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_UPDATE_GAMEMODE; - fpdRequest->updateGameMode.gameMode = gameMode; - fpdRequest->updateGameMode.gameModeMessage = gameModeMessage; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + StackAllocator pidBuf; pidBuf = pid; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::AddFriendAsyncByPid); + ipcCtx->AddInput(&pidBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_GetRequestBlockSettingAsync(PPCInterpreter_t* hCPU) + nnResult DeleteFriendFlagsAsync(uint32be* pidList, uint32 pidCount, uint32 ukn, void* funcPtr, void* customParam) { - ppcDefineParamTypePtr(settingList, uint8, 0); - ppcDefineParamTypePtr(pidList, uint32be, 1); - ppcDefineParamU32(pidCount, 2); - ppcDefineParamMPTR(funcMPTR, 3); - ppcDefineParamMPTR(customParam, 4); - - cemuLog_logDebug(LogType::Force, "GetRequestBlockSettingAsync(...) - todo"); - - for (uint32 i = 0; i < pidCount; i++) - settingList[i] = 0; - // 0 means not blocked. Friend app will continue with GetBasicInformation() - // 1 means blocked. Friend app will continue with AddFriendAsync to add the user as a provisional friend - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + // admin function? + FP_API_BASE(); + StackAllocator pidCountBuf; pidCountBuf = pidCount; + StackAllocator uknBuf; uknBuf = ukn; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::DeleteFriendFlagsAsync); + ipcCtx->AddInput(pidList, sizeof(uint32be) * pidCount); + ipcCtx->AddInput(&pidCountBuf, sizeof(uint32be)); + ipcCtx->AddInput(&uknBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_AddFriendAsync(PPCInterpreter_t* hCPU) + // overload of AddFriendRequestAsync + nnResult AddFriendRequestByPlayRecordAsync(iosu::fpd::RecentPlayRecordEx* playRecord, uint16be* message, void* funcPtr, void* customParam) { - // AddFriendAsync__Q2_2nn2fpFPCcPFQ2_2nn6ResultPv_vPv - ppcDefineParamU32(principalId, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamMPTR(customParam, 2); - -#ifdef CEMU_DEBUG_ASSERT - assert_dbg(); -#endif - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_ADD_FRIEND; - fpdRequest->addOrRemoveFriend.pid = principalId; - fpdRequest->addOrRemoveFriend.funcPtr = funcMPTR; - fpdRequest->addOrRemoveFriend.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::AddFriendRequestByPlayRecordAsync); + uint32 messageLen = 0; + while(message[messageLen] != 0) + messageLen++; + ipcCtx->AddInput(playRecord, sizeof(iosu::fpd::RecentPlayRecordEx)); + ipcCtx->AddInput(message, sizeof(uint16be) * (messageLen+1)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - - void export_DeleteFriendFlagsAsync(PPCInterpreter_t* hCPU) + nnResult RemoveFriendAsync(uint32 pid, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.DeleteFriendFlagsAsync(...) - todo"); - ppcDefineParamU32(uknR3, 0); // example value: pointer - ppcDefineParamU32(uknR4, 1); // example value: 1 - ppcDefineParamU32(uknR5, 2); // example value: 1 - ppcDefineParamMPTR(funcMPTR, 3); - ppcDefineParamU32(customParam, 4); - - if (g_fp.isAdminMode == false) - { - osLib_returnFromFunction(hCPU, 0xC0C00800); - return; - } - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + StackAllocator pidBuf; pidBuf = pid; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::RemoveFriendAsync); + ipcCtx->AddInput(&pidBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - - typedef struct + nnResult MarkFriendRequestsAsReceivedAsync(uint64be* messageIdList, uint32 count, void* funcPtr, void* customParam) { - /* +0x00 */ uint32be pid; - /* +0x04 */ uint8 ukn04; - /* +0x05 */ uint8 ukn05; - /* +0x06 */ uint8 ukn06[0x22]; - /* +0x28 */ uint8 ukn28[0x22]; - /* +0x4A */ uint8 _uknOrPadding4A[6]; - /* +0x50 */ uint32be ukn50; - /* +0x54 */ uint32be ukn54; - /* +0x58 */ uint16be ukn58; - /* +0x5C */ uint8 _padding5C[4]; - /* +0x60 */ iosu::fpd::fpdDate_t date; - }RecentPlayRecordEx_t; - - static_assert(sizeof(RecentPlayRecordEx_t) == 0x68, ""); - static_assert(offsetof(RecentPlayRecordEx_t, ukn06) == 0x06, ""); - static_assert(offsetof(RecentPlayRecordEx_t, ukn50) == 0x50, ""); - - void export_AddFriendRequestAsync(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(playRecord, RecentPlayRecordEx_t, 0); - ppcDefineParamTypePtr(message, uint16be, 1); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - - uint8* uknData = (uint8*)playRecord; - - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_ADD_FRIEND_REQUEST; - fpdRequest->addFriendRequest.pid = playRecord->pid; - fpdRequest->addFriendRequest.message = message; - fpdRequest->addFriendRequest.funcPtr = funcMPTR; - fpdRequest->addFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::MarkFriendRequestsAsReceivedAsync); + ipcCtx->AddInput(messageIdList, sizeof(uint64be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_RemoveFriendAsync(PPCInterpreter_t* hCPU) + nnResult CancelFriendRequestAsync(uint64 requestId, void* funcPtr, void* customParam) { - ppcDefineParamU32(principalId, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamMPTR(customParam, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_REMOVE_FRIEND_ASYNC; - fpdRequest->addOrRemoveFriend.pid = principalId; - fpdRequest->addOrRemoveFriend.funcPtr = funcMPTR; - fpdRequest->addOrRemoveFriend.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator requestIdBuf; requestIdBuf = requestId; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::CancelFriendRequestAsync); + ipcCtx->AddInput(&requestIdBuf, sizeof(uint64be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_MarkFriendRequestsAsReceivedAsync(PPCInterpreter_t* hCPU) + nnResult DeleteFriendRequestAsync(uint64 requestId, void* funcPtr, void* customParam) { - ppcDefineParamTypePtr(messageIdList, uint64, 0); - ppcDefineParamU32(count, 1); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_MARK_FRIEND_REQUEST_AS_RECEIVED_ASYNC; - fpdRequest->markFriendRequest.messageIdList = messageIdList; - fpdRequest->markFriendRequest.count = count; - fpdRequest->markFriendRequest.funcPtr = funcMPTR; - fpdRequest->markFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator requestIdBuf; requestIdBuf = requestId; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::DeleteFriendRequestAsync); + ipcCtx->AddInput(&requestIdBuf, sizeof(uint64be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_CancelFriendRequestAsync(PPCInterpreter_t* hCPU) + nnResult AcceptFriendRequestAsync(uint64 requestId, void* funcPtr, void* customParam) { - ppcDefineParamU64(frqMessageId, 0); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_CANCEL_FRIEND_REQUEST_ASYNC; - fpdRequest->cancelOrAcceptFriendRequest.messageId = frqMessageId; - fpdRequest->cancelOrAcceptFriendRequest.funcPtr = funcMPTR; - fpdRequest->cancelOrAcceptFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); - } - - void export_AcceptFriendRequestAsync(PPCInterpreter_t* hCPU) - { - ppcDefineParamU64(frqMessageId, 0); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_ACCEPT_FRIEND_REQUEST_ASYNC; - fpdRequest->cancelOrAcceptFriendRequest.messageId = frqMessageId; - fpdRequest->cancelOrAcceptFriendRequest.funcPtr = funcMPTR; - fpdRequest->cancelOrAcceptFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator requestIdBuf; requestIdBuf = requestId; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::AcceptFriendRequestAsync); + ipcCtx->AddInput(&requestIdBuf, sizeof(uint64be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } void load() { - osLib_addFunction("nn_fp", "Initialize__Q2_2nn2fpFv", export_Initialize); - osLib_addFunction("nn_fp", "InitializeAdmin__Q2_2nn2fpFv", export_InitializeAdmin); - osLib_addFunction("nn_fp", "IsInitialized__Q2_2nn2fpFv", export_IsInitialized); - osLib_addFunction("nn_fp", "IsInitializedAdmin__Q2_2nn2fpFv", export_IsInitializedAdmin); + g_fp.initCounter = 0; + g_fp.isAdminMode = false; + g_fp.isLoggedIn = false; + g_fp.getNotificationCalled = false; + g_fp.notificationHandler = nullptr; + g_fp.notificationHandlerParam = nullptr; - osLib_addFunction("nn_fp", "SetNotificationHandler__Q2_2nn2fpFUiPFQ3_2nn2fp16NotificationTypeUiPv_vPv", export_SetNotificationHandler); + coreinit::OSInitMutex(&g_fp.fpMutex); + FPIpcBufferAllocator.Init(); - osLib_addFunction("nn_fp", "LoginAsync__Q2_2nn2fpFPFQ2_2nn6ResultPv_vPv", export_LoginAsync); - osLib_addFunction("nn_fp", "HasLoggedIn__Q2_2nn2fpFv", export_HasLoggedIn); + cafeExportRegisterFunc(Initialize, "nn_fp", "Initialize__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(InitializeAdmin, "nn_fp", "InitializeAdmin__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(IsInitialized, "nn_fp", "IsInitialized__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(IsInitializedAdmin, "nn_fp", "IsInitializedAdmin__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(Finalize, "nn_fp", "Finalize__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(FinalizeAdmin, "nn_fp", "FinalizeAdmin__Q2_2nn2fpFv", LogType::NN_FP); - osLib_addFunction("nn_fp", "IsOnline__Q2_2nn2fpFv", export_IsOnline); + cafeExportRegisterFunc(SetNotificationHandler, "nn_fp", "SetNotificationHandler__Q2_2nn2fpFUiPFQ3_2nn2fp16NotificationTypeUiPv_vPv", LogType::NN_FP); - osLib_addFunction("nn_fp", "GetFriendList__Q2_2nn2fpFPUiT1UiT3", export_GetFriendList); - osLib_addFunction("nn_fp", "GetFriendRequestList__Q2_2nn2fpFPUiT1UiT3", export_GetFriendRequestList); - osLib_addFunction("nn_fp", "GetFriendListAll__Q2_2nn2fpFPUiT1UiT3", export_GetFriendListAll); - osLib_addFunction("nn_fp", "GetFriendListEx__Q2_2nn2fpFPQ3_2nn2fp10FriendDataPCUiUi", export_GetFriendListEx); - osLib_addFunction("nn_fp", "GetFriendRequestListEx__Q2_2nn2fpFPQ3_2nn2fp13FriendRequestPCUiUi", export_GetFriendRequestListEx); - osLib_addFunction("nn_fp", "GetBasicInfoAsync__Q2_2nn2fpFPQ3_2nn2fp9BasicInfoPCUiUiPFQ2_2nn6ResultPv_vPv", export_GetBasicInfoAsync); + cafeExportRegisterFunc(LoginAsync, "nn_fp", "LoginAsync__Q2_2nn2fpFPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(HasLoggedIn, "nn_fp", "HasLoggedIn__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(IsOnline, "nn_fp", "IsOnline__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendList, "nn_fp", "GetFriendList__Q2_2nn2fpFPUiT1UiT3", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendRequestList, "nn_fp", "GetFriendRequestList__Q2_2nn2fpFPUiT1UiT3", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendListAll, "nn_fp", "GetFriendListAll__Q2_2nn2fpFPUiT1UiT3", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendListEx, "nn_fp", "GetFriendListEx__Q2_2nn2fpFPQ3_2nn2fp10FriendDataPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendRequestListEx, "nn_fp", "GetFriendRequestListEx__Q2_2nn2fpFPQ3_2nn2fp13FriendRequestPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetBasicInfoAsync, "nn_fp", "GetBasicInfoAsync__Q2_2nn2fpFPQ3_2nn2fp9BasicInfoPCUiUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); - osLib_addFunction("nn_fp", "GetMyPrincipalId__Q2_2nn2fpFv", export_GetMyPrincipalId); - osLib_addFunction("nn_fp", "GetMyAccountId__Q2_2nn2fpFPc", export_GetMyAccountId); - osLib_addFunction("nn_fp", "GetMyScreenName__Q2_2nn2fpFPw", export_GetMyScreenName); - osLib_addFunction("nn_fp", "GetMyMii__Q2_2nn2fpFP12FFLStoreData", export_GetMyMii); - osLib_addFunction("nn_fp", "GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference", export_GetMyPreference); + cafeExportRegisterFunc(GetMyPrincipalId, "nn_fp", "GetMyPrincipalId__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(GetMyAccountId, "nn_fp", "GetMyAccountId__Q2_2nn2fpFPc", LogType::NN_FP); + cafeExportRegisterFunc(GetMyScreenName, "nn_fp", "GetMyScreenName__Q2_2nn2fpFPw", LogType::NN_FP); + cafeExportRegisterFunc(GetMyMii, "nn_fp", "GetMyMii__Q2_2nn2fpFP12FFLStoreData", LogType::NN_FP); + cafeExportRegisterFunc(GetMyPreference, "nn_fp", "GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference", LogType::NN_FP); - osLib_addFunction("nn_fp", "GetFriendAccountId__Q2_2nn2fpFPA17_cPCUiUi", export_GetFriendAccountId); - osLib_addFunction("nn_fp", "GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc", export_GetFriendScreenName); - osLib_addFunction("nn_fp", "GetFriendMii__Q2_2nn2fpFP12FFLStoreDataPCUiUi", export_GetFriendMii); - osLib_addFunction("nn_fp", "GetFriendPresence__Q2_2nn2fpFPQ3_2nn2fp14FriendPresencePCUiUi", export_GetFriendPresence); - osLib_addFunction("nn_fp", "GetFriendRelationship__Q2_2nn2fpFPUcPCUiUi", export_GetFriendRelationship); - osLib_addFunction("nn_fp", "IsJoinable__Q2_2nn2fpFPCQ3_2nn2fp14FriendPresenceUL", export_IsJoinable); + cafeExportRegisterFunc(GetFriendAccountId, "nn_fp", "GetFriendAccountId__Q2_2nn2fpFPA17_cPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendScreenName, "nn_fp", "GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendMii, "nn_fp", "GetFriendMii__Q2_2nn2fpFP12FFLStoreDataPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendPresence, "nn_fp", "GetFriendPresence__Q2_2nn2fpFPQ3_2nn2fp14FriendPresencePCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendRelationship, "nn_fp", "GetFriendRelationship__Q2_2nn2fpFPUcPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(IsJoinable, "nn_fp", "IsJoinable__Q2_2nn2fpFPCQ3_2nn2fp14FriendPresenceUL", LogType::NN_FP); - osLib_addFunction("nn_fp", "CheckSettingStatusAsync__Q2_2nn2fpFPUcPFQ2_2nn6ResultPv_vPv", export_CheckSettingStatusAsync); - osLib_addFunction("nn_fp", "IsPreferenceValid__Q2_2nn2fpFv", export_IsPreferenceValid); - osLib_addFunction("nn_fp", "UpdatePreferenceAsync__Q2_2nn2fpFPCQ3_2nn2fp10PreferencePFQ2_2nn6ResultPv_vPv", export_UpdatePreferenceAsync); + cafeExportRegisterFunc(CheckSettingStatusAsync, "nn_fp", "CheckSettingStatusAsync__Q2_2nn2fpFPUcPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(IsPreferenceValid, "nn_fp", "IsPreferenceValid__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(UpdatePreferenceAsync, "nn_fp", "UpdatePreferenceAsync__Q2_2nn2fpFPCQ3_2nn2fp10PreferencePFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(GetRequestBlockSettingAsync, "nn_fp", "GetRequestBlockSettingAsync__Q2_2nn2fpFPUcPCUiUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); - osLib_addFunction("nn_fp", "UpdateGameMode__Q2_2nn2fpFPCQ3_2nn2fp8GameModePCwUi", export_UpdateGameMode); - - osLib_addFunction("nn_fp", "GetRequestBlockSettingAsync__Q2_2nn2fpFPUcPCUiUiPFQ2_2nn6ResultPv_vPv", export_GetRequestBlockSettingAsync); - - osLib_addFunction("nn_fp", "AddFriendAsync__Q2_2nn2fpFPCcPFQ2_2nn6ResultPv_vPv", export_AddFriendAsync); - osLib_addFunction("nn_fp", "AddFriendRequestAsync__Q2_2nn2fpFPCQ3_2nn2fp18RecentPlayRecordExPCwPFQ2_2nn6ResultPv_vPv", export_AddFriendRequestAsync); - osLib_addFunction("nn_fp", "DeleteFriendFlagsAsync__Q2_2nn2fpFPCUiUiT2PFQ2_2nn6ResultPv_vPv", export_DeleteFriendFlagsAsync); - - osLib_addFunction("nn_fp", "RemoveFriendAsync__Q2_2nn2fpFUiPFQ2_2nn6ResultPv_vPv", export_RemoveFriendAsync); - osLib_addFunction("nn_fp", "MarkFriendRequestsAsReceivedAsync__Q2_2nn2fpFPCULUiPFQ2_2nn6ResultPv_vPv", export_MarkFriendRequestsAsReceivedAsync); - osLib_addFunction("nn_fp", "CancelFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", export_CancelFriendRequestAsync); - osLib_addFunction("nn_fp", "AcceptFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", export_AcceptFriendRequestAsync); + cafeExportRegisterFunc(UpdateGameModeWithUnusedParam, "nn_fp", "UpdateGameMode__Q2_2nn2fpFPCQ3_2nn2fp8GameModePCwUi", LogType::NN_FP); + cafeExportRegisterFunc(UpdateGameMode, "nn_fp", "UpdateGameMode__Q2_2nn2fpFPCQ3_2nn2fp8GameModePCw", LogType::NN_FP); + cafeExportRegisterFunc(AddFriendAsyncByPid, "nn_fp", "AddFriendAsync__Q2_2nn2fpFUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(AddFriendRequestByPlayRecordAsync, "nn_fp", "AddFriendRequestAsync__Q2_2nn2fpFPCQ3_2nn2fp18RecentPlayRecordExPCwPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(DeleteFriendFlagsAsync, "nn_fp", "DeleteFriendFlagsAsync__Q2_2nn2fpFPCUiUiT2PFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(RemoveFriendAsync, "nn_fp", "RemoveFriendAsync__Q2_2nn2fpFUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(MarkFriendRequestsAsReceivedAsync, "nn_fp", "MarkFriendRequestsAsReceivedAsync__Q2_2nn2fpFPCULUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(CancelFriendRequestAsync, "nn_fp", "CancelFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(DeleteFriendRequestAsync, "nn_fp", "DeleteFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(AcceptFriendRequestAsync, "nn_fp", "AcceptFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); } } } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp index 8df14ce0..1bf2b37d 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp @@ -47,10 +47,10 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, DownloadCommunityDataList_AsyncRequest, std::ref(req), reqUrl, requestDoneEvent.GetPointer(), pOutList, pOutNum, numMaxList, pParam); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp index 0ae581e0..5e6dba7e 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp @@ -199,9 +199,9 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, MakeDiscoveryRequest_AsyncRequest, std::ref(req), requestUrl.c_str(), requestDoneEvent.GetPointer()); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp index e6cea082..309394e6 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp @@ -25,7 +25,7 @@ namespace nn // open archive g_offlineDBArchive = ZArchiveReader::OpenFromFile(ActiveSettings::GetUserDataPath("resources/miiverse/OfflineDB.zar")); if(!g_offlineDBArchive) - cemuLog_log(LogType::Force, "Failed to open resources/miiverse/OfflineDB.zar. Miiverse posts will not be available"); + cemuLog_log(LogType::Force, "Offline miiverse posts are not available"); g_offlineDBInitialized = true; } @@ -175,9 +175,9 @@ namespace nn return OLV_RESULT_SUCCESS; // the offlineDB doesn't contain any self posts StackAllocator doneEvent; - coreinit::OSInitEvent(doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); auto asyncTask = std::async(std::launch::async, _Async_OfflineDB_DownloadPostDataListParam_DownloadPostDataList, doneEvent.GetPointer(), downloadedTopicData, downloadedPostData, postCountOut, maxCount, param); - coreinit::OSWaitEvent(doneEvent); + coreinit::OSWaitEvent(&doneEvent); nnResult r = asyncTask.get(); return r; } @@ -204,9 +204,9 @@ namespace nn nnResult OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize) { StackAllocator doneEvent; - coreinit::OSInitEvent(doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); auto asyncTask = std::async(std::launch::async, _Async_OfflineDB_DownloadPostDataListParam_DownloadExternalImageData, doneEvent.GetPointer(), _this, imageDataOut, imageSizeOut, maxSize); - coreinit::OSWaitEvent(doneEvent); + coreinit::OSWaitEvent(&doneEvent); nnResult r = asyncTask.get(); return r; } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp index b76e6d63..179d66bd 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp @@ -54,9 +54,9 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, UploadCommunityData_AsyncRequest, std::ref(req), requestUrl, requestDoneEvent.GetPointer(), pOutData, pParam); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp index 7d9220fc..307004b9 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp @@ -44,9 +44,9 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, UploadFavoriteToCommunityData_AsyncRequest, std::ref(req), requestUrl, requestDoneEvent.GetPointer(), pOutData, pParam); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index f8ce7265..6d596acf 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -43,6 +43,7 @@ const std::map g_logging_window_mapping {LogType::CoreinitMP, "Coreinit MP"}, {LogType::CoreinitThread, "Coreinit Thread"}, {LogType::NN_NFP, "nn::nfp"}, + {LogType::NN_FP, "nn::fp"}, {LogType::GX2, "GX2"}, {LogType::SoundAPI, "Audio"}, {LogType::InputAPI, "Input"}, diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index 7d6499fe..728c8b93 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -34,6 +34,7 @@ enum class LogType : sint32 NN_PDM = 21, NN_OLV = 23, NN_NFP = 13, + NN_FP = 24, TextureReadback = 29, diff --git a/src/Cemu/napi/napi_act.cpp b/src/Cemu/napi/napi_act.cpp index b395e2b7..9716c41e 100644 --- a/src/Cemu/napi/napi_act.cpp +++ b/src/Cemu/napi/napi_act.cpp @@ -358,7 +358,7 @@ namespace NAPI std::string_view port = tokenNode.child_value("port"); std::string_view token = tokenNode.child_value("token"); - std::memset(&result.nexToken, 0, sizeof(result.nexToken)); + memset(&result.nexToken, 0, sizeof(ACTNexToken)); if (host.size() > 15) cemuLog_log(LogType::Force, "NexToken response: host field too long"); if (nex_password.size() > 64) diff --git a/src/Cemu/nex/nex.cpp b/src/Cemu/nex/nex.cpp index 317b3877..d0857507 100644 --- a/src/Cemu/nex/nex.cpp +++ b/src/Cemu/nex/nex.cpp @@ -160,11 +160,10 @@ bool nexService::isMarkedForDestruction() void nexService::callMethod(uint8 protocolId, uint32 methodId, nexPacketBuffer* parameter, void(*nexServiceResponse)(nexService* nex, nexServiceResponse_t* serviceResponse), void* custom, bool callHandlerIfError) { - // add to queue queuedRequest_t queueRequest = { 0 }; queueRequest.protocolId = protocolId; queueRequest.methodId = methodId; - queueRequest.parameterData = std::vector(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); + queueRequest.parameterData.assign(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); queueRequest.nexServiceResponse = nexServiceResponse; queueRequest.custom = custom; queueRequest.callHandlerIfError = callHandlerIfError; @@ -175,11 +174,10 @@ void nexService::callMethod(uint8 protocolId, uint32 methodId, nexPacketBuffer* void nexService::callMethod(uint8 protocolId, uint32 methodId, nexPacketBuffer* parameter, std::function cb, bool callHandlerIfError) { - // add to queue queuedRequest_t queueRequest = { 0 }; queueRequest.protocolId = protocolId; queueRequest.methodId = methodId; - queueRequest.parameterData = std::vector(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); + queueRequest.parameterData.assign(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); queueRequest.nexServiceResponse = nullptr; queueRequest.cb2 = cb; queueRequest.callHandlerIfError = callHandlerIfError; diff --git a/src/Cemu/nex/nexFriends.cpp b/src/Cemu/nex/nexFriends.cpp index cf169b72..4fae8143 100644 --- a/src/Cemu/nex/nexFriends.cpp +++ b/src/Cemu/nex/nexFriends.cpp @@ -274,8 +274,7 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response return; } NexFriends* session = (NexFriends*)nexFriends; - - nexPrincipalPreference preference(&response->data); + session->myPreference = nexPrincipalPreference(&response->data); nexComment comment(&response->data); if (response->data.hasReadOutOfBounds()) return; @@ -290,29 +289,21 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response uint32 friendCount = response->data.readU32(); session->list_friends.resize(friendCount); for (uint32 i = 0; i < friendCount; i++) - { session->list_friends[i].readData(&response->data); - } // friend requests (outgoing) uint32 friendRequestsOutCount = response->data.readU32(); if (response->data.hasReadOutOfBounds()) - { return; - } session->list_friendReqOutgoing.resize(friendRequestsOutCount); for (uint32 i = 0; i < friendRequestsOutCount; i++) - { session->list_friendReqOutgoing[i].readData(&response->data); - } // friend requests (incoming) uint32 friendRequestsInCount = response->data.readU32(); if (response->data.hasReadOutOfBounds()) return; session->list_friendReqIncoming.resize(friendRequestsInCount); for (uint32 i = 0; i < friendRequestsInCount; i++) - { session->list_friendReqIncoming[i].readData(&response->data); - } if (response->data.hasReadOutOfBounds()) return; // blacklist @@ -336,7 +327,7 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response if (isPreferenceInvalid) { cemuLog_log(LogType::Force, "NEX: First time login into friend account, setting up default preferences"); - session->updatePreferences(nexPrincipalPreference(1, 1, 0)); + session->updatePreferencesAsync(nexPrincipalPreference(1, 1, 0), [](RpcErrorCode err){}); } if (session->firstInformationRequest == false) @@ -377,20 +368,27 @@ bool NexFriends::requestGetAllInformation(std::function cb) return true; } -void NexFriends::handleResponse_updatePreferences(nexServiceResponse_t* response, NexFriends* nexFriends, std::function cb) -{ - // todo -} - -bool NexFriends::updatePreferences(const nexPrincipalPreference& newPreferences) +bool NexFriends::updatePreferencesAsync(nexPrincipalPreference newPreferences, std::function cb) { uint8 tempNexBufferArray[1024]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); newPreferences.writeData(&packetBuffer); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 16, &packetBuffer, std::bind(handleResponse_updatePreferences, std::placeholders::_1, this, nullptr), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 16, &packetBuffer, [this, cb, newPreferences](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + this->myPreference = newPreferences; + return cb(NexFriends::ERR_NONE); + }, true); + // TEST return true; } +void NexFriends::getMyPreference(nexPrincipalPreference& preference) +{ + preference = myPreference; +} + bool NexFriends::addProvisionalFriendByPidGuessed(uint32 principalId) { uint8 tempNexBufferArray[512]; @@ -401,6 +399,7 @@ bool NexFriends::addProvisionalFriendByPidGuessed(uint32 principalId) return true; } +// returns true once connection is established and friend list data is available bool NexFriends::isOnline() { return isCurrentlyConnected && hasData; @@ -683,7 +682,7 @@ bool NexFriends::getFriendRequestByPID(nexFriendRequest& friendRequestData, bool { friendRequestData = it; if (isIncoming) - *isIncoming = false; + *isIncoming = false; return true; } } @@ -731,7 +730,7 @@ void addProvisionalFriendHandler(nexServiceResponse_t* nexResponse, std::functio } } -bool NexFriends::addProvisionalFriend(char* name, std::function cb) +bool NexFriends::addProvisionalFriend(char* name, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -754,12 +753,11 @@ void addFriendRequestHandler(nexServiceResponse_t* nexResponse, NexFriends* nexF { // todo: Properly handle returned error code cb(NexFriends::ERR_RPC_FAILED); - // refresh the list - nexFriends->requestGetAllInformation(); + nexFriends->requestGetAllInformation(); // refresh friend list and send add/remove notifications } } -void NexFriends::addFriendRequest(uint32 pid, const char* comment, std::function cb) +void NexFriends::addFriendRequest(uint32 pid, const char* comment, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -779,72 +777,31 @@ void NexFriends::addFriendRequest(uint32 pid, const char* comment, std::function nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 5, &packetBuffer, std::bind(addFriendRequestHandler, std::placeholders::_1, this, cb), true); } -typedef struct -{ - NEXFRIENDS_CALLBACK cb; - void* customParam; - NexFriends* nexFriends; - // command specific - struct - { - nexPrincipalBasicInfo* basicInfo; - sint32 count; - }principalBaseInfo; -}nexFriendsCallInfo_t; - -void NexFriends_handleResponse_requestPrincipleBaseInfoByPID(nexService* nex, nexServiceResponse_t* response) -{ - nexFriendsCallInfo_t* callInfo = (nexFriendsCallInfo_t*)response->custom; - if (response->isSuccessful == false) - { - // handle error case - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_RPC_FAILED, callInfo->customParam); - free(callInfo); - return; - } - // process result - uint32 count = response->data.readU32(); - if (count != callInfo->principalBaseInfo.count) - { - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_UNEXPECTED_RESULT, callInfo->customParam); - free(callInfo); - return; - } - for (uint32 i = 0; i < count; i++) - { - callInfo->principalBaseInfo.basicInfo[i].readData(&response->data); - } - if (response->data.hasReadOutOfBounds()) - { - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_UNEXPECTED_RESULT, callInfo->customParam); - free(callInfo); - return; - } - // callback - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_NONE, callInfo->customParam); - free(callInfo); -} - -void NexFriends::requestPrincipleBaseInfoByPID(nexPrincipalBasicInfo* basicInfo, uint32* pidList, sint32 count, NEXFRIENDS_CALLBACK cb, void* customParam) +void NexFriends::requestPrincipleBaseInfoByPID(uint32* pidList, sint32 count, const std::function basicInfo)>& cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) - { - // not connected - cb(this, ERR_NOT_CONNECTED, customParam); - return; - } + return cb(ERR_NOT_CONNECTED, {}); uint8 tempNexBufferArray[512]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(count); for(sint32 i=0; iprincipalBaseInfo.basicInfo = basicInfo; - callInfo->principalBaseInfo.count = count; - callInfo->cb = cb; - callInfo->customParam = customParam; - callInfo->nexFriends = this; - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 17, &packetBuffer, NexFriends_handleResponse_requestPrincipleBaseInfoByPID, callInfo, true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 17, &packetBuffer, [cb, count](nexServiceResponse_t* response) + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED, {}); + // process result + uint32 resultCount = response->data.readU32(); + if (resultCount != count) + return cb(NexFriends::ERR_UNEXPECTED_RESULT, {}); + std::vector nexBasicInfo; + nexBasicInfo.resize(count); + for (uint32 i = 0; i < resultCount; i++) + nexBasicInfo[i].readData(&response->data); + if (response->data.hasReadOutOfBounds()) + return cb(NexFriends::ERR_UNEXPECTED_RESULT, {}); + return cb(NexFriends::ERR_NONE, nexBasicInfo); + }, true); } void genericFriendServiceNoResponseHandler(nexServiceResponse_t* nexResponse, std::function cb) @@ -858,7 +815,7 @@ void genericFriendServiceNoResponseHandler(nexServiceResponse_t* nexResponse, st } } -void NexFriends::removeFriend(uint32 pid, std::function cb) +void NexFriends::removeFriend(uint32 pid, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -869,10 +826,20 @@ void NexFriends::removeFriend(uint32 pid, std::function cb) uint8 tempNexBufferArray[512]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(pid); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, std::bind(genericFriendServiceNoResponseHandler, std::placeholders::_1, cb), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, [this, cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + { + cb(NexFriends::ERR_NONE); + this->requestGetAllInformation(); // refresh friend list and send add/remove notifications + return; + } + }, true); } -void NexFriends::cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb) +void NexFriends::cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -883,53 +850,63 @@ void NexFriends::cancelOutgoingProvisionalFriendRequest(uint32 pid, std::functio uint8 tempNexBufferArray[512]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(pid); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, std::bind(genericFriendServiceNoResponseHandler, std::placeholders::_1, cb), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, [cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + return cb(NexFriends::ERR_NONE); + }, true); } -void NexFriends::acceptFriendRequest(uint64 messageId, std::function cb) +void NexFriends::acceptFriendRequest(uint64 messageId, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) - { - // not connected - cb(ERR_NOT_CONNECTED); - return; - } + return cb(ERR_NOT_CONNECTED); uint8 tempNexBufferArray[128]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU64(messageId); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 7, &packetBuffer, std::bind(genericFriendServiceNoResponseHandler, std::placeholders::_1, cb), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 7, &packetBuffer, [cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + return cb(NexFriends::ERR_NONE); + }, true); } -void markFriendRequestsAsReceivedHandler(nexServiceResponse_t* nexResponse, std::function cb) -{ - if (nexResponse->isSuccessful) - cb(0); - else - { - // todo: Properly handle returned error code - cb(NexFriends::ERR_RPC_FAILED); - } -} - -void NexFriends::markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb) +void NexFriends::deleteFriendRequest(uint64 messageId, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) - { - // not connected - cb(ERR_NOT_CONNECTED); - return; - } + return cb(ERR_NOT_CONNECTED); + uint8 tempNexBufferArray[128]; + nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); + packetBuffer.writeU64(messageId); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 8, &packetBuffer, [this, cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + cb(NexFriends::ERR_NONE); + this->requestGetAllInformation(); // refresh friend list and send add/remove notifications + }, true); +} + +void NexFriends::markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb) +{ + if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) + return cb(ERR_NOT_CONNECTED); uint8 tempNexBufferArray[1024]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(count); for(sint32 i=0; icallMethod(NEX_PROTOCOL_FRIENDS_WIIU, 10, &packetBuffer, std::bind(markFriendRequestsAsReceivedHandler, std::placeholders::_1, cb), true); -} - -void genericFriendServiceNoResponseHandlerWithoutCB(nexServiceResponse_t* nexResponse) -{ - + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 10, &packetBuffer, [cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + return cb(NexFriends::ERR_NONE); + }, true); } void NexFriends::updateMyPresence(nexPresenceV2& myPresence) @@ -943,7 +920,7 @@ void NexFriends::updateMyPresence(nexPresenceV2& myPresence) uint8 tempNexBufferArray[1024]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); myPresence.writeData(&packetBuffer); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 13, &packetBuffer, std::bind(genericFriendServiceNoResponseHandlerWithoutCB, std::placeholders::_1), false); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 13, &packetBuffer, +[](nexServiceResponse_t* nexResponse){}, false); } void NexFriends::update() diff --git a/src/Cemu/nex/nexFriends.h b/src/Cemu/nex/nexFriends.h index 16548bf3..06c75110 100644 --- a/src/Cemu/nex/nexFriends.h +++ b/src/Cemu/nex/nexFriends.h @@ -248,9 +248,9 @@ public: nexPrincipalPreference(uint8 ukn0, uint8 ukn1, uint8 ukn2) { - this->ukn0 = ukn0; - this->ukn1 = ukn1; - this->ukn2 = ukn2; + this->showOnline = ukn0; + this->showGame = ukn1; + this->blockFriendRequests = ukn2; } nexPrincipalPreference(nexPacketBuffer* pb) @@ -260,21 +260,21 @@ public: void writeData(nexPacketBuffer* pb) const override { - pb->writeU8(ukn0); - pb->writeU8(ukn1); - pb->writeU8(ukn2); + pb->writeU8(showOnline); + pb->writeU8(showGame); + pb->writeU8(blockFriendRequests); } void readData(nexPacketBuffer* pb) override { - ukn0 = pb->readU8(); - ukn1 = pb->readU8(); - ukn2 = pb->readU8(); + showOnline = pb->readU8(); + showGame = pb->readU8(); + blockFriendRequests = pb->readU8(); } public: - uint8 ukn0; - uint8 ukn1; - uint8 ukn2; + uint8 showOnline; + uint8 showGame; + uint8 blockFriendRequests; }; class nexComment : public nexType @@ -505,13 +505,12 @@ public: uint32 type; std::string msg; }; -class NexFriends; - -typedef void (*NEXFRIENDS_CALLBACK)(NexFriends* nexFriends, uint32 result, void* custom); class NexFriends { public: + using RpcErrorCode = int; // replace with enum class later + static const int ERR_NONE = 0; static const int ERR_RPC_FAILED = 1; static const int ERR_UNEXPECTED_RESULT = 2; @@ -544,27 +543,29 @@ public: int getPendingFriendRequestCount(); bool requestGetAllInformation(); - bool requestGetAllInformation(std::function cb); bool addProvisionalFriendByPidGuessed(uint32 principalId); - void acceptFriendRequest(uint64 messageId, std::function cb); - bool isOnline(); + // synchronous API (returns immediately) + bool requestGetAllInformation(std::function cb); void getFriendPIDs(uint32* pidList, uint32* pidCount, sint32 offset, sint32 count, bool includeFriendRequests); void getFriendRequestPIDs(uint32* pidList, uint32* pidCount, sint32 offset, sint32 count, bool includeIncoming, bool includeOutgoing); bool getFriendByPID(nexFriend& friendData, uint32 pid); bool getFriendRequestByPID(nexFriendRequest& friendRequestData, bool* isIncoming, uint32 searchedPid); bool getFriendRequestByMessageId(nexFriendRequest& friendRequestData, bool* isIncoming, uint64 messageId); + bool isOnline(); + void getMyPreference(nexPrincipalPreference& preference); - bool addProvisionalFriend(char* name, std::function cb); - void addFriendRequest(uint32 pid, const char* comment, std::function cb); - - void requestPrincipleBaseInfoByPID(nexPrincipalBasicInfo* basicInfo, uint32* pidList, sint32 count, NEXFRIENDS_CALLBACK cb, void* customParam); - void removeFriend(uint32 pid, std::function cb); - void cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb); - void markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb); - + // asynchronous API (data has to be requested) + bool addProvisionalFriend(char* name, std::function cb); + void addFriendRequest(uint32 pid, const char* comment, std::function cb); + void requestPrincipleBaseInfoByPID(uint32* pidList, sint32 count, const std::function basicInfo)>& cb); + void removeFriend(uint32 pid, std::function cb); + void cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb); + void markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb); + void acceptFriendRequest(uint64 messageId, std::function cb); + void deleteFriendRequest(uint64 messageId, std::function cb); // rejecting incoming friend request (differs from blocking friend requests) + bool updatePreferencesAsync(const nexPrincipalPreference newPreferences, std::function cb); void updateMyPresence(nexPresenceV2& myPresence); - bool updatePreferences(const nexPrincipalPreference& newPreferences); void setNotificationHandler(void(*notificationHandler)(NOTIFICATION_TYPE notificationType, uint32 pid)); @@ -578,7 +579,6 @@ private: static void handleResponse_acceptFriendRequest(nexService* nex, nexServiceResponse_t* response); static void handleResponse_getAllInformation(nexServiceResponse_t* response, NexFriends* nexFriends, std::function cb); - static void handleResponse_updatePreferences(nexServiceResponse_t* response, NexFriends* nexFriends, std::function cb); void generateNotification(NOTIFICATION_TYPE notificationType, uint32 pid); void trackNotifications(); @@ -618,6 +618,7 @@ private: }auth; // local friend state nexPresenceV2 myPresence; + nexPrincipalPreference myPreference; std::recursive_mutex mtx_lists; std::vector list_friends; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 7ed3d67a..9a764593 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(CemuCommon StackAllocator.h SysAllocator.cpp SysAllocator.h + CafeString.h version.h ) diff --git a/src/Common/CafeString.h b/src/Common/CafeString.h new file mode 100644 index 00000000..45a515b1 --- /dev/null +++ b/src/Common/CafeString.h @@ -0,0 +1,73 @@ +#pragma once +#include "betype.h" +#include "util/helpers/StringHelpers.h" + +/* Helper classes to represent CafeOS strings in emulated memory */ +template +class CafeString // fixed buffer size, null-terminated, PPC char +{ + public: + bool assign(std::string_view sv) + { + if (sv.size()+1 >= N) + { + memcpy(data, sv.data(), sv.size()-1); + data[sv.size()-1] = '\0'; + return false; + } + memcpy(data, sv.data(), sv.size()); + data[sv.size()] = '\0'; + return true; + } + + uint8be data[N]; +}; + +template +class CafeWideString // fixed buffer size, null-terminated, PPC wchar_t (16bit big-endian) +{ + public: + bool assign(const uint16be* input) + { + size_t i = 0; + while(input[i]) + { + if(i >= N-1) + { + data[N-1] = 0; + return false; + } + data[i] = input[i]; + i++; + } + data[i] = 0; + return true; + } + + bool assignFromUTF8(std::string_view sv) + { + std::basic_string beStr = StringHelpers::FromUtf8(sv); + if(beStr.length() > N-1) + { + memcpy(data, beStr.data(), (N-1)*sizeof(uint16be)); + data[N-1] = 0; + return false; + } + memcpy(data, beStr.data(), beStr.length()*sizeof(uint16be)); + data[beStr.length()] = '\0'; + return true; + } + + uint16be data[N]; +}; + +namespace CafeStringHelpers +{ + static uint32 Length(const uint16be* input, uint32 maxLength) + { + uint32 i = 0; + while(input[i] && i < maxLength) + i++; + return i; + } +}; diff --git a/src/Common/StackAllocator.h b/src/Common/StackAllocator.h index 750db13f..a69b7aaa 100644 --- a/src/Common/StackAllocator.h +++ b/src/Common/StackAllocator.h @@ -29,9 +29,36 @@ public: T* GetPointer() const { return m_ptr; } uint32 GetMPTR() const { return MEMPTR(m_ptr).GetMPTR(); } uint32 GetMPTRBE() const { return MEMPTR(m_ptr).GetMPTRBE(); } - - operator T*() const { return GetPointer(); } + + T* operator&() { return GetPointer(); } + explicit operator T*() const { return GetPointer(); } explicit operator uint32() const { return GetMPTR(); } + explicit operator bool() const { return *m_ptr != 0; } + + // for arrays (count > 1) allow direct access via [] operator + template + requires (c > 1) + T& operator[](const uint32 index) + { + return m_ptr[index]; + } + + // if count is 1, then allow direct value assignment via = operator + template + requires (c == 1) + T& operator=(const T& rhs) + { + *m_ptr = rhs; + return *m_ptr; + } + + // if count is 1, then allow == and != operators + template + requires (c == 1) + bool operator==(const T& rhs) const + { + return *m_ptr == rhs; + } private: static const uint32 kStaticMemOffset = 64; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c0d975ec..3156f2de 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -2198,6 +2198,7 @@ void MainWindow::RecreateMenu() debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitMP), _("&Coreinit MP API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitMP)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThread), _("&Coreinit Thread API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThread)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_NFP), _("&NN NFP"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::NN_NFP)); + debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_FP), _("&NN FP"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::NN_FP)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::GX2), _("&GX2 API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::GX2)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SoundAPI), _("&Audio API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::SoundAPI)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::InputAPI), _("&Input API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::InputAPI)); diff --git a/src/util/helpers/StringHelpers.h b/src/util/helpers/StringHelpers.h index 24e70d49..54141808 100644 --- a/src/util/helpers/StringHelpers.h +++ b/src/util/helpers/StringHelpers.h @@ -2,6 +2,7 @@ #include "boost/nowide/convert.hpp" #include +// todo - move the Cafe/PPC specific parts to CafeString.h eventually namespace StringHelpers { // convert Wii U big-endian wchar_t string to utf8 string diff --git a/src/util/highresolutiontimer/HighResolutionTimer.h b/src/util/highresolutiontimer/HighResolutionTimer.h index 12dc0751..7a545c86 100644 --- a/src/util/highresolutiontimer/HighResolutionTimer.h +++ b/src/util/highresolutiontimer/HighResolutionTimer.h @@ -36,6 +36,16 @@ public: static HighResolutionTimer now(); static HRTick getFrequency(); + static HRTick microsecondsToTicks(uint64 microseconds) + { + return microseconds * m_freq / 1000000; + } + + static uint64 ticksToMicroseconds(HRTick ticks) + { + return ticks * 1000000 / m_freq; + } + private: HighResolutionTimer(uint64 timePoint) : m_timePoint(timePoint) {}; From 2959802ae25cf026a6f8ea08b81b02282b650687 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:24:59 +0200 Subject: [PATCH 060/101] Use utf-8 for exe path --- src/gui/CemuApp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 53a42a10..f48957df 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -58,7 +58,7 @@ bool CemuApp::OnInit() { fs::path user_data_path, config_path, cache_path, data_path; auto standardPaths = wxStandardPaths::Get(); - fs::path exePath(standardPaths.GetExecutablePath().ToStdString()); + fs::path exePath(wxHelper::MakeFSPath(standardPaths.GetExecutablePath())); #ifdef PORTABLE #if MACOS_BUNDLE exePath = exePath.parent_path().parent_path().parent_path(); @@ -88,7 +88,7 @@ bool CemuApp::OnInit() #endif auto failed_write_access = ActiveSettings::LoadOnce(exePath, user_data_path, config_path, cache_path, data_path); for (auto&& path : failed_write_access) - wxMessageBox(formatWxString(_("Cemu can't write to {}!"), path.generic_string()), + wxMessageBox(formatWxString(_("Cemu can't write to {}!"), wxString::FromUTF8(_pathToUtf8(path))), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); NetworkConfig::LoadOnce(); From c440ecdf36495008541e7ea39bd2000f82cec7a7 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 17 Oct 2023 06:16:29 +0200 Subject: [PATCH 061/101] FPD: Fix a crash due to incorrect instantiation --- src/Cafe/IOSU/legacy/iosu_fpd.cpp | 18 +++++++++--------- src/Cemu/nex/nexTypes.h | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index 75bf0463..bcd580ef 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -173,7 +173,7 @@ namespace iosu return t; } - void NexPresenceToGameMode(nexPresenceV2* presence, GameMode* gameMode) + void NexPresenceToGameMode(const nexPresenceV2* presence, GameMode* gameMode) { memset(gameMode, 0, sizeof(GameMode)); gameMode->joinFlagMask = presence->joinFlagMask; @@ -185,9 +185,9 @@ namespace iosu memcpy(gameMode->appSpecificData, presence->appSpecificData, 0x14); } - void GameModeToNexPresence(GameMode* gameMode, nexPresenceV2* presence) + void GameModeToNexPresence(const GameMode* gameMode, nexPresenceV2* presence) { - memset(presence, 0, sizeof(nexPresenceV2)); + *presence = {}; presence->joinFlagMask = gameMode->joinFlagMask; presence->joinAvailability = (uint8)(uint32)gameMode->matchmakeType; presence->gameId = gameMode->joinGameId; @@ -197,7 +197,7 @@ namespace iosu memcpy(presence->appSpecificData, gameMode->appSpecificData, 0x14); } - void NexFriendToFPDFriendData(FriendData* friendData, nexFriend* frd) + void NexFriendToFPDFriendData(const nexFriend* frd, FriendData* friendData) { memset(friendData, 0, sizeof(FriendData)); // setup friend data @@ -232,7 +232,7 @@ namespace iosu convertFPDTimestampToDate(frd->lastOnlineTimestamp, &friendData->friendExtraData.lastOnline); } - void NexFriendRequestToFPDFriendData(FriendData* friendData, nexFriendRequest* frdReq, bool isIncoming) + void NexFriendRequestToFPDFriendData(const nexFriendRequest* frdReq, bool isIncoming, FriendData* friendData) { memset(friendData, 0, sizeof(FriendData)); // setup friend data @@ -282,7 +282,7 @@ namespace iosu convertFPDTimestampToDate(frdReq->message.expireTimestamp, &friendData->requestExtraData.uknData1); } - void NexFriendRequestToFPDFriendRequest(FriendRequest* friendRequest, nexFriendRequest* frdReq, bool isIncoming) + void NexFriendRequestToFPDFriendRequest(const nexFriendRequest* frdReq, bool isIncoming, FriendRequest* friendRequest) { memset(friendRequest, 0, sizeof(FriendRequest)); @@ -1007,7 +1007,7 @@ namespace iosu cemuLog_log(LogType::Force, "GetFriendRequestListEx: Failed to get friend request"); return FPResult_RequestFailed; } - NexFriendRequestToFPDFriendRequest(friendRequests + i, &frdReq, incoming); + NexFriendRequestToFPDFriendRequest(&frdReq, incoming, friendRequests + i); } return FPResult_Ok; } @@ -1063,13 +1063,13 @@ namespace iosu nexFriendRequest frdReq; if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) { - NexFriendToFPDFriendData(friendData, &frd); + NexFriendToFPDFriendData(&frd, friendData); continue; } bool incoming = false; if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) { - NexFriendRequestToFPDFriendData(friendData, &frdReq, incoming); + NexFriendRequestToFPDFriendData(&frdReq, incoming, friendData); continue; } cemuLog_logDebug(LogType::Force, "GetFriendListEx: Failed to find friend or request with pid {}", pid); diff --git a/src/Cemu/nex/nexTypes.h b/src/Cemu/nex/nexTypes.h index 49edd3d3..f43a83f2 100644 --- a/src/Cemu/nex/nexTypes.h +++ b/src/Cemu/nex/nexTypes.h @@ -16,7 +16,9 @@ public: class nexType { -public: + public: + virtual ~nexType(){}; + virtual const char* getMetaName() { cemu_assert_unimplemented(); From 66711529bec7e652b12db3e9f9729d1bc04f5386 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:06:36 +0200 Subject: [PATCH 062/101] Avoid wxGetKeyState since it asserts on Linux with wayland GTK Only modifier keys are allowed, but we used it to test for Escape --- src/gui/CemuApp.cpp | 1 - src/gui/guiWrapper.h | 4 ++++ src/gui/input/panels/InputPanel.cpp | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index f48957df..4acc1cf5 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -191,7 +191,6 @@ int CemuApp::FilterEvent(wxEvent& event) if(event.GetEventType() == wxEVT_KEY_DOWN) { const auto& key_event = (wxKeyEvent&)event; - wxGetKeyState(wxKeyCode::WXK_F17); g_window_info.set_keystate(fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()), true); } else if(event.GetEventType() == wxEVT_KEY_UP) diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index dd77819c..ec94c1a0 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -41,18 +41,22 @@ enum struct PlatformKeyCodes : uint32 LCONTROL = VK_LCONTROL, RCONTROL = VK_RCONTROL, TAB = VK_TAB, + ESCAPE = VK_ESCAPE, #elif BOOST_OS_LINUX LCONTROL = GDK_KEY_Control_L, RCONTROL = GDK_KEY_Control_R, TAB = GDK_KEY_Tab, + ESCAPE = GDK_KEY_Escape, #elif BOOST_OS_MACOS LCONTROL = kVK_Control, RCONTROL = kVK_RightControl, TAB = kVK_Tab, + ESCAPE = kVK_Escape, #else LCONTROL = 0, RCONTROL = 0, TAB = 0, + ESCAPE = 0, #endif }; diff --git a/src/gui/input/panels/InputPanel.cpp b/src/gui/input/panels/InputPanel.cpp index 6c1b8dda..514461fd 100644 --- a/src/gui/input/panels/InputPanel.cpp +++ b/src/gui/input/panels/InputPanel.cpp @@ -1,3 +1,4 @@ +#include "gui/guiWrapper.h" #include "gui/input/panels/InputPanel.h" #include @@ -26,7 +27,7 @@ void InputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, cons const auto mapping = reinterpret_cast(element->GetClientData()); // reset mapping - if(std::exchange(m_right_down, false) || wxGetKeyState(WXK_ESCAPE)) + if(std::exchange(m_right_down, false) || gui_isKeyDown(PlatformKeyCodes::ESCAPE)) { element->SetBackgroundColour(kKeyColourNormalMode); m_color_backup[element->GetId()] = kKeyColourNormalMode; From 63861bf812ef6364b90b0f84412b5ac76bbc78ef Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 17 Oct 2023 13:07:43 +0200 Subject: [PATCH 063/101] Fix SpotPass downloads on Linux/MacOS --- src/Cafe/IOSU/legacy/iosu_boss.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Cafe/IOSU/legacy/iosu_boss.cpp b/src/Cafe/IOSU/legacy/iosu_boss.cpp index 7b7ce250..c2c1eb51 100644 --- a/src/Cafe/IOSU/legacy/iosu_boss.cpp +++ b/src/Cafe/IOSU/legacy/iosu_boss.cpp @@ -119,7 +119,7 @@ namespace iosu uint32 turn_state = 0; uint32 wait_state = 0; - uint32 http_status_code = 0; + long http_status_code = 0; ContentType content_type = ContentType::kUnknownContent; std::vector result_buffer; @@ -592,6 +592,7 @@ namespace iosu int curl_result = curl_easy_perform(curl); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &it->http_status_code); + static_assert(sizeof(it->http_status_code) == sizeof(long)); //it->turn_state = kFinished; @@ -909,10 +910,10 @@ namespace iosu return it != g_boss.tasks.cend() ? std::make_pair(it->exec_count, it->processed_length) : std::make_pair(0u, (uint64)0); } - std::pair task_get_http_status_code(const char* taskId, uint32 accountId, uint64 titleId) + std::pair task_get_http_status_code(const char* taskId, uint32 accountId, uint64 titleId) { const auto it = get_task(taskId, accountId, titleId); - return it != g_boss.tasks.cend() ? std::make_pair(it->exec_count, it->http_status_code) : std::make_pair(0u, (uint32)0); + return it != g_boss.tasks.cend() ? std::make_pair(it->exec_count, it->http_status_code) : std::make_pair(0u, (long)0); } std::pair task_get_turn_state(const char* taskId, uint32 accountId, uint64 titleId) From 9ec50b865ddea2ffda33fbb34f0cdebcba643b55 Mon Sep 17 00:00:00 2001 From: bslhq Date: Tue, 17 Oct 2023 20:45:55 +0800 Subject: [PATCH 064/101] Fix nfc menu list of recent nfc files (#996) --- src/gui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 3156f2de..92594d00 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1751,7 +1751,7 @@ void MainWindow::UpdateNFCMenu() if (entry.empty()) continue; - if (!fs::exists(entry)) + if (!fs::exists(_utf8ToPath(entry))) continue; if (recentFileIndex == 0) From 9bb409314d307024899c7e41d1b4980ca73bb588 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:43:36 +0200 Subject: [PATCH 065/101] coreinit: Fix potential race condition in IPC code --- src/Cafe/IOSU/kernel/iosu_kernel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Cafe/IOSU/kernel/iosu_kernel.cpp b/src/Cafe/IOSU/kernel/iosu_kernel.cpp index 52097698..666e0373 100644 --- a/src/Cafe/IOSU/kernel/iosu_kernel.cpp +++ b/src/Cafe/IOSU/kernel/iosu_kernel.cpp @@ -578,8 +578,12 @@ namespace iosu return r; } + std::mutex sMtxReply[3]; + void _IPCReplyAndRelease(IOSDispatchableCommand* dispatchCmd, uint32 result) { + cemu_assert(dispatchCmd->ppcCoreIndex < 3); + std::unique_lock _l(sMtxReply[(uint32)dispatchCmd->ppcCoreIndex]); cemu_assert(dispatchCmd >= sIPCDispatchableCommandPool.GetPtr() && dispatchCmd < sIPCDispatchableCommandPool.GetPtr() + sIPCDispatchableCommandPool.GetCount()); dispatchCmd->originalBody->result = result; // submit to COS From b0a7fd4e072434d910600c62e7ce7199a2c70c81 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:49:12 +0200 Subject: [PATCH 066/101] Set default alignment for SysAllocator to cache-line size Avoids memory corruptions when the memory is cleared via DCZeroRange. Seen in BotW with AX AUX buffers. --- src/Common/SysAllocator.h | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Common/SysAllocator.h b/src/Common/SysAllocator.h index 7930a16b..7a11601e 100644 --- a/src/Common/SysAllocator.h +++ b/src/Common/SysAllocator.h @@ -1,10 +1,10 @@ #pragma once -#include - uint32 coreinit_allocFromSysArea(uint32 size, uint32 alignment); class SysAllocatorBase; +#define SYSALLOCATOR_GUARDS 0 // if 1, create a magic constant at the top of each memory allocation which is used to check for memory corruption + class SysAllocatorContainer { public: @@ -29,9 +29,7 @@ private: virtual void Initialize() = 0; }; - - -template +template class SysAllocator : public SysAllocatorBase { public: @@ -68,11 +66,17 @@ public: T* GetPtr() const { +#if SYSALLOCATOR_GUARDS + cemu_assert(*(uint32*)((uint8*)m_sysMem.GetPtr()+(sizeof(T) * count)) == 0x112A33C4); +#endif return m_sysMem.GetPtr(); } uint32 GetMPTR() const { +#if SYSALLOCATOR_GUARDS + cemu_assert(*(uint32*)((uint8*)m_sysMem.GetPtr()+(sizeof(T) * count)) == 0x112A33C4); +#endif return m_sysMem.GetMPTR(); } @@ -130,11 +134,17 @@ private: { if (m_sysMem.GetMPTR() != 0) return; - // alloc mem - m_sysMem = { coreinit_allocFromSysArea(sizeof(T) * count, alignment) }; + uint32 guardSize = 0; +#if SYSALLOCATOR_GUARDS + guardSize = 4; +#endif + m_sysMem = { coreinit_allocFromSysArea(sizeof(T) * count + guardSize, alignment) }; // copy temp buffer to mem and clear it memcpy(m_sysMem.GetPtr(), m_tempData.data(), sizeof(T)*count); +#if SYSALLOCATOR_GUARDS + *(uint32*)((uint8*)m_sysMem.GetPtr()+(sizeof(T) * count)) = 0x112A33C4; +#endif m_tempData.clear(); } @@ -197,9 +207,8 @@ private: { if (m_sysMem.GetMPTR() != 0) return; - // alloc mem - m_sysMem = { coreinit_allocFromSysArea(sizeof(T), 8) }; + m_sysMem = { coreinit_allocFromSysArea(sizeof(T), 32) }; // copy temp buffer to mem and clear it *m_sysMem = m_tempData; } From f3c95f72e74d8a5f5873061fbb994643c63ec9c5 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 19 Oct 2023 05:55:52 +0200 Subject: [PATCH 067/101] nn_fp: Multiple fixes --- src/Cafe/IOSU/legacy/iosu_fpd.cpp | 3 ++- src/Cafe/OS/libs/nn_fp/nn_fp.cpp | 13 +++++++------ src/util/ChunkedHeap/ChunkedHeap.h | 10 +++++++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index bcd580ef..9130b28d 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -328,6 +328,7 @@ namespace iosu static const auto FPResult_Ok = 0; static const auto FPResult_InvalidIPCParam = BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_FP, 0x680); static const auto FPResult_RequestFailed = BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // figure out proper error code + static const auto FPResult_Aborted = BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_FP, 0x3480); class FPDService : public iosu::nn::IPCSimpleService { @@ -586,7 +587,7 @@ namespace iosu if (!ActiveSettings::IsOnlineEnabled()) { // not online, fail immediately - return BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // todo + return FPResult_Ok; // Splatoon expects this to always return success otherwise it will softlock. This should be FPResult_Aborted? } StartFriendSession(); fpdClient->hasLoggedIn = true; diff --git a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp index 53ab3eef..fc757ea9 100644 --- a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp +++ b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp @@ -55,8 +55,8 @@ namespace nn { std::unique_lock _l(m_mtx); void* p = g_fp.fpBufferHeap->alloc(size, 32); - uint32 heapSize, allocationSize, allocNum; - g_fp.fpBufferHeap->getStats(heapSize, allocationSize, allocNum); + if (!p) + cemuLog_log(LogType::Force, "nn_fp: Internal heap is full"); return p; } @@ -153,8 +153,11 @@ namespace nn totalBufferSize += m_vec[i].size; totalBufferSize = (totalBufferSize+31)&~31; } - m_dataBuffer = FPIpcBufferAllocator.Allocate(totalBufferSize, 32); - cemu_assert_debug(m_dataBuffer); + if(totalBufferSize > 0) + { + m_dataBuffer = FPIpcBufferAllocator.Allocate(totalBufferSize, 32); + cemu_assert_debug(m_dataBuffer); + } // update Ioctl vector addresses for(uint8 i=0; im_asyncResult = result; // store result in variable since FP callbacks pass a pointer to nnResult and not the value directly ipcCtx->CopyBackOutputs(); - cemuLog_logDebug(LogType::Force, "[DBG] AsyncHandler BeforeCallback"); PPCCoreCallback(ipcCtx->m_callbackFunc, &ipcCtx->m_asyncResult, ipcCtx->m_callbackParam); - cemuLog_logDebug(LogType::Force, "[DBG] AsyncHandler AfterCallback"); delete ipcCtx; osLib_returnFromFunction(hCPU, 0); } diff --git a/src/util/ChunkedHeap/ChunkedHeap.h b/src/util/ChunkedHeap/ChunkedHeap.h index 8e458d40..abc45429 100644 --- a/src/util/ChunkedHeap/ChunkedHeap.h +++ b/src/util/ChunkedHeap/ChunkedHeap.h @@ -489,6 +489,11 @@ private: bool _alloc(uint32 size, uint32 alignment, uint32& allocOffsetOut) { + if(size == 0) + { + size = 1; // zero-sized allocations are not supported + cemu_assert_suspicious(); + } // find smallest bucket to scan uint32 alignmentM1 = alignment - 1; uint32 bucketIndex = ulog2(size); @@ -521,7 +526,10 @@ private: { auto it = map_allocatedRange.find(addrOffset); if (it == map_allocatedRange.end()) - assert_dbg(); + { + cemuLog_log(LogType::Force, "VHeap internal error"); + cemu_assert(false); + } allocRange_t* range = it->second; map_allocatedRange.erase(it); m_statsMemAllocated -= range->size; From 80543ba2c65bfedfa124dd62e15ce3dfb90fd201 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Mon, 23 Oct 2023 01:10:11 +0300 Subject: [PATCH 068/101] Fix build --- src/android/app/build.gradle | 6 ++++-- src/android/app/src/main/cpp/GameTitleLoader.cpp | 2 +- src/main.cpp | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 1f5257fc..4edf3415 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -13,7 +13,8 @@ android { versionCode 1 versionName "1.0" ndk { - abiFilters("x86_64", "arm64-v8a") +// abiFilters("x86_64", "arm64-v8a") + abiFilters("arm64-v8a") } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -47,7 +48,8 @@ android { '-DENABLE_DISCORD_RPC=OFF', '-DENABLE_WAYLAND=OFF' ) - abiFilters("x86_64", "arm64-v8a") +// abiFilters("x86_64", "arm64-v8a") + abiFilters("arm64-v8a") } } } diff --git a/src/android/app/src/main/cpp/GameTitleLoader.cpp b/src/android/app/src/main/cpp/GameTitleLoader.cpp index fc6a17ac..6c63e613 100644 --- a/src/android/app/src/main/cpp/GameTitleLoader.cpp +++ b/src/android/app/src/main/cpp/GameTitleLoader.cpp @@ -109,7 +109,7 @@ std::string GameTitleLoader::GetNameByTitleId(uint64 titleId) return "Unknown title"; std::string name; if (!GetConfig().GetGameListCustomName(titleId, name)) - name = titleInfo.GetTitleName(); + name = titleInfo.GetMetaTitleName(); m_name_cache.emplace(titleId, name); return name; } diff --git a/src/main.cpp b/src/main.cpp index e25223c5..8a423daf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include "config/NetworkSettings.h" #include "config/LaunchSettings.h" #include "input/InputManager.h" -#include "gui/CemuApp.h" #include "Cafe/CafeSystem.h" #include "Cafe/TitleList/TitleList.h" From 036551aab7f407cdbbc5f831690de20357a8152c Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Tue, 24 Oct 2023 21:50:30 +0300 Subject: [PATCH 069/101] Fix crash in input settings fragment --- .../src/main/java/info/cemu/Cemu/InputSettingsFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/app/src/main/java/info/cemu/Cemu/InputSettingsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/InputSettingsFragment.java index 77e43e23..8c6c6629 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/InputSettingsFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/InputSettingsFragment.java @@ -23,10 +23,10 @@ public class InputSettingsFragment extends Fragment { GenericRecyclerViewAdapter genericRecyclerViewAdapter = new GenericRecyclerViewAdapter(); for (int index = 0; index < NativeLibrary.MAX_CONTROLLERS; index++) { int controllerIndex = index; - String controllerType = getString(NativeLibrary.controllerTypeToResourceNameId(NativeLibrary.getControllerType(controllerIndex))); + int controllerType = NativeLibrary.isControllerDisabled(controllerIndex) ? NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED : NativeLibrary.getControllerType(controllerIndex); ButtonRecyclerViewItem buttonRecyclerViewItem = new ButtonRecyclerViewItem( getString(R.string.controller_numbered, controllerIndex + 1), - getString(R.string.emulated_controller_with_type, controllerType), + getString(R.string.emulated_controller_with_type, getString(NativeLibrary.controllerTypeToResourceNameId(controllerType))), () -> { Bundle bundle = new Bundle(); bundle.putInt(ControllerInputsFragment.CONTROLLER_INDEX, controllerIndex); From 5047c4d083d0cb939200f29b5554dce697847998 Mon Sep 17 00:00:00 2001 From: GaryOderNichts <12049776+GaryOderNichts@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:21:52 +0100 Subject: [PATCH 070/101] GDBStub: Fix checkSum string to int conversion (#1029) --- src/Cafe/HW/Espresso/Debugger/GDBStub.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp index e934e55d..c8308594 100644 --- a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp +++ b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp @@ -356,7 +356,7 @@ void GDBServer::ThreadFunc() } char checkSumStr[2]; receiveMessage(checkSumStr, 2); - uint32_t checkSum = std::stoi(checkSumStr, nullptr, 16); + uint32_t checkSum = std::stoi(std::string(checkSumStr, sizeof(checkSumStr)), nullptr, 16); assert((checkedSum & 0xFF) == checkSum); HandleCommand(message); From 09409a51089db5432c0bdd7ec8f2e3907a8b2669 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:24:26 +0100 Subject: [PATCH 071/101] Set macOS min version to 12.0 Monterey (#1025) --- src/CMakeLists.txt | 1 + src/resource/MacOSXBundleInfo.plist.in | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00a43a80..8ab07e7a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -81,6 +81,7 @@ if (MACOS_BUNDLE) set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2023 Cemu Project") set(MACOSX_BUNDLE_CATEGORY "public.app-category.games") + set(MACOSX_MINIMUM_SYSTEM_VERSION "12.0") set_target_properties(CemuBin PROPERTIES MACOSX_BUNDLE true diff --git a/src/resource/MacOSXBundleInfo.plist.in b/src/resource/MacOSXBundleInfo.plist.in index c181b388..74dc0d59 100644 --- a/src/resource/MacOSXBundleInfo.plist.in +++ b/src/resource/MacOSXBundleInfo.plist.in @@ -26,7 +26,9 @@ NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} - LSApplicationCategoryType - ${MACOSX_BUNDLE_CATEGORY} + LSApplicationCategoryType + ${MACOSX_BUNDLE_CATEGORY} + LSMinimumSystemVersion + ${MACOSX_MINIMUM_SYSTEM_VERSION} - \ No newline at end of file + From 18490830738c61b1b35fa11a9207bcf3fc4edd3e Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Wed, 6 Dec 2023 01:33:29 +0000 Subject: [PATCH 072/101] Use hidapi for Wiimotes on Windows (#1033) --- CMakeLists.txt | 5 +- src/input/CMakeLists.txt | 11 +- .../api/Wiimote/WiimoteControllerProvider.cpp | 4 - .../api/Wiimote/windows/WinWiimoteDevice.cpp | 130 ------------------ .../api/Wiimote/windows/WinWiimoteDevice.h | 24 ---- vcpkg.json | 5 +- 6 files changed, 4 insertions(+), 175 deletions(-) delete mode 100644 src/input/api/Wiimote/windows/WinWiimoteDevice.cpp delete mode 100644 src/input/api/Wiimote/windows/WinWiimoteDevice.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dc1a6f2..c988508c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,10 +89,9 @@ if (WIN32) option(ENABLE_XINPUT "Enables the usage of XInput" ON) option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON) add_compile_definitions(HAS_DIRECTINPUT) - set(ENABLE_WIIMOTE ON) -elseif (UNIX) - option(ENABLE_HIDAPI "Build with HIDAPI" ON) endif() + +option(ENABLE_HIDAPI "Build with HIDAPI" ON) option(ENABLE_SDL "Enables the SDLController backend" ON) # audio backends diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 53b4dc3b..9f7873a1 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -70,18 +70,9 @@ if (ENABLE_WIIMOTE) api/Wiimote/NativeWiimoteController.h api/Wiimote/NativeWiimoteController.cpp api/Wiimote/WiimoteDevice.h - ) - if (ENABLE_HIDAPI) - target_sources(CemuInput PRIVATE api/Wiimote/hidapi/HidapiWiimote.cpp api/Wiimote/hidapi/HidapiWiimote.h - ) - elseif (WIN32) - target_sources(CemuInput PRIVATE - api/Wiimote/windows/WinWiimoteDevice.cpp - api/Wiimote/windows/WinWiimoteDevice.h - ) - endif() + ) endif () diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.cpp b/src/input/api/Wiimote/WiimoteControllerProvider.cpp index 0ca00a1a..55f28c01 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.cpp +++ b/src/input/api/Wiimote/WiimoteControllerProvider.cpp @@ -2,11 +2,7 @@ #include "input/api/Wiimote/NativeWiimoteController.h" #include "input/api/Wiimote/WiimoteMessages.h" -#ifdef HAS_HIDAPI #include "input/api/Wiimote/hidapi/HidapiWiimote.h" -#elif BOOST_OS_WINDOWS -#include "input/api/Wiimote/windows/WinWiimoteDevice.h" -#endif #include #include diff --git a/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp b/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp deleted file mode 100644 index 09d73013..00000000 --- a/src/input/api/Wiimote/windows/WinWiimoteDevice.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "input/api/Wiimote/windows/WinWiimoteDevice.h" - -#include -#include - -#pragma comment(lib, "Setupapi.lib") -#pragma comment(lib, "hid.lib") - -WinWiimoteDevice::WinWiimoteDevice(HANDLE handle, std::vector identifier) - : m_handle(handle), m_identifier(std::move(identifier)) -{ - m_overlapped.hEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); -} - -WinWiimoteDevice::~WinWiimoteDevice() -{ - CancelIo(m_handle); - ResetEvent(m_overlapped.hEvent); - CloseHandle(m_handle); -} - -bool WinWiimoteDevice::write_data(const std::vector& data) -{ - return HidD_SetOutputReport(m_handle, (void*)data.data(), (ULONG)data.size()); -} - -std::optional> WinWiimoteDevice::read_data() -{ - DWORD read = 0; - std::array buffer{}; - - if (!ReadFile(m_handle, buffer.data(), (DWORD)buffer.size(), &read, &m_overlapped)) - { - const auto error = GetLastError(); - if (error == ERROR_DEVICE_NOT_CONNECTED) - return {}; - else if (error == ERROR_IO_PENDING) - { - const auto wait_result = WaitForSingleObject(m_overlapped.hEvent, 100); - if (wait_result == WAIT_TIMEOUT) - { - CancelIo(m_handle); - ResetEvent(m_overlapped.hEvent); - return {}; - } - else if (wait_result == WAIT_FAILED) - return {}; - - if (GetOverlappedResult(m_handle, &m_overlapped, &read, FALSE) == FALSE) - return {}; - } - else if (error == ERROR_INVALID_HANDLE) - { - ResetEvent(m_overlapped.hEvent); - return {}; - } - else - { - cemu_assert_debug(false); - } - } - - ResetEvent(m_overlapped.hEvent); - if (read == 0) - return {}; - - return {{buffer.cbegin(), buffer.cbegin() + read}}; -} - -std::vector WinWiimoteDevice::get_devices() -{ - std::vector result; - - GUID hid_guid; - HidD_GetHidGuid(&hid_guid); - - const auto device_info = SetupDiGetClassDevs(&hid_guid, nullptr, nullptr, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)); - - for (DWORD index = 0; ; ++index) - { - SP_DEVICE_INTERFACE_DATA device_data{}; - device_data.cbSize = sizeof(device_data); - if (SetupDiEnumDeviceInterfaces(device_info, nullptr, &hid_guid, index, &device_data) == FALSE) - break; - - DWORD device_data_len; - if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, nullptr, 0, &device_data_len, nullptr) == FALSE - && GetLastError() != ERROR_INSUFFICIENT_BUFFER) - continue; - - std::vector detail_data_buffer; - detail_data_buffer.resize(device_data_len); - - const auto detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)detail_data_buffer.data(); - detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, device_data_len, nullptr, nullptr) - == FALSE) - continue; - - HANDLE device_handle = CreateFile(detail_data->DevicePath, (GENERIC_READ | GENERIC_WRITE), - (FILE_SHARE_READ | FILE_SHARE_WRITE), nullptr, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, nullptr); - if (device_handle == INVALID_HANDLE_VALUE) - continue; - - HIDD_ATTRIBUTES attributes{}; - attributes.Size = sizeof(attributes); - if (HidD_GetAttributes(device_handle, &attributes) == FALSE) - { - CloseHandle(device_handle); - continue; - } - - if (attributes.VendorID != 0x057e || (attributes.ProductID != 0x0306 && attributes.ProductID != 0x0330)) - { - CloseHandle(device_handle); - continue; - } - - result.emplace_back(std::make_shared(device_handle, detail_data_buffer)); - } - - return result; -} - -bool WinWiimoteDevice::operator==(WiimoteDevice& o) const -{ - return m_identifier == static_cast(o).m_identifier; -} diff --git a/src/input/api/Wiimote/windows/WinWiimoteDevice.h b/src/input/api/Wiimote/windows/WinWiimoteDevice.h deleted file mode 100644 index 077882db..00000000 --- a/src/input/api/Wiimote/windows/WinWiimoteDevice.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "input/api/Wiimote/WiimoteDevice.h" - -class WinWiimoteDevice : public WiimoteDevice -{ -public: - WinWiimoteDevice(HANDLE handle, std::vector identifier); - ~WinWiimoteDevice(); - - bool write_data(const std::vector& data) override; - std::optional> read_data() override; - - static std::vector get_devices(); - - bool operator==(WiimoteDevice& o) const override; - -private: - HANDLE m_handle; - OVERLAPPED m_overlapped{}; - std::vector m_identifier; -}; - -using WiimoteDevice_t = WinWiimoteDevice; diff --git a/vcpkg.json b/vcpkg.json index 7ea8058e..d14a1a8a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -26,10 +26,7 @@ "boost-static-string", "boost-random", "fmt", - { - "name": "hidapi", - "platform": "!windows" - }, + "hidapi", "libpng", "glm", { From b6aaf6633063be47d89a8216e269e32aec5a4b49 Mon Sep 17 00:00:00 2001 From: qurious-pixel <62252937+qurious-pixel@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:07:50 -0800 Subject: [PATCH 073/101] [AppImage] Bundle libstdc++ (#1038) --- dist/linux/appimage.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/linux/appimage.sh b/dist/linux/appimage.sh index 7043a759..60a50329 100755 --- a/dist/linux/appimage.sh +++ b/dist/linux/appimage.sh @@ -46,6 +46,7 @@ fi echo "Cemu Version Cemu-${GITVERSION}" rm AppDir/usr/lib/libwayland-client.so.0 +cp /lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/lib/ echo -e "export LC_ALL=C\nexport FONTCONFIG_PATH=/etc/fonts" >> AppDir/apprun-hooks/linuxdeploy-plugin-gtk.sh VERSION="${GITVERSION}" ./mkappimage.AppImage --appimage-extract-and-run "${GITHUB_WORKSPACE}"/AppDir From f6bb666abf9a34bab705b53bf8fb913696bb4b31 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 10 Dec 2023 08:30:08 +0100 Subject: [PATCH 074/101] Mac: Add wua filetype to info.plist (#1039) --- src/CMakeLists.txt | 1 + src/resource/MacOSXBundleInfo.plist.in | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ab07e7a..de9a6600 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,6 +82,7 @@ if (MACOS_BUNDLE) set(MACOSX_BUNDLE_CATEGORY "public.app-category.games") set(MACOSX_MINIMUM_SYSTEM_VERSION "12.0") + set(MACOSX_BUNDLE_TYPE_EXTENSION "wua") set_target_properties(CemuBin PROPERTIES MACOSX_BUNDLE true diff --git a/src/resource/MacOSXBundleInfo.plist.in b/src/resource/MacOSXBundleInfo.plist.in index 74dc0d59..98064735 100644 --- a/src/resource/MacOSXBundleInfo.plist.in +++ b/src/resource/MacOSXBundleInfo.plist.in @@ -30,5 +30,18 @@ ${MACOSX_BUNDLE_CATEGORY} LSMinimumSystemVersion ${MACOSX_MINIMUM_SYSTEM_VERSION} + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + ${MACOSX_BUNDLE_TYPE_EXTENSION} + + CFBundleTypeName + Wii U File + CFBundleTypeRole + Viewer + + From 9398c0ca6b142ec92e297dd099bbcba7f22749b1 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:18:17 +0100 Subject: [PATCH 075/101] Latte: Simplify and fix texture copy --- src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp | 26 +++------------------ src/Cafe/HW/Latte/Core/LatteTexture.cpp | 18 ++++++++++++-- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp b/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp index df99307c..4f5b24ad 100644 --- a/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp @@ -46,9 +46,7 @@ void LatteSurfaceCopy_copySurfaceNew(MPTR srcPhysAddr, MPTR srcMipAddr, uint32 s // mark source and destination texture as still in use LatteTC_MarkTextureStillInUse(destinationTexture); LatteTC_MarkTextureStillInUse(sourceTexture); - // determine GL slice indices sint32 realSrcSlice = srcSlice; - sint32 realDstSlice = dstSlice; if (LatteTexture_doesEffectiveRescaleRatioMatch(sourceTexture, sourceView->firstMip, destinationTexture, destinationView->firstMip)) { // adjust copy size @@ -62,29 +60,11 @@ void LatteSurfaceCopy_copySurfaceNew(MPTR srcPhysAddr, MPTR srcMipAddr, uint32 s LatteTexture_scaleToEffectiveSize(sourceTexture, &effectiveCopyWidth, &effectiveCopyHeight, 0); // copy slice if (sourceView->baseTexture->isDepth != destinationView->baseTexture->isDepth) - { g_renderer->surfaceCopy_copySurfaceWithFormatConversion(sourceTexture, sourceView->firstMip, sourceView->firstSlice, destinationTexture, destinationView->firstMip, destinationView->firstSlice, copyWidth, copyHeight); - uint64 eventCounter = LatteTexture_getNextUpdateEventCounter(); - LatteTexture_MarkDynamicTextureAsChanged(destinationTexture->baseView, destinationView->firstSlice, destinationView->firstMip, eventCounter); - } else - { - // calculate mip levels relative to texture base - sint32 texDstMipLevel; - if (destinationTexture->physAddress == dstPhysAddr) - { - texDstMipLevel = dstLevel; - } - else - { - // todo - handle mip addresses properly - texDstMipLevel = dstLevel - destinationView->firstMip; - } - - g_renderer->texture_copyImageSubData(sourceTexture, sourceView->firstMip, 0, 0, realSrcSlice, destinationTexture, texDstMipLevel, 0, 0, realDstSlice, effectiveCopyWidth, effectiveCopyHeight, 1); - uint64 eventCounter = LatteTexture_getNextUpdateEventCounter(); - LatteTexture_MarkDynamicTextureAsChanged(destinationTexture->baseView, destinationView->firstSlice, texDstMipLevel, eventCounter); - } + g_renderer->texture_copyImageSubData(sourceTexture, sourceView->firstMip, 0, 0, realSrcSlice, destinationTexture, destinationView->firstMip, 0, 0, destinationView->firstSlice, effectiveCopyWidth, effectiveCopyHeight, 1); + const uint64 eventCounter = LatteTexture_getNextUpdateEventCounter(); + LatteTexture_MarkDynamicTextureAsChanged(destinationTexture->baseView, destinationView->firstSlice, destinationView->firstMip, eventCounter); } else { diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index 19162e04..42a9d8c6 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -836,6 +836,11 @@ bool IsDimensionCompatibleForView(Latte::E_DIM baseDim, Latte::E_DIM viewDim) // not compatible incompatibleDim = true; } + else if (baseDim == Latte::E_DIM::DIM_3D && viewDim == Latte::E_DIM::DIM_3D) + { + // incompatible by default, but may be compatible if the view matches the depth of the base texture and starts at mip/slice 0 + incompatibleDim = true; + } else if ((baseDim == Latte::E_DIM::DIM_2D && viewDim == Latte::E_DIM::DIM_CUBEMAP) || (baseDim == Latte::E_DIM::DIM_CUBEMAP && viewDim == Latte::E_DIM::DIM_2D)) { @@ -872,7 +877,9 @@ VIEWCOMPATIBILITY LatteTexture_CanTextureBeRepresentedAsView(LatteTexture* baseT return VIEW_NOT_COMPATIBLE; // depth and non-depth formats are never compatible (on OpenGL) if (!LatteTexture_IsTexelSizeCompatibleFormat(baseTexture->format, format) || baseTexture->width != width || baseTexture->height != height) return VIEW_NOT_COMPATIBLE; - if (!IsDimensionCompatibleForView(baseTexture->dim, dimView)) + // 3D views are only compatible on Vulkan if they match the base texture in regards to mip and slice count + bool isCompatible3DView = dimView == Latte::E_DIM::DIM_3D && baseTexture->dim == dimView && firstSlice == 0 && firstMip == 0 && baseTexture->mipLevels == numMip && baseTexture->depth == numSlice; + if (!isCompatible3DView && !IsDimensionCompatibleForView(baseTexture->dim, dimView)) return VIEW_NOT_COMPATIBLE; if (baseTexture->isDepth && baseTexture->format != format) { @@ -999,6 +1006,7 @@ void LatteTexture_RecreateTextureWithDifferentMipSliceCount(LatteTexture* textur // create new texture representation // if allowCreateNewDataTexture is true, a new texture will be created if necessary. If it is false, only existing textures may be used, except if a data-compatible version of the requested texture already exists and it's not view compatible +// the returned view will map to the provided mip and slice range within the created texture, this is to match the behavior of lookupSliceEx LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, sint32 width, sint32 height, sint32 depth, sint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, sint32 firstMip, sint32 numMip, sint32 firstSlice, sint32 numSlice, Latte::E_GX2SURFFMT format, Latte::E_DIM dimBase, Latte::E_DIM dimView, bool isDepth, bool allowCreateNewDataTexture) { if (format == Latte::E_GX2SURFFMT::INVALID_FORMAT) @@ -1105,11 +1113,17 @@ LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, si if (allowCreateNewDataTexture == false) return nullptr; LatteTextureView* view = LatteTexture_CreateTexture(0, dimBase, physAddr, physMipAddr, format, width, height, depth, pitch, firstMip + numMip, swizzle, tileMode, isDepth); + LatteTexture* newTexture = view->baseTexture; LatteTexture_GatherTextureRelations(view->baseTexture); LatteTexture_UpdateTextureFromDynamicChanges(view->baseTexture); // delete any individual smaller slices/mips that have become redundant LatteTexture_DeleteAbsorbedSubtextures(view->baseTexture); - return view; + // create view + sint32 relativeMipIndex; + sint32 relativeSliceIndex; + VIEWCOMPATIBILITY viewCompatibility = LatteTexture_CanTextureBeRepresentedAsView(newTexture, physAddr, width, height, pitch, dimView, format, isDepth, firstMip, numMip, firstSlice, numSlice, relativeMipIndex, relativeSliceIndex); + cemu_assert(viewCompatibility == VIEW_COMPATIBLE); + return view->baseTexture->GetOrCreateView(dimView, format, relativeMipIndex + firstMip, numMip, relativeSliceIndex + firstSlice, numSlice); } LatteTextureView* LatteTC_LookupTextureByData(MPTR physAddr, sint32 width, sint32 height, sint32 pitch, sint32 firstMip, sint32 numMip, sint32 firstSlice, sint32 numSlice, sint32* searchIndex) From 67f7ce815c0b97c5ff0f19e9c3ad9a6d64d756a7 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:39:55 +0100 Subject: [PATCH 076/101] nn_pdm: Refactor code to use new module structure --- src/Cafe/CafeSystem.cpp | 7 +- src/Cafe/IOSU/PDM/iosu_pdm.cpp | 135 +++++++++++++++++++++++++++------ src/Cafe/IOSU/PDM/iosu_pdm.h | 7 +- 3 files changed, 117 insertions(+), 32 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 3d06281e..30dab1d4 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -530,7 +530,8 @@ namespace CafeSystem { // entries in this list are ordered by initialization order. Shutdown in reverse order iosu::kernel::GetModule(), - iosu::fpd::GetModule() + iosu::fpd::GetModule(), + iosu::pdm::GetModule(), }; // initialize all subsystems which are persistent and don't depend on a game running @@ -571,7 +572,6 @@ namespace CafeSystem iosu::iosuAcp_init(); iosu::boss_init(); iosu::nim::Initialize(); - iosu::pdm::Initialize(); iosu::odm::Initialize(); // init Cafe OS avm::Initialize(); @@ -840,7 +840,6 @@ namespace CafeSystem coreinit::OSSchedulerBegin(3); else coreinit::OSSchedulerBegin(1); - iosu::pdm::StartTrackingTime(GetForegroundTitleId()); } void LaunchForegroundTitle() @@ -970,8 +969,6 @@ namespace CafeSystem RPLLoader_ResetState(); for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) (*it)->TitleStop(); - // stop time tracking - iosu::pdm::Stop(); // reset Cemu subsystems PPCRecompiler_Shutdown(); GraphicPack2::Reset(); diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.cpp b/src/Cafe/IOSU/PDM/iosu_pdm.cpp index 45b4a1d8..e54529a9 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.cpp +++ b/src/Cafe/IOSU/PDM/iosu_pdm.cpp @@ -1,4 +1,5 @@ #include "iosu_pdm.h" +#include "Cafe/CafeSystem.h" #include "config/ActiveSettings.h" #include "Common/FileStream.h" #include "util/helpers/Semaphore.h" @@ -17,7 +18,8 @@ namespace iosu { namespace pdm { - std::mutex sDiaryLock; + std::recursive_mutex sPlaystatsLock; + std::recursive_mutex sDiaryLock; fs::path GetPDFile(const char* filename) { @@ -80,14 +82,16 @@ namespace iosu static_assert((NUM_PLAY_STATS_ENTRIES * sizeof(PlayStatsEntry)) == 0x1400); } - void LoadPlaystats() + void OpenPlaystats() { + std::unique_lock _l(sPlaystatsLock); PlayStats.numEntries = 0; for (size_t i = 0; i < NUM_PLAY_STATS_ENTRIES; i++) { auto& e = PlayStats.entry[i]; memset(&e, 0, sizeof(PlayStatsEntry)); } + cemu_assert_debug(!PlayStats.fs); PlayStats.fs = FileStream::openFile2(GetPDFile("PlayStats.dat"), true); if (!PlayStats.fs) { @@ -98,18 +102,39 @@ namespace iosu { delete PlayStats.fs; PlayStats.fs = nullptr; - cemuLog_log(LogType::Force, "PlayStats.dat malformed"); + cemuLog_log(LogType::Force, "PlayStats.dat malformed. Time tracking wont be used"); // dont delete the existing file in case it could still be salvaged (todo) and instead just dont track play time return; } + PlayStats.numEntries = 0; PlayStats.fs->readData(&PlayStats.numEntries, sizeof(uint32be)); if (PlayStats.numEntries > NUM_PLAY_STATS_ENTRIES) PlayStats.numEntries = NUM_PLAY_STATS_ENTRIES; PlayStats.fs->readData(PlayStats.entry, NUM_PLAY_STATS_ENTRIES * 20); } + void ClosePlaystats() + { + std::unique_lock _l(sPlaystatsLock); + if (PlayStats.fs) + { + delete PlayStats.fs; + PlayStats.fs = nullptr; + } + } + + void UnloadPlaystats() + { + std::unique_lock _l(sPlaystatsLock); + cemu_assert_debug(!PlayStats.fs); // unloading expects that file is closed + PlayStats.numEntries = 0; + for(auto& it : PlayStats.entry) + it = PlayStatsEntry{}; + } + PlayStatsEntry* PlayStats_GetEntry(uint64 titleId) { + std::unique_lock _l(sPlaystatsLock); uint32be titleIdHigh = (uint32)(titleId>>32); uint32be titleIdLow = (uint32)(titleId & 0xFFFFFFFF); size_t numEntries = PlayStats.numEntries; @@ -121,7 +146,7 @@ namespace iosu return nullptr; } - void PlayStats_WriteEntry(PlayStatsEntry* entry, bool writeEntryCount = false) + void PlayStats_WriteEntryNoLock(PlayStatsEntry* entry, bool writeEntryCount = false) { if (!PlayStats.fs) return; @@ -141,8 +166,15 @@ namespace iosu } } + void PlayStats_WriteEntry(PlayStatsEntry* entry, bool writeEntryCount = false) + { + std::unique_lock _l(sPlaystatsLock); + PlayStats_WriteEntryNoLock(entry, writeEntryCount); + } + PlayStatsEntry* PlayStats_CreateEntry(uint64 titleId) { + std::unique_lock _l(sPlaystatsLock); bool entryCountChanged = false; PlayStatsEntry* newEntry; if(PlayStats.numEntries < NUM_PLAY_STATS_ENTRIES) @@ -168,7 +200,7 @@ namespace iosu newEntry->numTimesLaunched = 1; newEntry->totalMinutesPlayed = 0; newEntry->ukn12 = 0; - PlayStats_WriteEntry(newEntry, entryCountChanged); + PlayStats_WriteEntryNoLock(newEntry, entryCountChanged); return newEntry; } @@ -176,6 +208,7 @@ namespace iosu // if it does not exist it creates a new entry with first and last played set to today PlayStatsEntry* PlayStats_BeginNewTracking(uint64 titleId) { + std::unique_lock _l(sPlaystatsLock); PlayStatsEntry* entry = PlayStats_GetEntry(titleId); if (entry) { @@ -189,11 +222,12 @@ namespace iosu void PlayStats_CountAdditionalMinutes(PlayStatsEntry* entry, uint32 additionalMinutes) { + std::unique_lock _l(sPlaystatsLock); if (additionalMinutes == 0) return; entry->totalMinutesPlayed += additionalMinutes; entry->mostRecentDayIndex = GetTodaysDayIndex(); - PlayStats_WriteEntry(entry); + PlayStats_WriteEntryNoLock(entry); } struct PlayDiaryHeader @@ -218,6 +252,7 @@ namespace iosu void CreatePlayDiary() { MakeDirectory(); + cemu_assert_debug(!PlayDiaryData.fs); PlayDiaryData.fs = FileStream::createFile2(GetPDFile("PlayDiary.dat")); if (!PlayDiaryData.fs) { @@ -230,7 +265,7 @@ namespace iosu PlayDiaryData.fs->writeData(&PlayDiaryData.header, sizeof(PlayDiaryHeader)); } - void LoadPlayDiary() + void OpenPlayDiary() { std::unique_lock _lock(sDiaryLock); cemu_assert_debug(!PlayDiaryData.fs); @@ -268,6 +303,26 @@ namespace iosu } } + void ClosePlayDiary() + { + std::unique_lock _lock(sDiaryLock); + if (PlayDiaryData.fs) + { + delete PlayDiaryData.fs; + PlayDiaryData.fs = nullptr; + } + } + + void UnloadDiaryData() + { + std::unique_lock _lock(sDiaryLock); + cemu_assert_debug(!PlayDiaryData.fs); // unloading expects that file is closed + PlayDiaryData.header.readIndex = 0; + PlayDiaryData.header.writeIndex = 0; + for (auto& it : PlayDiaryData.entry) + it = PlayDiaryEntry{}; + } + uint32 GetDiaryEntries(uint8 accountSlot, PlayDiaryEntry* diaryEntries, uint32 maxEntries) { std::unique_lock _lock(sDiaryLock); @@ -352,25 +407,59 @@ namespace iosu } } - void Initialize() + class : public ::IOSUModule { - // todo - add support for per-account handling - LoadPlaystats(); - LoadPlayDiary(); - } - - void StartTrackingTime(uint64 titleId) - { - sPDMRequestExitThread = false; - sPDMTimeTrackingThread = std::thread(TimeTrackingThread, titleId); - } + void PDMLoadAll() + { + OpenPlaystats(); + OpenPlayDiary(); + } - void Stop() + void PDMUnloadAll() + { + UnloadPlaystats(); + UnloadDiaryData(); + } + + void PDMCloseAll() + { + ClosePlaystats(); + ClosePlayDiary(); + } + + void SystemLaunch() override + { + // todo - add support for per-account handling + PDMLoadAll(); + PDMCloseAll(); // close the files again, user may mess with MLC files or change MLC path while no game is running + } + void SystemExit() override + { + PDMCloseAll(); + PDMUnloadAll(); + } + void TitleStart() override + { + // reload data and keep files open + PDMUnloadAll(); + PDMLoadAll(); + auto titleId = CafeSystem::GetForegroundTitleId(); + sPDMRequestExitThread = false; + sPDMTimeTrackingThread = std::thread(TimeTrackingThread, titleId); + } + void TitleStop() override + { + sPDMRequestExitThread.store(true); + sPDMSem.increment(); + if(sPDMTimeTrackingThread.joinable()) + sPDMTimeTrackingThread.join(); + PDMCloseAll(); + } + }sIOSUModuleNNPDM; + + IOSUModule* GetModule() { - sPDMRequestExitThread.store(true); - sPDMSem.increment(); - if(sPDMTimeTrackingThread.joinable()) - sPDMTimeTrackingThread.join(); + return static_cast(&sIOSUModuleNNPDM); } }; diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.h b/src/Cafe/IOSU/PDM/iosu_pdm.h index fbafbc02..0dd8a39d 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.h +++ b/src/Cafe/IOSU/PDM/iosu_pdm.h @@ -1,13 +1,10 @@ #pragma once +#include "Cafe/IOSU/iosu_types_common.h" namespace iosu { namespace pdm { - void Initialize(); - void StartTrackingTime(uint64 titleId); - void Stop(); - inline constexpr size_t NUM_PLAY_STATS_ENTRIES = 256; inline constexpr size_t NUM_PLAY_DIARY_ENTRIES_MAX = 18250; // 0x474A @@ -34,5 +31,7 @@ namespace iosu }; bool GetStatForGamelist(uint64 titleId, GameListStat& stat); + + IOSUModule* GetModule(); }; }; \ No newline at end of file From bffeb818d1e2770c3f47e7290deffdc5136c6b90 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 22 Nov 2023 17:57:20 +0100 Subject: [PATCH 077/101] GfxPack: Refactor + better unicode support --- src/Cafe/GameProfile/GameProfile.cpp | 2 +- src/Cafe/GraphicPack/GraphicPack2.cpp | 63 +++++++++---------- src/Cafe/GraphicPack/GraphicPack2.h | 18 +++--- src/Cafe/GraphicPack/GraphicPack2Patches.cpp | 15 +---- .../GraphicPack/GraphicPack2PatchesParser.cpp | 2 +- src/gui/GraphicPacksWindow2.cpp | 45 ++++++------- 6 files changed, 64 insertions(+), 81 deletions(-) diff --git a/src/Cafe/GameProfile/GameProfile.cpp b/src/Cafe/GameProfile/GameProfile.cpp index d068237e..ee92107a 100644 --- a/src/Cafe/GameProfile/GameProfile.cpp +++ b/src/Cafe/GameProfile/GameProfile.cpp @@ -209,7 +209,7 @@ bool GameProfile::Load(uint64_t title_id) m_gameName = std::string(game_name.begin(), game_name.end()); trim(m_gameName.value()); } - IniParser iniParser(*profileContents, gameProfilePath.string()); + IniParser iniParser(*profileContents, _pathToUtf8(gameProfilePath)); // parse ini while (iniParser.NextSection()) { diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index 72e301c4..365e6e3e 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -28,7 +28,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath) return; std::vector rulesData; fs_rules->extract(rulesData); - IniParser iniParser(rulesData, rulesPath.string()); + IniParser iniParser(rulesData, _pathToUtf8(rulesPath)); if (!iniParser.NextSection()) { @@ -51,10 +51,9 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath) cemuLog_log(LogType::Force, "{}: Unable to parse version", _pathToUtf8(rulesPath)); return; } - if (versionNum > GP_LEGACY_VERSION) { - GraphicPack2::LoadGraphicPack(_pathToUtf8(rulesPath), iniParser); + GraphicPack2::LoadGraphicPack(rulesPath, iniParser); return; } } @@ -79,22 +78,22 @@ void GraphicPack2::LoadAll() } } -bool GraphicPack2::LoadGraphicPack(const std::string& filename, IniParser& rules) +bool GraphicPack2::LoadGraphicPack(const fs::path& rulesPath, IniParser& rules) { try { - auto gp = std::make_shared(filename, rules); + auto gp = std::make_shared(rulesPath, rules); // check if enabled and preset set const auto& config_entries = g_config.data().graphic_pack_entries; // legacy absolute path checking for not breaking compatibility - auto file = gp->GetFilename2(); + auto file = gp->GetRulesPath(); auto it = config_entries.find(file.lexically_normal()); if (it == config_entries.cend()) { // check for relative path - it = config_entries.find(MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename2()).lexically_normal()); + it = config_entries.find(_utf8ToPath(gp->GetNormalizedPathString())); } if (it != config_entries.cend()) @@ -145,7 +144,7 @@ bool GraphicPack2::DeactivateGraphicPack(const std::shared_ptr& gr const auto it = std::find_if(s_active_graphic_packs.begin(), s_active_graphic_packs.end(), [graphic_pack](const GraphicPackPtr& gp) { - return gp->GetFilename() == graphic_pack->GetFilename(); + return gp->GetNormalizedPathString() == graphic_pack->GetNormalizedPathString(); } ); @@ -173,12 +172,12 @@ void GraphicPack2::ActivateForCurrentTitle() { if (gp->GetPresets().empty()) { - cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetPath()); + cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetVirtualPath()); } else { std::string logLine; - logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetPath())); + logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetVirtualPath())); bool isFirst = true; for (auto& itr : gp->GetPresets()) { @@ -249,8 +248,8 @@ std::unordered_map GraphicPack2::ParsePres return vars; } -GraphicPack2::GraphicPack2(std::string filename, IniParser& rules) - : m_filename(std::move(filename)) +GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) + : m_rulesPath(std::move(rulesPath)) { // we're already in [Definition] auto option_version = rules.FindOption("version"); @@ -259,7 +258,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules) m_version = StringHelpers::ToInt(*option_version, -1); if (m_version < 0) { - cemuLog_log(LogType::Force, "{}: Invalid version", m_filename); + cemuLog_log(LogType::Force, "{}: Invalid version", _pathToUtf8(m_rulesPath)); throw std::exception(); } @@ -305,7 +304,7 @@ GraphicPack2::GraphicPack2(std::string filename, IniParser& rules) cemuLog_log(LogType::Force, "[Definition] section from '{}' graphic pack must contain option: path", gp_name_log.has_value() ? *gp_name_log : "Unknown"); throw std::exception(); } - m_path = *option_path; + m_virtualPath = *option_path; auto option_gp_name = rules.FindOption("name"); if (option_gp_name) @@ -508,6 +507,11 @@ bool GraphicPack2::Reload() return Activate(); } +std::string GraphicPack2::GetNormalizedPathString() const +{ + return _pathToUtf8(MakeRelativePath(ActiveSettings::GetUserDataPath(), GetRulesPath()).lexically_normal()); +} + bool GraphicPack2::ContainsTitleId(uint64_t title_id) const { const auto it = std::find_if(m_title_ids.begin(), m_title_ids.end(), [title_id](uint64 id) { return id == title_id; }); @@ -650,7 +654,7 @@ bool GraphicPack2::SetActivePreset(std::string_view category, std::string_view n void GraphicPack2::LoadShaders() { - fs::path path(m_filename); + fs::path path = GetRulesPath(); for (auto& it : fs::directory_iterator(path.remove_filename())) { if (!is_regular_file(it)) @@ -676,7 +680,7 @@ void GraphicPack2::LoadShaders() { std::ifstream file(p); if (!file.is_open()) - throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str()); + throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename()))); file.seekg(0, std::ios::end); m_output_shader_source.reserve(file.tellg()); @@ -689,7 +693,7 @@ void GraphicPack2::LoadShaders() { std::ifstream file(p); if (!file.is_open()) - throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str()); + throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename()))); file.seekg(0, std::ios::end); m_upscaling_shader_source.reserve(file.tellg()); @@ -702,7 +706,7 @@ void GraphicPack2::LoadShaders() { std::ifstream file(p); if (!file.is_open()) - throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str()); + throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename()))); file.seekg(0, std::ios::end); m_downscaling_shader_source.reserve(file.tellg()); @@ -805,7 +809,7 @@ void GraphicPack2::AddConstantsForCurrentPreset(ExpressionParser& ep) } } -void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC) +void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC) { uint64 currentTitleId = CafeSystem::GetForegroundTitleId(); uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull; @@ -833,7 +837,7 @@ void GraphicPack2::LoadReplacedFiles() return; m_patchedFilesLoaded = true; - fs::path gfxPackPath = _utf8ToPath(m_filename); + fs::path gfxPackPath = GetRulesPath(); gfxPackPath = gfxPackPath.remove_filename(); // /content/ @@ -843,10 +847,9 @@ void GraphicPack2::LoadReplacedFiles() std::error_code ec; if (fs::exists(contentPath, ec)) { - std::wstring internalPath(L"/vol/content/"); // setup redirections fscDeviceRedirect_map(); - _iterateReplacedFiles(contentPath, internalPath, false); + _iterateReplacedFiles(contentPath, false); } // /aoc/ fs::path aocPath(gfxPackPath); @@ -857,13 +860,9 @@ void GraphicPack2::LoadReplacedFiles() uint64 aocTitleId = CafeSystem::GetForegroundTitleId(); aocTitleId = aocTitleId & 0xFFFFFFFFULL; aocTitleId |= 0x0005000c00000000ULL; - wchar_t internalAocPath[128]; - swprintf(internalAocPath, sizeof(internalAocPath)/sizeof(wchar_t), L"/aoc/%016llx/", aocTitleId); - - std::wstring internalPath(internalAocPath); // setup redirections fscDeviceRedirect_map(); - _iterateReplacedFiles(aocPath, internalPath, true); + _iterateReplacedFiles(aocPath, true); } } @@ -886,14 +885,14 @@ bool GraphicPack2::Activate() return false; } - FileStream* fs_rules = FileStream::openFile2(_utf8ToPath(m_filename)); + FileStream* fs_rules = FileStream::openFile2(m_rulesPath); if (!fs_rules) return false; std::vector rulesData; fs_rules->extract(rulesData); delete fs_rules; - IniParser rules({ (char*)rulesData.data(), rulesData.size()}, m_filename); + IniParser rules({ (char*)rulesData.data(), rulesData.size()}, GetNormalizedPathString()); // load rules try @@ -947,7 +946,7 @@ bool GraphicPack2::Activate() else if (anisotropyValue == 16) rule.overwrite_settings.anistropic_value = 4; else - cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, m_filename); + cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, GetNormalizedPathString()); } m_texture_rules.emplace_back(rule); } @@ -992,11 +991,11 @@ bool GraphicPack2::Activate() if (LatteTiming_getCustomVsyncFrequency(globalCustomVsyncFreq)) { if (customVsyncFreq != globalCustomVsyncFreq) - cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetPath()); + cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetVirtualPath()); } else { - cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetPath()); + cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetVirtualPath()); LatteTiming_setCustomVsyncFrequency(customVsyncFreq); } } diff --git a/src/Cafe/GraphicPack/GraphicPack2.h b/src/Cafe/GraphicPack/GraphicPack2.h index 6396ecc7..6b07cce9 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.h +++ b/src/Cafe/GraphicPack/GraphicPack2.h @@ -97,20 +97,20 @@ public: }; using PresetPtr = std::shared_ptr; - GraphicPack2(std::string filename, IniParser& rules); + GraphicPack2(fs::path rulesPath, IniParser& rules); bool IsEnabled() const { return m_enabled; } bool IsActivated() const { return m_activated; } sint32 GetVersion() const { return m_version; } - const std::string& GetFilename() const { return m_filename; } - const fs::path GetFilename2() const { return fs::path(m_filename); } + const fs::path GetRulesPath() const { return m_rulesPath; } + std::string GetNormalizedPathString() const; bool RequiresRestart(bool changeEnableState, bool changePreset); bool Reload(); bool HasName() const { return !m_name.empty(); } - const std::string& GetName() const { return m_name.empty() ? m_path : m_name; } - const std::string& GetPath() const { return m_path; } + const std::string& GetName() const { return m_name.empty() ? m_virtualPath : m_name; } + const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy const std::string& GetDescription() const { return m_description; } bool IsDefaultEnabled() const { return m_default_enabled; } @@ -164,7 +164,7 @@ public: static const std::vector>& GetGraphicPacks() { return s_graphic_packs; } static const std::vector>& GetActiveGraphicPacks() { return s_active_graphic_packs; } static void LoadGraphicPack(fs::path graphicPackPath); - static bool LoadGraphicPack(const std::string& filename, class IniParser& rules); + static bool LoadGraphicPack(const fs::path& rulesPath, class IniParser& rules); static bool ActivateGraphicPack(const std::shared_ptr& graphic_pack); static bool DeactivateGraphicPack(const std::shared_ptr& graphic_pack); static void ClearGraphicPacks(); @@ -208,11 +208,11 @@ private: parser.TryAddConstant(var.first, (TType)var.second.second); } - std::string m_filename; + fs::path m_rulesPath; sint32 m_version; std::string m_name; - std::string m_path; + std::string m_virtualPath; std::string m_description; bool m_default_enabled = false; @@ -257,7 +257,7 @@ private: CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type) const; void ApplyShaderPresets(std::string& shader_source) const; void LoadReplacedFiles(); - void _iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC); + void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC); // ram mappings std::vector> m_ramMappings; diff --git a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp index 5c79630c..2c067484 100644 --- a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp @@ -71,19 +71,8 @@ void PatchErrorHandler::showStageErrorMessageBox() // returns true if at least one file was found even if it could not be successfully parsed bool GraphicPack2::LoadCemuPatches() { - // todo - once we have updated to C++20 we can replace these with the new std::string functions - auto startsWith = [](const std::wstring& str, const std::wstring& prefix) - { - return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix); - }; - - auto endsWith = [](const std::wstring& str, const std::wstring& suffix) - { - return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); - }; - bool foundPatches = false; - fs::path path(_utf8ToPath(m_filename)); + fs::path path(m_rulesPath); path.remove_filename(); for (auto& p : fs::directory_iterator(path)) { @@ -129,7 +118,7 @@ void GraphicPack2::LoadPatchFiles() if (LoadCemuPatches()) return; // exit if at least one Cemu style patch file was found // fall back to Cemuhook patches.txt to guarantee backward compatibility - fs::path path(_utf8ToPath(m_filename)); + fs::path path(m_rulesPath); path.remove_filename(); path.append("patches.txt"); FileStream* patchFile = FileStream::openFile2(path); diff --git a/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp b/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp index d011a10b..05f8c696 100644 --- a/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp @@ -25,7 +25,7 @@ sint32 GraphicPack2::GetLengthWithoutComment(const char* str, size_t length) void GraphicPack2::LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg) { - cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", this->GetFilename()); + cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", _pathToUtf8(this->GetRulesPath())); if(lineNumber >= 0) cemuLog_log(LogType::Force, fmt::format("Line {0}: {1}", lineNumber, errorMsg)); else diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index 13fec49a..78b344d5 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -64,7 +64,7 @@ void GraphicPacksWindow2::FillGraphicPackList() const { bool found = false; - if (boost::icontains(p->GetPath(), m_filter)) + if (boost::icontains(p->GetVirtualPath(), m_filter)) found = true; else { @@ -82,7 +82,7 @@ void GraphicPacksWindow2::FillGraphicPackList() const continue; } - const auto& path = p->GetPath(); + const auto& path = p->GetVirtualPath(); auto tokens = TokenizeView(path, '/'); auto node = root; for(size_t i=0; iGetFilename())).lexically_normal(); + auto filename = _utf8ToPath(gp->GetNormalizedPathString()); if (gp->IsEnabled()) { data.graphic_pack_entries.try_emplace(filename); @@ -603,34 +603,29 @@ void GraphicPacksWindow2::OnCheckForUpdates(wxCommandEvent& event) { if (!CafeSystem::IsTitleRunning()) { - std::vector old_packs = GraphicPack2::GetGraphicPacks(); + // remember virtual paths of all the enabled packs + std::map previouslyEnabledPacks; + for(auto& it : GraphicPack2::GetGraphicPacks()) + { + if(it->IsEnabled()) + previouslyEnabledPacks.emplace(it->GetNormalizedPathString(), it->GetVirtualPath()); + } + // reload graphic packs RefreshGraphicPacks(); FillGraphicPackList(); - - // check if enabled graphic packs are lost: - const auto& new_packs = GraphicPack2::GetGraphicPacks(); - std::stringstream lost_packs; - for(const auto& p : old_packs) + // remove packs which are still present + for(auto& it : GraphicPack2::GetGraphicPacks()) + previouslyEnabledPacks.erase(it->GetNormalizedPathString()); + if(!previouslyEnabledPacks.empty()) { - if (!p->IsEnabled()) - continue; - - const auto it = std::find_if(new_packs.cbegin(), new_packs.cend(), [&p](const auto& gp) - { - return gp->GetFilename() == p->GetFilename(); - }); - - if(it == new_packs.cend()) + std::string lost_packs; + for(auto& it : previouslyEnabledPacks) { - lost_packs << p->GetPath() << "\n"; + lost_packs.append(it.second); + lost_packs.push_back('\n'); } - } - - const auto lost_packs_str = lost_packs.str(); - if (!lost_packs_str.empty()) - { wxString message = _("This update removed or renamed the following graphic packs:"); - message << "\n \n" << lost_packs_str << " \n" << _("You may need to set them up again."); + message << "\n \n" << wxString::FromUTF8(lost_packs) << " \n" << _("You may need to set them up again."); wxMessageBox(message, _("Warning"), wxOK | wxCENTRE | wxICON_INFORMATION, this); } } From e7fa8ec0c63f37a93fbcb0c7b372d5fb8640091e Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:16:17 +0100 Subject: [PATCH 078/101] Vulkan: Properly shut down compilation threads --- .../Renderer/Vulkan/RendererShaderVk.cpp | 23 ++++++++++++++++--- .../Latte/Renderer/Vulkan/RendererShaderVk.h | 3 +++ .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 5 ++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp index e4c87d62..8460c8b5 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp @@ -139,7 +139,7 @@ public: } } - ~_ShaderVkThreadPool() + void StopThreads() { m_shutdownThread.store(true); for (uint32 i = 0; i < s_threads.size(); ++i) @@ -149,6 +149,11 @@ public: s_threads.clear(); } + ~_ShaderVkThreadPool() + { + StopThreads(); + } + void CompilerThreadFunc() { while (!m_shutdownThread.load(std::memory_order::relaxed)) @@ -176,6 +181,8 @@ public: } } + bool HasThreadsRunning() const { return !m_shutdownThread; } + public: std::vector s_threads; @@ -195,8 +202,8 @@ RendererShaderVk::RendererShaderVk(ShaderType type, uint64 baseHash, uint64 auxH m_compilationState.setValue(COMPILATION_STATE::QUEUED); ShaderVkThreadPool.s_compilationQueue.push_back(this); ShaderVkThreadPool.s_compilationQueueCount.increment(); - ShaderVkThreadPool.StartThreads(); ShaderVkThreadPool.s_compilationQueueMutex.unlock(); + cemu_assert_debug(ShaderVkThreadPool.HasThreadsRunning()); // make sure .StartThreads() was called } RendererShaderVk::~RendererShaderVk() @@ -204,6 +211,16 @@ RendererShaderVk::~RendererShaderVk() VulkanRenderer::GetInstance()->destroyShader(this); } +void RendererShaderVk::Init() +{ + ShaderVkThreadPool.StartThreads(); +} + +void RendererShaderVk::Shutdown() +{ + ShaderVkThreadPool.StopThreads(); +} + sint32 RendererShaderVk::GetUniformLocation(const char* name) { cemu_assert_suspicious(); @@ -457,4 +474,4 @@ void RendererShaderVk::ShaderCacheLoading_Close() { delete s_spirvCache; s_spirvCache = nullptr; -} \ No newline at end of file +} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h index 561145f9..207ea3ea 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h @@ -28,6 +28,9 @@ public: RendererShaderVk(ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& glslCode); virtual ~RendererShaderVk(); + static void Init(); + static void Shutdown(); + sint32 GetUniformLocation(const char* name) override; void SetUniform1iv(sint32 location, void* data, sint32 count) override; void SetUniform2fv(sint32 location, void* data, sint32 count) override; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 052ca21a..5b4dd739 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -591,6 +591,9 @@ VulkanRenderer::VulkanRenderer() { //cemuLog_log(LogType::Force, "Disable surface copies via buffer (Requires 2GB. Has only {}MB available)", availableSurfaceCopyBufferMem / 1024ull / 1024ull); } + + // start compilation threads + RendererShaderVk::Init(); } VulkanRenderer::~VulkanRenderer() @@ -598,6 +601,8 @@ VulkanRenderer::~VulkanRenderer() SubmitCommandBuffer(); WaitDeviceIdle(); WaitCommandBufferFinished(GetCurrentCommandBufferId()); + // shut down compilation threads + RendererShaderVk::Shutdown(); // shut down pipeline save thread m_destructionRequested = true; m_pipeline_cache_semaphore.notify(); From dee764473db26462a898aae8ea73c65a9cbafda1 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:29:56 +0100 Subject: [PATCH 079/101] Latte: Small refactor for GLSL texture coord handling Also adds support for 2D textures coordinates with source as 0.0 or 1.0 literals instead of GPRs. Seen in shaders generated by CafeGLSL --- .../LatteDecompilerEmitGLSL.cpp | 108 ++++++++---------- 1 file changed, 46 insertions(+), 62 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index 334b4855..a37ba011 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -507,7 +507,7 @@ void _emitRegisterAccessCode(LatteDecompilerShaderContext* shaderContext, sint32 { _emitTypeConversionPrefix(shaderContext, registerElementDataType, dataType); } - if(shaderContext->typeTracker.useArrayGPRs ) + if (shaderContext->typeTracker.useArrayGPRs) src->add("R"); else src->addFmt("R{}", gprIndex); @@ -540,6 +540,26 @@ void _emitRegisterAccessCode(LatteDecompilerShaderContext* shaderContext, sint32 _emitTypeConversionSuffix(shaderContext, registerElementDataType, dataType); } +// optimized variant of _emitRegisterAccessCode for raw one channel reads +void _emitRegisterChannelAccessCode(LatteDecompilerShaderContext* shaderContext, sint32 gprIndex, sint32 channel, sint32 dataType) +{ + cemu_assert_debug(gprIndex >= 0 && gprIndex <= 127); + cemu_assert_debug(channel >= 0 && channel < 4); + StringBuf* src = shaderContext->shaderSource; + sint32 registerElementDataType = shaderContext->typeTracker.defaultDataType; + _emitTypeConversionPrefix(shaderContext, registerElementDataType, dataType); + if (shaderContext->typeTracker.useArrayGPRs) + src->add("R"); + else + src->addFmt("R{}", gprIndex); + _appendRegisterTypeSuffix(src, registerElementDataType); + if (shaderContext->typeTracker.useArrayGPRs) + src->addFmt("[{}]", gprIndex); + src->add("."); + src->add(_getElementStrByIndex(channel)); + _emitTypeConversionSuffix(shaderContext, registerElementDataType, dataType); +} + void _emitALURegisterInputAccessCode(LatteDecompilerShaderContext* shaderContext, LatteDecompilerALUInstruction* aluInstruction, sint32 operandIndex) { StringBuf* src = shaderContext->shaderSource; @@ -2129,63 +2149,31 @@ void _emitALUClauseCode(LatteDecompilerShaderContext* shaderContext, LatteDecomp /* * Emits code to access one component (xyzw) of the texture coordinate input vector */ -void _emitTEXSampleCoordInputComponent(LatteDecompilerShaderContext* shaderContext, LatteDecompilerTEXInstruction* texInstruction, sint32 componentIndex, sint32 varType) +void _emitTEXSampleCoordInputComponent(LatteDecompilerShaderContext* shaderContext, LatteDecompilerTEXInstruction* texInstruction, sint32 componentIndex, sint32 interpretSrcAsType) { + cemu_assert(componentIndex >= 0 && componentIndex < 4); + cemu_assert_debug(interpretSrcAsType == LATTE_DECOMPILER_DTYPE_SIGNED_INT || interpretSrcAsType == LATTE_DECOMPILER_DTYPE_FLOAT); StringBuf* src = shaderContext->shaderSource; - if( componentIndex >= 4 ) + sint32 elementSel = texInstruction->textureFetch.srcSel[componentIndex]; + if (elementSel < 4) { - debugBreakpoint(); + _emitRegisterChannelAccessCode(shaderContext, texInstruction->srcGpr, elementSel, interpretSrcAsType); return; } - sint32 elementSel = texInstruction->textureFetch.srcSel[componentIndex]; const char* resultElemTable[4] = {"x","y","z","w"}; - if( varType == LATTE_DECOMPILER_DTYPE_SIGNED_INT ) + if(interpretSrcAsType == LATTE_DECOMPILER_DTYPE_SIGNED_INT ) { - if (elementSel < 4) - { - if (shaderContext->typeTracker.defaultDataType == LATTE_DECOMPILER_DTYPE_SIGNED_INT) - src->addFmt("{}.{}", _getRegisterVarName(shaderContext, texInstruction->srcGpr), resultElemTable[elementSel]); - else if (shaderContext->typeTracker.defaultDataType == LATTE_DECOMPILER_DTYPE_FLOAT) - src->addFmt("floatBitsToInt({}.{})", _getRegisterVarName(shaderContext, texInstruction->srcGpr), resultElemTable[elementSel]); - else - { - cemu_assert_unimplemented(); - } - } - else if( elementSel == 4 ) + if( elementSel == 4 ) src->add("floatBitsToInt(0.0)"); else if( elementSel == 5 ) src->add("floatBitsToInt(1.0)"); - else - { - cemu_assert_unimplemented(); - } } - else if( varType == LATTE_DECOMPILER_DTYPE_FLOAT ) + else if(interpretSrcAsType == LATTE_DECOMPILER_DTYPE_FLOAT ) { - if (elementSel < 4) - { - if (shaderContext->typeTracker.defaultDataType == LATTE_DECOMPILER_DTYPE_SIGNED_INT) - src->addFmt("intBitsToFloat({}.{})", _getRegisterVarName(shaderContext, texInstruction->srcGpr), resultElemTable[elementSel]); - else if (shaderContext->typeTracker.defaultDataType == LATTE_DECOMPILER_DTYPE_FLOAT) - src->addFmt("{}.{}", _getRegisterVarName(shaderContext, texInstruction->srcGpr), resultElemTable[elementSel]); - else - { - cemu_assert_unimplemented(); - } - } - else if( elementSel == 4 ) - src->addFmt("0.0"); + if( elementSel == 4 ) + src->add("0.0"); else if( elementSel == 5 ) - src->addFmt("1.0"); - else - { - cemu_assert_unimplemented(); - } - } - else - { - cemu_assert_unimplemented(); + src->add("1.0"); } } @@ -2430,10 +2418,6 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt cemu_assert_unimplemented(); src->add("texture("); } - if( texInstruction->textureFetch.srcSel[0] >= 4 ) - cemu_assert_unimplemented(); - if( texInstruction->textureFetch.srcSel[1] >= 4 ) - cemu_assert_unimplemented(); src->addFmt("{}{}, ", _getTextureUnitVariablePrefixName(shaderContext->shader->shaderType), texInstruction->textureFetch.textureIndex); // for textureGather() add shift (todo: depends on rounding mode set in sampler registers?) @@ -2455,7 +2439,7 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt } } - + const sint32 texCoordDataType = (texOpcode == GPU7_TEX_INST_LD) ? LATTE_DECOMPILER_DTYPE_SIGNED_INT : LATTE_DECOMPILER_DTYPE_FLOAT; if(useTexelCoordinates) { // handle integer coordinates for texelFetch @@ -2463,9 +2447,9 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt { src->add("ivec2("); src->add("vec2("); - _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 0, (texOpcode == GPU7_TEX_INST_LD) ? LATTE_DECOMPILER_DTYPE_SIGNED_INT : LATTE_DECOMPILER_DTYPE_FLOAT); + _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 0, texCoordDataType); src->addFmt(", "); - _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 1, (texOpcode == GPU7_TEX_INST_LD) ? LATTE_DECOMPILER_DTYPE_SIGNED_INT : LATTE_DECOMPILER_DTYPE_FLOAT); + _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 1, texCoordDataType); src->addFmt(")*uf_tex{}Scale", texInstruction->textureFetch.textureIndex); // close vec2 and scale @@ -2485,7 +2469,7 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt else cemu_assert_debug(false); } - else + else /* useTexelCoordinates == false */ { // float coordinates if ( (texOpcode == GPU7_TEX_INST_SAMPLE_C || texOpcode == GPU7_TEX_INST_SAMPLE_C_L || texOpcode == GPU7_TEX_INST_SAMPLE_C_LZ) ) @@ -2549,10 +2533,8 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt else if( texDim == Latte::E_DIM::DIM_CUBEMAP ) { // 2 coords + faceId - if( texInstruction->textureFetch.srcSel[0] >= 4 || texInstruction->textureFetch.srcSel[1] >= 4 ) - { - debugBreakpoint(); - } + cemu_assert_debug(texInstruction->textureFetch.srcSel[0] < 4); + cemu_assert_debug(texInstruction->textureFetch.srcSel[1] < 4); src->add("vec4("); src->addFmt("redcCUBEReverse({},", _getTexGPRAccess(shaderContext, texInstruction->srcGpr, LATTE_DECOMPILER_DTYPE_FLOAT, texInstruction->textureFetch.srcSel[0], texInstruction->textureFetch.srcSel[1], -1, -1, tempBuffer0)); _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 2, LATTE_DECOMPILER_DTYPE_SIGNED_INT); @@ -2567,8 +2549,11 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt else { // 2 coords - src->add(_getTexGPRAccess(shaderContext, texInstruction->srcGpr, LATTE_DECOMPILER_DTYPE_FLOAT, texInstruction->textureFetch.srcSel[0], texInstruction->textureFetch.srcSel[1], -1, -1, tempBuffer0)); - + src->add("vec2("); + _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 0, LATTE_DECOMPILER_DTYPE_FLOAT); + src->add(","); + _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 1, LATTE_DECOMPILER_DTYPE_FLOAT); + src->add(")"); // avoid truncate to effectively round downwards on texel edges if (ActiveSettings::ForceSamplerRoundToPrecision()) src->addFmt("+ vec2(1.0)/vec2(textureSize({}{}, 0))/512.0", _getTextureUnitVariablePrefixName(shaderContext->shader->shaderType), texInstruction->textureFetch.textureIndex); @@ -2576,9 +2561,8 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt // lod or lod bias parameter if( texOpcode == GPU7_TEX_INST_SAMPLE_L || texOpcode == GPU7_TEX_INST_SAMPLE_LB || texOpcode == GPU7_TEX_INST_SAMPLE_C_L) { - if( texInstruction->textureFetch.srcSel[3] >= 4 ) - debugBreakpoint(); - src->addFmt(",{}", _getTexGPRAccess(shaderContext, texInstruction->srcGpr, LATTE_DECOMPILER_DTYPE_FLOAT, texInstruction->textureFetch.srcSel[3], -1, -1, -1, tempBuffer0)); + src->add(","); + _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 3, LATTE_DECOMPILER_DTYPE_FLOAT); } else if( texOpcode == GPU7_TEX_INST_SAMPLE_LZ || texOpcode == GPU7_TEX_INST_SAMPLE_C_LZ ) { From 646835346c1eb6d29f632843b5e07e90a804d62c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:50:16 +0100 Subject: [PATCH 080/101] Latte: Refactor legacy OpenGL code for shader binding --- src/Cafe/HW/Latte/Core/LatteShader.cpp | 16 +------------ .../Latte/Renderer/OpenGL/OpenGLRenderer.cpp | 24 +++---------------- .../HW/Latte/Renderer/OpenGL/OpenGLRenderer.h | 14 ++++------- .../Renderer/OpenGL/OpenGLRendererCore.cpp | 15 ++++++++++++ .../Renderer/OpenGL/RendererShaderGL.cpp | 5 ---- .../Latte/Renderer/OpenGL/RendererShaderGL.h | 1 - src/Cafe/HW/Latte/Renderer/Renderer.h | 2 -- .../HW/Latte/Renderer/RendererOuputShader.cpp | 6 ----- .../HW/Latte/Renderer/RendererOuputShader.h | 1 - src/Cafe/HW/Latte/Renderer/RendererShader.h | 3 +-- .../Renderer/Vulkan/RendererShaderVk.cpp | 5 ---- .../Latte/Renderer/Vulkan/RendererShaderVk.h | 1 - .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 11 --------- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 2 -- 14 files changed, 25 insertions(+), 81 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteShader.cpp b/src/Cafe/HW/Latte/Core/LatteShader.cpp index c0ad06a1..503fb664 100644 --- a/src/Cafe/HW/Latte/Core/LatteShader.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShader.cpp @@ -838,7 +838,6 @@ LatteDecompilerShader* LatteShader_CompileSeparablePixelShader(uint64 baseHash, void LatteSHRC_UpdateVertexShader(uint8* vertexShaderPtr, uint32 vertexShaderSize, bool usesGeometryShader) { // todo - should include VTX_SEMANTIC table in state - LatteSHRC_UpdateVSBaseHash(vertexShaderPtr, vertexShaderSize, usesGeometryShader); uint64 vsAuxHash = 0; auto itBaseShader = sVertexShaders.find(_shaderBaseHash_vs); @@ -855,15 +854,13 @@ void LatteSHRC_UpdateVertexShader(uint8* vertexShaderPtr, uint32 vertexShaderSiz LatteGPUState.activeShaderHasError = true; return; } - g_renderer->shader_bind(vertexShader->shader); _activeVertexShader = vertexShader; } void LatteSHRC_UpdateGeometryShader(bool usesGeometryShader, uint8* geometryShaderPtr, uint32 geometryShaderSize, uint8* geometryCopyShader, uint32 geometryCopyShaderSize) { - if (usesGeometryShader == false || _activeVertexShader == nullptr) + if (!usesGeometryShader || !_activeVertexShader) { - g_renderer->shader_unbind(RendererShader::ShaderType::kGeometry); _shaderBaseHash_gs = 0; _activeGeometryShader = nullptr; return; @@ -887,21 +884,11 @@ void LatteSHRC_UpdateGeometryShader(bool usesGeometryShader, uint8* geometryShad LatteGPUState.activeShaderHasError = true; return; } - g_renderer->shader_bind(geometryShader->shader); _activeGeometryShader = geometryShader; } void LatteSHRC_UpdatePixelShader(uint8* pixelShaderPtr, uint32 pixelShaderSize, bool usesGeometryShader) { - if (LatteGPUState.contextRegister[mmVGT_STRMOUT_EN] != 0 && g_renderer->GetType() == RendererAPI::OpenGL) - { - if (_activePixelShader) - { - g_renderer->shader_unbind(RendererShader::ShaderType::kFragment); - _activePixelShader = nullptr; - } - return; - } LatteSHRC_UpdatePSBaseHash(pixelShaderPtr, pixelShaderSize, usesGeometryShader); uint64 psAuxHash = 0; auto itBaseShader = sPixelShaders.find(_shaderBaseHash_ps); @@ -918,7 +905,6 @@ void LatteSHRC_UpdatePixelShader(uint8* pixelShaderPtr, uint32 pixelShaderSize, LatteGPUState.activeShaderHasError = true; return; } - g_renderer->shader_bind(pixelShader->shader); _activePixelShader = pixelShader; } diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index 5269be64..01068a3d 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -275,10 +275,6 @@ void OpenGLRenderer::Initialize() cemuLog_log(LogType::Force, "ARB_copy_image: {}", (glCopyImageSubData != NULL) ? "available" : "not supported"); cemuLog_log(LogType::Force, "NV_depth_buffer_float: {}", (glDepthRangedNV != NULL) ? "available" : "not supported"); - // generate default frame buffer - glGenFramebuffers(1, &m_defaultFramebufferId); - catchOpenGLError(); - // enable framebuffer SRGB support glEnable(GL_FRAMEBUFFER_SRGB); @@ -566,10 +562,9 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu sint32 effectiveHeight; LatteTexture_getEffectiveSize(texView->baseTexture, &effectiveWidth, &effectiveHeight, nullptr, 0); - g_renderer->shader_unbind(RendererShader::ShaderType::kVertex); - g_renderer->shader_unbind(RendererShader::ShaderType::kGeometry); - g_renderer->shader_unbind(RendererShader::ShaderType::kFragment); - shader->Bind(); + shader_unbind(RendererShader::ShaderType::kGeometry); + shader_bind(shader->GetVertexShader()); + shader_bind(shader->GetFragmentShader()); shader->SetUniformParameters(*texView, { effectiveWidth, effectiveHeight }, { imageWidth, imageHeight }); // set viewport @@ -1433,31 +1428,25 @@ RendererShader* OpenGLRenderer::shader_create(RendererShader::ShaderType type, u void OpenGLRenderer::shader_bind(RendererShader* shader) { auto shaderGL = (RendererShaderGL*)shader; - GLbitfield shaderBit; - const auto program = shaderGL->GetProgram(); - switch(shader->GetType()) { case RendererShader::ShaderType::kVertex: if (program == prevVertexShaderProgram) return; - shaderBit = GL_VERTEX_SHADER_BIT; prevVertexShaderProgram = program; break; case RendererShader::ShaderType::kFragment: if (program == prevPixelShaderProgram) return; - shaderBit = GL_FRAGMENT_SHADER_BIT; prevPixelShaderProgram = program; break; case RendererShader::ShaderType::kGeometry: if (program == prevGeometryShaderProgram) return; - shaderBit = GL_GEOMETRY_SHADER_BIT; prevGeometryShaderProgram = program; break; @@ -1470,13 +1459,6 @@ void OpenGLRenderer::shader_bind(RendererShader* shader) catchOpenGLError(); } -void OpenGLRenderer::shader_bind(GLuint program, GLbitfield shaderType) -{ - catchOpenGLError(); - glUseProgramStages(m_pipeline, shaderType, program); - catchOpenGLError(); -} - void OpenGLRenderer::shader_unbind(RendererShader::ShaderType shaderType) { switch (shaderType) { diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h index 600985ff..b789e2a7 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h @@ -127,9 +127,8 @@ public: // shader RendererShader* shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool isGameShader, bool isGfxPackShader) override; - void shader_bind(RendererShader* shader) override; - void shader_bind(GLuint program, GLbitfield shaderType); - void shader_unbind(RendererShader::ShaderType shaderType) override; + void shader_bind(RendererShader* shader); + void shader_unbind(RendererShader::ShaderType shaderType); // streamout void streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) override; @@ -165,7 +164,6 @@ private: void texture_syncSliceSpecialBC4(LatteTexture* srcTexture, sint32 srcSliceIndex, sint32 srcMipIndex, LatteTexture* dstTexture, sint32 dstSliceIndex, sint32 dstMipIndex); void texture_syncSliceSpecialIntegerToBC3(LatteTexture* srcTexture, sint32 srcSliceIndex, sint32 srcMipIndex, LatteTexture* dstTexture, sint32 dstSliceIndex, sint32 dstMipIndex); - GLuint m_defaultFramebufferId; GLuint m_pipeline = 0; bool m_isPadViewContext{}; @@ -216,8 +214,6 @@ private: uint32 prevLogicOp = 0; uint32 prevBlendColorConstant[4] = { 0 }; uint8 prevAlphaTestEnable = 0; - uint8 prevAlphaTestFunc = 0; - uint32 prevAlphaTestRefU32 = 0; bool prevDepthEnable = 0; bool prevDepthWriteEnable = 0; Latte::LATTE_DB_DEPTH_CONTROL::E_ZFUNC prevDepthFunc = (Latte::LATTE_DB_DEPTH_CONTROL::E_ZFUNC)-1; @@ -263,9 +259,9 @@ private: std::vector list_queryCacheOcclusion; // cache for unused queries // resource garbage collection - struct bufferCacheReleaseQueueEntry_t + struct BufferCacheReleaseQueueEntry { - bufferCacheReleaseQueueEntry_t(VirtualBufferHeap_t* heap, VirtualBufferHeapEntry_t* entry) : m_heap(heap), m_entry(entry) {}; + BufferCacheReleaseQueueEntry(VirtualBufferHeap_t* heap, VirtualBufferHeapEntry_t* entry) : m_heap(heap), m_entry(entry) {}; void free() { @@ -279,7 +275,7 @@ private: struct { sint32 index; - std::vector bufferCacheEntries; + std::vector bufferCacheEntries; }m_destructionQueues; }; diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp index d5cec237..f78b8bd6 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp @@ -912,6 +912,21 @@ void OpenGLRenderer::draw_genericDrawHandler(uint32 baseVertex, uint32 baseInsta { beginPerfMonProfiling(performanceMonitor.gpuTime_dcStageShaderAndUniformMgr); LatteSHRC_UpdateActiveShaders(); + LatteDecompilerShader* vs = (LatteDecompilerShader*)LatteSHRC_GetActiveVertexShader(); + LatteDecompilerShader* gs = (LatteDecompilerShader*)LatteSHRC_GetActiveGeometryShader(); + LatteDecompilerShader* ps = (LatteDecompilerShader*)LatteSHRC_GetActivePixelShader(); + if (vs) + shader_bind(vs->shader); + else + shader_unbind(RendererShader::ShaderType::kVertex); + if (ps && LatteGPUState.contextRegister[mmVGT_STRMOUT_EN] == 0) + shader_bind(ps->shader); + else + shader_unbind(RendererShader::ShaderType::kFragment); + if (gs) + shader_bind(gs->shader); + else + shader_unbind(RendererShader::ShaderType::kGeometry); endPerfMonProfiling(performanceMonitor.gpuTime_dcStageShaderAndUniformMgr); } if (LatteGPUState.activeShaderHasError) diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp index 5530b4ec..3d46f206 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp @@ -230,11 +230,6 @@ sint32 RendererShaderGL::GetUniformLocation(const char* name) return glGetUniformLocation(m_program, name); } -void RendererShaderGL::SetUniform1iv(sint32 location, void* data, sint32 count) -{ - glProgramUniform1iv(m_program, location, count, (const GLint*)data); -} - void RendererShaderGL::SetUniform2fv(sint32 location, void* data, sint32 count) { glProgramUniform2fv(m_program, location, count, (const GLfloat*)data); diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h index abc62358..60c51cc1 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h @@ -18,7 +18,6 @@ public: GLuint GetShaderObject() const { cemu_assert_debug(m_isCompiled); return m_shader_object; } sint32 GetUniformLocation(const char* name) override; - void SetUniform1iv(sint32 location, void* data, sint32 count) override; void SetUniform2fv(sint32 location, void* data, sint32 count) override; void SetUniform4iv(sint32 location, void* data, sint32 count) override; diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.h b/src/Cafe/HW/Latte/Renderer/Renderer.h index 61ff10c8..11d102d0 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.h +++ b/src/Cafe/HW/Latte/Renderer/Renderer.h @@ -135,8 +135,6 @@ public: // shader virtual RendererShader* shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool compileAsync, bool isGfxPackSource) = 0; - virtual void shader_bind(RendererShader* shader) = 0; - virtual void shader_unbind(RendererShader::ShaderType shaderType) = 0; // streamout virtual void streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) = 0; diff --git a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp index ae528944..cdbeb3f3 100644 --- a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp +++ b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp @@ -233,12 +233,6 @@ void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_ } } -void RendererOutputShader::Bind() const -{ - g_renderer->shader_bind(m_vertex_shader); - g_renderer->shader_bind(m_fragment_shader); -} - RendererOutputShader* RendererOutputShader::s_copy_shader; RendererOutputShader* RendererOutputShader::s_copy_shader_ud; diff --git a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.h b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.h index 253990e2..398ac663 100644 --- a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.h +++ b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.h @@ -18,7 +18,6 @@ public: virtual ~RendererOutputShader() = default; void SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& input_res, const Vector2i& output_res) const; - void Bind() const; RendererShader* GetVertexShader() const { diff --git a/src/Cafe/HW/Latte/Renderer/RendererShader.h b/src/Cafe/HW/Latte/Renderer/RendererShader.h index 1c15211f..e3f254c6 100644 --- a/src/Cafe/HW/Latte/Renderer/RendererShader.h +++ b/src/Cafe/HW/Latte/Renderer/RendererShader.h @@ -19,8 +19,7 @@ public: virtual bool WaitForCompiled() = 0; virtual sint32 GetUniformLocation(const char* name) = 0; - - virtual void SetUniform1iv(sint32 location, void* data, sint32 count) = 0; + virtual void SetUniform2fv(sint32 location, void* data, sint32 count) = 0; virtual void SetUniform4iv(sint32 location, void* data, sint32 count) = 0; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp index 8460c8b5..970f5517 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp @@ -227,11 +227,6 @@ sint32 RendererShaderVk::GetUniformLocation(const char* name) return 0; } -void RendererShaderVk::SetUniform1iv(sint32 location, void* data, sint32 count) -{ - cemu_assert_suspicious(); -} - void RendererShaderVk::SetUniform2fv(sint32 location, void* data, sint32 count) { cemu_assert_suspicious(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h index 207ea3ea..f9c3ede1 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h @@ -32,7 +32,6 @@ public: static void Shutdown(); sint32 GetUniformLocation(const char* name) override; - void SetUniform1iv(sint32 location, void* data, sint32 count) override; void SetUniform2fv(sint32 location, void* data, sint32 count) override; void SetUniform4iv(sint32 location, void* data, sint32 count) override; VkShaderModule& GetShaderModule() { return m_shader_module; } diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 5b4dd739..2ce5dacd 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1090,17 +1090,6 @@ RendererShader* VulkanRenderer::shader_create(RendererShader::ShaderType type, u return new RendererShaderVk(type, baseHash, auxHash, isGameShader, isGfxPackShader, source); } -void VulkanRenderer::shader_bind(RendererShader* shader) -{ - // does nothing on Vulkan - // remove from main render backend and internalize into GL backend -} - -void VulkanRenderer::shader_unbind(RendererShader::ShaderType shaderType) -{ - // does nothing on Vulkan -} - bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info) { std::vector availableDeviceExtensions; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 3d68f844..84cae587 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -350,8 +350,6 @@ public: void buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size) override; RendererShader* shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool isGameShader, bool isGfxPackShader) override; - void shader_bind(RendererShader* shader) override; - void shader_unbind(RendererShader::ShaderType shaderType) override; void* indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex) override; void indexData_uploadIndexMemory(uint32 offset, uint32 size) override; From df282ab230d07629e28dca16b5decea198879baa Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:19:12 +0100 Subject: [PATCH 081/101] Latte: Clean up OpenGL relics in shared render code --- src/Cafe/HW/Latte/Core/Latte.h | 2 +- src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp | 4 - src/Cafe/HW/Latte/Core/LatteTexture.cpp | 14 +- src/Cafe/HW/Latte/Core/LatteTexture.h | 6 +- src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp | 23 ++- src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp | 2 +- src/Cafe/HW/Latte/Core/LatteThread.cpp | 8 +- .../Latte/Renderer/OpenGL/LatteTextureGL.cpp | 28 +-- .../HW/Latte/Renderer/OpenGL/LatteTextureGL.h | 6 +- .../Renderer/OpenGL/LatteTextureViewGL.cpp | 2 +- .../Latte/Renderer/OpenGL/OpenGLRenderer.cpp | 147 ++++----------- .../HW/Latte/Renderer/OpenGL/OpenGLRenderer.h | 18 +- .../Renderer/OpenGL/OpenGLRendererCore.cpp | 4 +- .../Renderer/OpenGL/OpenGLSurfaceCopy.cpp | 2 +- .../Renderer/OpenGL/TextureReadbackGL.cpp | 4 +- src/Cafe/HW/Latte/Renderer/Renderer.h | 8 +- .../Latte/Renderer/Vulkan/LatteTextureVk.cpp | 4 +- .../HW/Latte/Renderer/Vulkan/LatteTextureVk.h | 2 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 11 +- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 9 +- src/Common/GLInclude/GLInclude.h | 171 ++++++++++++++++++ src/Common/GLInclude/glFunctions.h | 8 + 22 files changed, 267 insertions(+), 216 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index 861d7ddf..dc3cbc91 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -115,7 +115,7 @@ void LatteTC_RegisterTexture(LatteTexture* tex); void LatteTC_UnregisterTexture(LatteTexture* tex); uint32 LatteTexture_CalculateTextureDataHash(LatteTexture* hostTexture); -void LatteTexture_ReloadData(LatteTexture* hostTexture, uint32 textureUnit); +void LatteTexture_ReloadData(LatteTexture* hostTexture); bool LatteTC_HasTextureChanged(LatteTexture* hostTexture, bool force = false); void LatteTC_ResetTextureChangeTracker(LatteTexture* hostTexture, bool force = false); diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index 06015949..abdfda21 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -239,8 +239,6 @@ LatteTextureView* LatteMRT_CreateColorBuffer(MPTR colorBufferPhysMem, uint32 wid textureView = LatteTexture_CreateMapping(colorBufferPhysMem, MPTR_NULL, width, height, viewSlice+1, pitch, tileMode, swizzle, 0, 1, viewSlice, 1, format, Latte::E_DIM::DIM_2D_ARRAY, Latte::E_DIM::DIM_2D, false); else textureView = LatteTexture_CreateMapping(colorBufferPhysMem, MPTR_NULL, width, height, 1, pitch, tileMode, swizzle, 0, 1, viewSlice, 1, format, Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, false); - // unbind texture - g_renderer->texture_bindAndActivate(nullptr, 0); return textureView; } @@ -253,8 +251,6 @@ LatteTextureView* LatteMRT_CreateDepthBuffer(MPTR depthBufferPhysMem, uint32 wid textureView = LatteTexture_CreateMapping(depthBufferPhysMem, MPTR_NULL, width, height, viewSlice+1, pitch, tileMode, swizzle, 0, 1, viewSlice, 1, format, Latte::E_DIM::DIM_2D_ARRAY, Latte::E_DIM::DIM_2D, true); LatteMRT::SetDepthAndStencilAttachment(textureView, textureView->baseTexture->hasStencil); - // unbind texture - g_renderer->texture_bindAndActivate(nullptr, 0); return textureView; } diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index 42a9d8c6..06afed60 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -985,7 +985,7 @@ void LatteTexture_RecreateTextureWithDifferentMipSliceCount(LatteTexture* textur newDim = Latte::E_DIM::DIM_2D_ARRAY; else if (newDim == Latte::E_DIM::DIM_1D && newDepth > 1) newDim = Latte::E_DIM::DIM_1D_ARRAY; - LatteTextureView* view = LatteTexture_CreateTexture(0, newDim, texture->physAddress, physMipAddr, texture->format, texture->width, texture->height, newDepth, texture->pitch, newMipCount, texture->swizzle, texture->tileMode, texture->isDepth); + LatteTextureView* view = LatteTexture_CreateTexture(newDim, texture->physAddress, physMipAddr, texture->format, texture->width, texture->height, newDepth, texture->pitch, newMipCount, texture->swizzle, texture->tileMode, texture->isDepth); cemu_assert(!(view->baseTexture->mipLevels <= 1 && physMipAddr == MPTR_NULL && newMipCount > 1)); // copy data from old texture if its dynamically updated if (texture->isUpdatedOnGPU) @@ -1112,7 +1112,7 @@ LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, si // create new texture if (allowCreateNewDataTexture == false) return nullptr; - LatteTextureView* view = LatteTexture_CreateTexture(0, dimBase, physAddr, physMipAddr, format, width, height, depth, pitch, firstMip + numMip, swizzle, tileMode, isDepth); + LatteTextureView* view = LatteTexture_CreateTexture(dimBase, physAddr, physMipAddr, format, width, height, depth, pitch, firstMip + numMip, swizzle, tileMode, isDepth); LatteTexture* newTexture = view->baseTexture; LatteTexture_GatherTextureRelations(view->baseTexture); LatteTexture_UpdateTextureFromDynamicChanges(view->baseTexture); @@ -1191,12 +1191,8 @@ LatteTextureView* LatteTC_GetTextureSliceViewOrTryCreate(MPTR srcImagePtr, MPTR void LatteTexture_UpdateDataToLatest(LatteTexture* texture) { if (LatteTC_HasTextureChanged(texture)) - { - g_renderer->texture_rememberBoundTexture(0); - g_renderer->texture_bindAndActivateRawTex(texture, 0); - LatteTexture_ReloadData(texture, 0); - g_renderer->texture_restoreBoundTexture(0); - } + LatteTexture_ReloadData(texture); + if (texture->reloadFromDynamicTextures) { LatteTexture_UpdateCacheFromDynamicTextures(texture); @@ -1245,7 +1241,7 @@ std::vector& LatteTexture::GetAllTextures() return sAllTextures; } -LatteTexture::LatteTexture(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, +LatteTexture::LatteTexture(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) { _AddTextureToGlobalList(this); diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.h b/src/Cafe/HW/Latte/Core/LatteTexture.h index 6cdc528e..d5e872e6 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.h +++ b/src/Cafe/HW/Latte/Core/LatteTexture.h @@ -24,11 +24,9 @@ struct LatteSamplerState class LatteTexture { public: - LatteTexture(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth); + LatteTexture(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth); virtual ~LatteTexture(); - virtual void InitTextureState() {}; - LatteTextureView* GetOrCreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) { for (auto& itr : views) @@ -307,7 +305,7 @@ std::vector LatteTexture_QueryCacheInfo(); float* LatteTexture_getEffectiveTextureScale(LatteConst::ShaderType shaderType, sint32 texUnit); -LatteTextureView* LatteTexture_CreateTexture(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth); +LatteTextureView* LatteTexture_CreateTexture(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth); void LatteTexture_Delete(LatteTexture* texture); void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* textureData, uint32 sliceIndex, uint32 mipIndex, uint8* linearPixelData); diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp index 9cce2526..0260002b 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp @@ -32,9 +32,9 @@ void LatteTexture_setEffectiveTextureScale(LatteConst::ShaderType shaderType, si t[1] = v; } -void LatteTextureLoader_UpdateTextureSliceData(LatteTexture* tex, sint32 textureUnit, uint32 sliceIndex, uint32 mipIndex, MPTR physImagePtr, MPTR physMipPtr, Latte::E_DIM dim, uint32 width, uint32 height, uint32 depth, uint32 mipLevels, uint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, bool dumpTex); +void LatteTextureLoader_UpdateTextureSliceData(LatteTexture* tex, uint32 sliceIndex, uint32 mipIndex, MPTR physImagePtr, MPTR physMipPtr, Latte::E_DIM dim, uint32 width, uint32 height, uint32 depth, uint32 mipLevels, uint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, bool dumpTex); -void LatteTexture_ReloadData(LatteTexture* tex, uint32 textureUnit) +void LatteTexture_ReloadData(LatteTexture* tex) { tex->reloadCount++; for(sint32 mip=0; mipmipLevels; mip++) @@ -44,35 +44,35 @@ void LatteTexture_ReloadData(LatteTexture* tex, uint32 textureUnit) { sint32 numSlices = std::max(tex->depth, 1); for(sint32 s=0; sphysAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); + LatteTextureLoader_UpdateTextureSliceData(tex, s, mip, tex->physAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); } else if( tex->dim == Latte::E_DIM::DIM_CUBEMAP ) { cemu_assert_debug((tex->depth % 6) == 0); sint32 numFullCubeMaps = tex->depth/6; // number of cubemaps (if numFullCubeMaps is >1 then this texture is a cubemap array) for(sint32 s=0; sphysAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); + LatteTextureLoader_UpdateTextureSliceData(tex, s, mip, tex->physAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); } else if( tex->dim == Latte::E_DIM::DIM_3D ) { sint32 mipDepth = std::max(tex->depth>>mip, 1); for(sint32 s=0; sphysAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); + LatteTextureLoader_UpdateTextureSliceData(tex, s, mip, tex->physAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); } } else { // load slice 0 - LatteTextureLoader_UpdateTextureSliceData(tex, textureUnit, 0, mip, tex->physAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); + LatteTextureLoader_UpdateTextureSliceData(tex, 0, mip, tex->physAddress, tex->physMipAddress, tex->dim, tex->width, tex->height, tex->depth, tex->mipLevels, tex->pitch, tex->tileMode, tex->swizzle, true); } } tex->lastUpdateEventCounter = LatteTexture_getNextUpdateEventCounter(); } -LatteTextureView* LatteTexture_CreateTexture(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) +LatteTextureView* LatteTexture_CreateTexture(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) { - const auto tex = g_renderer->texture_createTextureEx(textureUnit, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); + const auto tex = g_renderer->texture_createTextureEx(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); // init slice/mip info array LatteTexture_InitSliceAndMipInfo(tex); LatteTexture_RegisterTextureMemoryOccupancy(tex); @@ -110,7 +110,7 @@ LatteTextureView* LatteTexture_CreateTexture(uint32 textureUnit, Latte::E_DIM di } } } - LatteTexture_ReloadData(tex, textureUnit); + LatteTexture_ReloadData(tex); LatteTC_MarkTextureStillInUse(tex); LatteTC_RegisterTexture(tex); // create initial view that maps to the whole texture @@ -247,7 +247,7 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u textureView->lastTextureBindIndex = LatteGPUState.textureBindCounter; rendererGL->renderstate_updateTextureSettingsGL(shaderContext, textureView, textureIndex + glBackendBaseTexUnit, word4, textureIndex, isDepthSampler); } - g_renderer->texture_bindOnly(textureView, textureIndex + glBackendBaseTexUnit); + g_renderer->texture_setLatteTexture(textureView, textureIndex + glBackendBaseTexUnit); // update if data changed bool swizzleChanged = false; if (textureView->baseTexture->swizzle != swizzle) @@ -285,9 +285,8 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u textureView->baseTexture->physMipAddress = physMipAddr; } } - g_renderer->texture_bindAndActivateRawTex(textureView->baseTexture, textureIndex + glBackendBaseTexUnit); debug_printf("Reload reason: Data-change when bound as texture (new hash 0x%08x)\n", textureView->baseTexture->texDataHash2); - LatteTexture_ReloadData(textureView->baseTexture, textureIndex + glBackendBaseTexUnit); + LatteTexture_ReloadData(textureView->baseTexture); } LatteTexture* baseTexture = textureView->baseTexture; if (baseTexture->reloadFromDynamicTextures) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp b/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp index 331c1500..862fff06 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp @@ -599,7 +599,7 @@ void LatteTextureLoader_loadTextureDataIntoSlice(LatteTexture* hostTexture, sint } } -void LatteTextureLoader_UpdateTextureSliceData(LatteTexture* tex, sint32 textureUnit, uint32 sliceIndex, uint32 mipIndex, MPTR physImagePtr, MPTR physMipPtr, Latte::E_DIM dim, uint32 width, uint32 height, uint32 depth, uint32 mipLevels, uint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, bool dumpTex) +void LatteTextureLoader_UpdateTextureSliceData(LatteTexture* tex, uint32 sliceIndex, uint32 mipIndex, MPTR physImagePtr, MPTR physMipPtr, Latte::E_DIM dim, uint32 width, uint32 height, uint32 depth, uint32 mipLevels, uint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, bool dumpTex) { LatteTextureLoaderCtx textureLoader = { 0 }; diff --git a/src/Cafe/HW/Latte/Core/LatteThread.cpp b/src/Cafe/HW/Latte/Core/LatteThread.cpp index 897f769c..60b32ec4 100644 --- a/src/Cafe/HW/Latte/Core/LatteThread.cpp +++ b/src/Cafe/HW/Latte/Core/LatteThread.cpp @@ -44,7 +44,7 @@ LatteTextureView* LatteHandleOSScreen_getOrCreateScreenTex(MPTR physAddress, uin LatteTextureView* texView = LatteTextureViewLookupCache::lookup(physAddress, width, height, 1, pitch, 0, 1, 0, 1, Latte::E_GX2SURFFMT::R8_G8_B8_A8_UNORM, Latte::E_DIM::DIM_2D); if (texView) return texView; - return LatteTexture_CreateTexture(0, Latte::E_DIM::DIM_2D, physAddress, 0, Latte::E_GX2SURFFMT::R8_G8_B8_A8_UNORM, width, height, 1, pitch, 1, 0, Latte::E_HWTILEMODE::TM_LINEAR_ALIGNED, false); + return LatteTexture_CreateTexture(Latte::E_DIM::DIM_2D, physAddress, 0, Latte::E_GX2SURFFMT::R8_G8_B8_A8_UNORM, width, height, 1, pitch, 1, 0, Latte::E_HWTILEMODE::TM_LINEAR_ALIGNED, false); } void LatteHandleOSScreen_prepareTextures() @@ -71,8 +71,7 @@ bool LatteHandleOSScreen_TV() const uint32 bufferIndexTV = (bufferDisplayTV); const uint32 bufferIndexDRC = bufferDisplayDRC; - g_renderer->texture_bindAndActivate(osScreenTVTex[bufferIndexTV], 0); - LatteTexture_ReloadData(osScreenTVTex[bufferIndexTV]->baseTexture, 0); + LatteTexture_ReloadData(osScreenTVTex[bufferIndexTV]->baseTexture); // TV screen LatteRenderTarget_copyToBackbuffer(osScreenTVTex[bufferIndexTV]->baseTexture->baseView, false); @@ -94,8 +93,7 @@ bool LatteHandleOSScreen_DRC() const uint32 bufferIndexDRC = bufferDisplayDRC; - g_renderer->texture_bindAndActivate(osScreenDRCTex[bufferIndexDRC], 0); - LatteTexture_ReloadData(osScreenDRCTex[bufferIndexDRC]->baseTexture, 0); + LatteTexture_ReloadData(osScreenDRCTex[bufferIndexDRC]->baseTexture); // GamePad screen LatteRenderTarget_copyToBackbuffer(osScreenDRCTex[bufferIndexDRC]->baseTexture->baseView, true); diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp index c9541470..584af40c 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp @@ -19,20 +19,17 @@ static GLuint _genTextureHandleGL() return texIdPool[texIdPoolIndex - 1]; } -LatteTextureGL::LatteTextureGL(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, +LatteTextureGL::LatteTextureGL(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) - : LatteTexture(textureUnit, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth) + : LatteTexture(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth) { - GenerateEmptyTextureFromGX2Dim(dim, this->glId_texture, this->glTexTarget); + GenerateEmptyTextureFromGX2Dim(dim, this->glId_texture, this->glTexTarget, true); // set format info FormatInfoGL glFormatInfo; GetOpenGLFormatInfo(isDepth, format, dim, &glFormatInfo); this->glInternalFormat = glFormatInfo.glInternalFormat; this->isAlternativeFormat = glFormatInfo.isUsingAlternativeFormat; this->hasStencil = glFormatInfo.hasStencil; // todo - should get this from the GX2 format? - // bind texture - g_renderer->texture_bindAndActivateRawTex(this, textureUnit); - LatteTextureGL::InitTextureState(); // set debug name bool useGLDebugNames = false; #ifdef CEMU_DEBUG_ASSERT @@ -54,9 +51,8 @@ LatteTextureGL::~LatteTextureGL() catchOpenGLError(); } -void LatteTextureGL::GenerateEmptyTextureFromGX2Dim(Latte::E_DIM dim, GLuint& texId, GLint& texTarget) +void LatteTextureGL::GenerateEmptyTextureFromGX2Dim(Latte::E_DIM dim, GLuint& texId, GLint& texTarget, bool createForTargetType) { - texId = _genTextureHandleGL(); if (dim == Latte::E_DIM::DIM_2D) texTarget = GL_TEXTURE_2D; else if (dim == Latte::E_DIM::DIM_1D) @@ -73,6 +69,10 @@ void LatteTextureGL::GenerateEmptyTextureFromGX2Dim(Latte::E_DIM dim, GLuint& te { cemu_assert_unimplemented(); } + if(createForTargetType) + texId = glCreateTextureWrapper(texTarget); // initializes the texture to texTarget (equivalent to calling glGenTextures + glBindTexture) + else + glGenTextures(1, &texId); } LatteTextureView* LatteTextureGL::CreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) @@ -80,18 +80,6 @@ LatteTextureView* LatteTextureGL::CreateView(Latte::E_DIM dim, Latte::E_GX2SURFF return new LatteTextureViewGL(this, dim, format, firstMip, mipCount, firstSlice, sliceCount); } -void LatteTextureGL::InitTextureState() -{ - // init texture with some default parameters (todo - this shouldn't be necessary if we properly set parameters when a texture is used) - catchOpenGLError(); - glTexParameteri(glTexTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(glTexTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(glTexTarget, GL_TEXTURE_COMPARE_MODE, GL_NONE); - catchOpenGLError(); -} - void LatteTextureGL::GetOpenGLFormatInfo(bool isDepth, Latte::E_GX2SURFFMT format, Latte::E_DIM dim, FormatInfoGL* formatInfoOut) { formatInfoOut->isUsingAlternativeFormat = false; diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h index fabd1bac..9169bb29 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h @@ -6,14 +6,12 @@ class LatteTextureGL : public LatteTexture { public: - LatteTextureGL(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, + LatteTextureGL(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth); ~LatteTextureGL(); - static void GenerateEmptyTextureFromGX2Dim(Latte::E_DIM dim, GLuint& texId, GLint& texTarget); - - void InitTextureState() override; + static void GenerateEmptyTextureFromGX2Dim(Latte::E_DIM dim, GLuint& texId, GLint& texTarget, bool createForTargetType); protected: LatteTextureView* CreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) override; diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp index f33fd7ff..29085642 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp @@ -11,7 +11,7 @@ LatteTextureViewGL::LatteTextureViewGL(LatteTextureGL* texture, Latte::E_DIM dim firstSlice != 0 || firstMip != 0 || mipCount != texture->mipLevels || sliceCount != texture->depth || forceCreateNewTexId) { - LatteTextureGL::GenerateEmptyTextureFromGX2Dim(dim, glTexId, glTexTarget); + LatteTextureGL::GenerateEmptyTextureFromGX2Dim(dim, glTexId, glTexTarget, false); this->glInternalFormat = 0; InitAliasView(); } diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index 01068a3d..f09f04f1 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -77,8 +77,6 @@ static const GLenum glAlphaTestFunc[] = GL_ALWAYS }; - - OpenGLRenderer::OpenGLRenderer() { glRendererState.useTextureUploadBuffer = false; @@ -571,7 +569,7 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu glViewportIndexedf(0, imageX, imageY, imageWidth, imageHeight); LatteTextureViewGL* texViewGL = (LatteTextureViewGL*)texView; - g_renderer->texture_bindAndActivate(texView, 0); + texture_bindAndActivate(texView, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, useLinearTexFilter ? GL_LINEAR : GL_NEAREST); texViewGL->samplerState.filterMag = 0xFFFFFFFF; @@ -586,7 +584,7 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu glEnable(GL_FRAMEBUFFER_SRGB); // unbind texture - g_renderer->texture_bindAndActivate(nullptr, 0); + texture_bindAndActivate(nullptr, 0); catchOpenGLError(); @@ -990,8 +988,9 @@ void OpenGLRenderer::texture_destroy(LatteTexture* hostTexture) delete hostTexture; } -void OpenGLRenderer::texture_reserveTextureOnGPU(LatteTexture* hostTexture) +void OpenGLRenderer::texture_reserveTextureOnGPU(LatteTexture* hostTextureGeneric) { + auto hostTexture = (LatteTextureGL*)hostTextureGeneric; cemu_assert_debug(hostTexture->isDataDefined == false); sint32 effectiveBaseWidth = hostTexture->width; sint32 effectiveBaseHeight = hostTexture->height; @@ -1012,25 +1011,25 @@ void OpenGLRenderer::texture_reserveTextureOnGPU(LatteTexture* hostTexture) if (hostTexture->dim == Latte::E_DIM::DIM_2D || hostTexture->dim == Latte::E_DIM::DIM_2D_MSAA) { cemu_assert_debug(effectiveBaseDepth == 1); - glTexStorage2D(GL_TEXTURE_2D, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight); + glTextureStorage2DWrapper(GL_TEXTURE_2D, hostTexture->glId_texture, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight); } else if (hostTexture->dim == Latte::E_DIM::DIM_1D) { cemu_assert_debug(effectiveBaseHeight == 1); cemu_assert_debug(effectiveBaseDepth == 1); - glTexStorage1D(GL_TEXTURE_1D, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth); + glTextureStorage1DWrapper(GL_TEXTURE_1D, hostTexture->glId_texture, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth); } else if (hostTexture->dim == Latte::E_DIM::DIM_2D_ARRAY || hostTexture->dim == Latte::E_DIM::DIM_2D_ARRAY_MSAA) { - glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight, std::max(1, effectiveBaseDepth)); + glTextureStorage3DWrapper(GL_TEXTURE_2D_ARRAY, hostTexture->glId_texture, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight, std::max(1, effectiveBaseDepth)); } else if (hostTexture->dim == Latte::E_DIM::DIM_3D) { - glTexStorage3D(GL_TEXTURE_3D, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight, std::max(1, effectiveBaseDepth)); + glTextureStorage3DWrapper(GL_TEXTURE_3D, hostTexture->glId_texture, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight, std::max(1, effectiveBaseDepth)); } else if (hostTexture->dim == Latte::E_DIM::DIM_CUBEMAP) { - glTexStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight, effectiveBaseDepth); + glTextureStorage3DWrapper(GL_TEXTURE_CUBE_MAP_ARRAY, hostTexture->glId_texture, mipLevels, glFormatInfo.glInternalFormat, effectiveBaseWidth, effectiveBaseHeight, effectiveBaseDepth); } else { @@ -1042,7 +1041,6 @@ void OpenGLRenderer::texture_reserveTextureOnGPU(LatteTexture* hostTexture) void OpenGLRenderer_texture_loadSlice_normal(LatteTexture* hostTextureGeneric, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 imageSize) { auto hostTexture = (LatteTextureGL*)hostTextureGeneric; - sint32 effectiveWidth = width; sint32 effectiveHeight = height; sint32 effectiveDepth = depth; @@ -1053,58 +1051,37 @@ void OpenGLRenderer_texture_loadSlice_normal(LatteTexture* hostTextureGeneric, s LatteTextureGL::GetOpenGLFormatInfo(hostTexture->isDepth, hostTexture->overwriteInfo.hasFormatOverwrite ? (Latte::E_GX2SURFFMT)hostTexture->overwriteInfo.format : hostTexture->format, hostTexture->dim, &glFormatInfo); // upload slice catchOpenGLError(); + if (mipIndex >= hostTexture->maxPossibleMipLevels) + { + cemuLog_logDebug(LogType::Force, "2D texture mip level allocated out of range"); + return; + } if (hostTexture->dim == Latte::E_DIM::DIM_2D || hostTexture->dim == Latte::E_DIM::DIM_2D_MSAA) { if (glFormatInfo.glIsCompressed) - { - if (glCompressedTextureSubImage2D) - glCompressedTextureSubImage2D(hostTexture->glId_texture, mipIndex, 0, 0, effectiveWidth, effectiveHeight, glFormatInfo.glInternalFormat, imageSize, pixelData); - else - glCompressedTexSubImage2D(GL_TEXTURE_2D, mipIndex, 0, 0, effectiveWidth, effectiveHeight, glFormatInfo.glInternalFormat, imageSize, pixelData); - } + glCompressedTextureSubImage2DWrapper(hostTexture->glTexTarget, hostTexture->glId_texture, mipIndex, 0, 0, effectiveWidth, effectiveHeight, glFormatInfo.glInternalFormat, imageSize, pixelData); else - { - if (mipIndex < hostTexture->maxPossibleMipLevels) - glTexSubImage2D(GL_TEXTURE_2D, mipIndex, 0, 0, effectiveWidth, effectiveHeight, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); - else - cemuLog_logDebug(LogType::Force, "2D texture mip level allocated out of range"); - } + glTextureSubImage2DWrapper(hostTexture->glTexTarget, hostTexture->glId_texture, mipIndex, 0, 0, effectiveWidth, effectiveHeight, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); } else if (hostTexture->dim == Latte::E_DIM::DIM_1D) { - if (glFormatInfo.glIsCompressed == true) - cemu_assert_unimplemented(); - glTexSubImage1D(GL_TEXTURE_1D, mipIndex, 0, width, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); + if (glFormatInfo.glIsCompressed) + glCompressedTextureSubImage1DWrapper(hostTexture->glTexTarget, hostTexture->glId_texture, mipIndex, 0, width, glFormatInfo.glInternalFormat, imageSize, pixelData); + else + glTextureSubImage1DWrapper(hostTexture->glTexTarget, hostTexture->glId_texture, mipIndex, 0, width, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); } - else if (hostTexture->dim == Latte::E_DIM::DIM_2D_ARRAY || hostTexture->dim == Latte::E_DIM::DIM_2D_ARRAY_MSAA) + else if (hostTexture->dim == Latte::E_DIM::DIM_2D_ARRAY || hostTexture->dim == Latte::E_DIM::DIM_2D_ARRAY_MSAA || + hostTexture->dim == Latte::E_DIM::DIM_3D || + hostTexture->dim == Latte::E_DIM::DIM_CUBEMAP) { if (glFormatInfo.glIsCompressed) - glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, glFormatInfo.glInternalFormat, imageSize, pixelData); + glCompressedTextureSubImage3DWrapper(hostTexture->glTexTarget, hostTexture->glId_texture, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, glFormatInfo.glInternalFormat, imageSize, pixelData); else - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); - } - else if (hostTexture->dim == Latte::E_DIM::DIM_3D) - { - if (glFormatInfo.glIsCompressed) - glCompressedTexSubImage3D(GL_TEXTURE_3D, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, glFormatInfo.glInternalFormat, imageSize, pixelData); - else - glTexSubImage3D(GL_TEXTURE_3D, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); - } - else if (hostTexture->dim == Latte::E_DIM::DIM_CUBEMAP) - { - if (glFormatInfo.glIsCompressed) - glCompressedTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mipIndex, 0, 0, sliceIndex, width, height, 1, glFormatInfo.glInternalFormat, imageSize, pixelData); - else - glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mipIndex, 0, 0, sliceIndex, width, height, 1, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); - } - else - { - cemu_assert_debug(false); + glTextureSubImage3DWrapper(hostTexture->glTexTarget, hostTexture->glId_texture, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, glFormatInfo.glSuppliedFormat, glFormatInfo.glSuppliedFormatType, pixelData); } catchOpenGLError(); } - // use persistent buffers to upload data void OpenGLRenderer_texture_loadSlice_viaBuffers(LatteTexture* hostTexture, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 imageSize) { @@ -1220,10 +1197,10 @@ void OpenGLRenderer::texture_clearSlice(LatteTexture* hostTextureGeneric, sint32 glClearTexSubImage(hostTexture->glId_texture, mipIndex, 0, 0, sliceIndex, effectiveWidth, effectiveHeight, 1, formatInfoGL.glSuppliedFormat, formatInfoGL.glSuppliedFormatType, NULL); } -LatteTexture* OpenGLRenderer::texture_createTextureEx(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, +LatteTexture* OpenGLRenderer::texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) { - return new LatteTextureGL(textureUnit, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); + return new LatteTextureGL(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); } @@ -1239,42 +1216,18 @@ void OpenGLRenderer::texture_setActiveTextureUnit(sint32 index) void OpenGLRenderer::texture_bindAndActivate(LatteTextureView* textureView, uint32 textureUnit) { const auto textureViewGL = (LatteTextureViewGL*)textureView; - cemu_assert_debug(textureUnit < (sizeof(LatteBoundTexturesBackup) / sizeof(LatteBoundTexturesBackup[0]))); // don't call glBindTexture if the texture is already bound - if (LatteBoundTextures[textureUnit] == textureViewGL) + if (m_latteBoundTextures[textureUnit] == textureViewGL) { texture_setActiveTextureUnit(textureUnit); return; // already bound } // bind - LatteBoundTextures[textureUnit] = textureViewGL; + m_latteBoundTextures[textureUnit] = textureViewGL; texture_setActiveTextureUnit(textureUnit); if (textureViewGL) { glBindTexture(textureViewGL->glTexTarget, textureViewGL->glTexId); - texUnitTexId[textureUnit] = textureViewGL->glTexId; - texUnitTexTarget[textureUnit] = textureViewGL->glTexTarget; - } -} - -void OpenGLRenderer::texture_bindAndActivateRawTex(LatteTexture* texture, uint32 textureUnit) -{ - cemu_assert_debug(textureUnit < (sizeof(LatteBoundTexturesBackup) / sizeof(LatteBoundTexturesBackup[0]))); - // don't call glBindTexture if the texture is already bound - if (LatteBoundTextures[textureUnit] == texture) - { - texture_setActiveTextureUnit(textureUnit); - return; // already bound - } - // bind - LatteBoundTextures[textureUnit] = texture; - texture_setActiveTextureUnit(textureUnit); - if (texture) - { - auto textureGL = (LatteTextureGL*)texture; - glBindTexture(textureGL->glTexTarget, textureGL->glId_texture); - texUnitTexId[textureUnit] = textureGL->glId_texture; - texUnitTexTarget[textureUnit] = textureGL->glTexTarget; } } @@ -1282,18 +1235,18 @@ void OpenGLRenderer::texture_notifyDelete(LatteTextureView* textureView) { for (uint32 i = 0; i < Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3; i++) { - if (LatteBoundTextures[i] == textureView) - LatteBoundTextures[i] = nullptr; + if (m_latteBoundTextures[i] == textureView) + m_latteBoundTextures[i] = nullptr; } } -// similar to _bindAndActivate() but doesn't call _setActiveTextureUnit() if texture is already bound -void OpenGLRenderer::texture_bindOnly(LatteTextureView* textureView1, uint32 textureUnit) +// set Latte texture, on the OpenGL renderer this behaves like _bindAndActivate() but doesn't call _setActiveTextureUnit() if the texture is already bound +void OpenGLRenderer::texture_setLatteTexture(LatteTextureView* textureView1, uint32 textureUnit) { auto textureView = ((LatteTextureViewGL*)textureView1); cemu_assert_debug(textureUnit < Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3); - if (LatteBoundTextures[textureUnit] == textureView) + if (m_latteBoundTextures[textureUnit] == textureView) return; if (textureView == nullptr) return; @@ -1301,45 +1254,17 @@ void OpenGLRenderer::texture_bindOnly(LatteTextureView* textureView1, uint32 tex if (glBindTextureUnit) { glBindTextureUnit(textureUnit, textureView->glTexId); - LatteBoundTextures[textureUnit] = textureView; - texUnitTexId[textureUnit] = textureView->glTexId; - texUnitTexTarget[textureUnit] = textureView->glTexTarget; + m_latteBoundTextures[textureUnit] = textureView; activeTextureUnit = -1; } else { texture_setActiveTextureUnit(textureUnit); glBindTexture(textureView->glTexTarget, textureView->glTexId); - LatteBoundTextures[textureUnit] = textureView; - texUnitTexId[textureUnit] = textureView->glTexId; - texUnitTexTarget[textureUnit] = textureView->glTexTarget; + m_latteBoundTextures[textureUnit] = textureView; } } -void OpenGLRenderer::texture_rememberBoundTexture(uint32 textureUnit) -{ - cemu_assert_debug(texUnitBackupSlotUsed[textureUnit] == false); - texUnitBackupSlotUsed[textureUnit] = true; - LatteBoundTexturesBackup[textureUnit] = LatteBoundTextures[textureUnit]; - texUnitTexIdBackup[textureUnit] = texUnitTexId[textureUnit]; - texUnitTexTargetBackup[textureUnit] = texUnitTexTarget[textureUnit]; -} - -void OpenGLRenderer::texture_restoreBoundTexture(uint32 textureUnit) -{ - cemu_assert_debug(texUnitBackupSlotUsed[textureUnit] == true); - texUnitBackupSlotUsed[textureUnit] = false; - if (LatteBoundTextures[textureUnit] == LatteBoundTexturesBackup[textureUnit]) - { - return; // already bound - } - LatteBoundTextures[textureUnit] = LatteBoundTexturesBackup[textureUnit]; - texUnitTexId[textureUnit] = texUnitTexIdBackup[textureUnit]; - texUnitTexTarget[textureUnit] = texUnitTexTargetBackup[textureUnit]; - texture_setActiveTextureUnit(textureUnit); - glBindTexture(texUnitTexTargetBackup[textureUnit], texUnitTexIdBackup[textureUnit]); -} - void OpenGLRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 effectiveSrcX, sint32 effectiveSrcY, sint32 srcSlice, LatteTexture* dst, sint32 dstMip, sint32 effectiveDstX, sint32 effectiveDstY, sint32 dstSlice, sint32 effectiveCopyWidth, sint32 effectiveCopyHeight, sint32 srcDepth) { diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h index b789e2a7..8a4b1a1d 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h @@ -79,13 +79,10 @@ public: void texture_clearColorSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a) override; void texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sliceIndex, sint32 mipIndex, bool clearDepth, bool clearStencil, float depthValue, uint32 stencilValue) override; - LatteTexture* texture_createTextureEx(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) override; + LatteTexture* texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) override; - void texture_bindAndActivate(LatteTextureView* textureView, uint32 textureUnit) override; - void texture_bindAndActivateRawTex(LatteTexture* texture, uint32 textureUnit) override; - void texture_bindOnly(LatteTextureView* textureView, uint32 textureUnit) override; - void texture_rememberBoundTexture(uint32 textureUnit) override; - void texture_restoreBoundTexture(uint32 textureUnit) override; + void texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) override; + void texture_bindAndActivate(LatteTextureView* textureView, uint32 textureUnit); void texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 effectiveSrcX, sint32 effectiveSrcY, sint32 srcSlice, LatteTexture* dst, sint32 dstMip, sint32 effectiveDstX, sint32 effectiveDstY, sint32 dstSlice, sint32 effectiveCopyWidth, sint32 effectiveCopyHeight, sint32 srcDepth) override; void texture_notifyDelete(LatteTextureView* textureView); @@ -188,14 +185,7 @@ private: bool m_isXfbActive = false; sint32 activeTextureUnit = 0; - void* LatteBoundTextures[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; - GLuint texUnitTexId[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; - GLenum texUnitTexTarget[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; - - void* LatteBoundTexturesBackup[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; - GLuint texUnitTexIdBackup[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; - GLenum texUnitTexTargetBackup[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; - bool texUnitBackupSlotUsed[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; + void* m_latteBoundTextures[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{}; // attribute stream GLuint glAttributeCacheAB{}; diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp index f78b8bd6..51d0d206 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp @@ -1342,7 +1342,7 @@ uint32 _correctTextureCompSelGL(Latte::E_GX2SURFFMT format, uint32 compSel) return compSel; } -#define quickBindTexture() if( textureIsActive == false ) { g_renderer->texture_bindAndActivate(hostTextureView, hostTextureUnit); textureIsActive = true; } +#define quickBindTexture() if( textureIsActive == false ) { texture_bindAndActivate(hostTextureView, hostTextureUnit); textureIsActive = true; } uint32 _getGLMinFilter(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER filterMin, Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_Z_FILTER filterMip) { @@ -1365,11 +1365,9 @@ uint32 _getGLMinFilter(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER filterMi /* * Update channel swizzling and other texture settings for a texture unit * hostTextureView is the texture unit view used on the host side -* The baseGX2TexUnit parameter is used to identify the shader stage in which this texture is accessed */ void OpenGLRenderer::renderstate_updateTextureSettingsGL(LatteDecompilerShader* shaderContext, LatteTextureView* _hostTextureView, uint32 hostTextureUnit, const Latte::LATTE_SQ_TEX_RESOURCE_WORD4_N texUnitWord4, uint32 texUnitIndex, bool isDepthSampler) { - // todo - this is OpenGL-specific, decouple this from the renderer-neutral backend auto hostTextureView = (LatteTextureViewGL*)_hostTextureView; LatteTexture* baseTexture = hostTextureView->baseTexture; diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp index 8c8c36d7..c49a57e4 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp @@ -52,7 +52,7 @@ void OpenGLRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* s LatteTextureView* sourceView = sourceTexture->GetOrCreateView(srcMip, 1, srcSlice, 1); LatteTextureView* destinationView = destinationTexture->GetOrCreateView(dstMip, 1, dstSlice, 1); - g_renderer->texture_bindAndActivate(sourceView, 0); + texture_bindAndActivate(sourceView, 0); catchOpenGLError(); // setup texture attributes _setDepthCompareMode((LatteTextureViewGL*)sourceView, 0); diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp index 56011dab..b2966706 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp @@ -1,4 +1,5 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" +#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLTextureReadback.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" @@ -93,8 +94,7 @@ LatteTextureReadbackInfoGL::~LatteTextureReadbackInfoGL() void LatteTextureReadbackInfoGL::StartTransfer() { cemu_assert(m_textureView); - - g_renderer->texture_bindAndActivate(m_textureView, 0); + ((OpenGLRenderer*)g_renderer.get())->texture_bindAndActivate(m_textureView, 0); // create unsynchronized buffer glGenBuffers(1, &texImageBufferGL); glBindBuffer(GL_PIXEL_PACK_BUFFER, texImageBufferGL); diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.h b/src/Cafe/HW/Latte/Renderer/Renderer.h index 11d102d0..93edaf8d 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.h +++ b/src/Cafe/HW/Latte/Renderer/Renderer.h @@ -110,13 +110,9 @@ public: virtual void texture_clearColorSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a) = 0; virtual void texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sliceIndex, sint32 mipIndex, bool clearDepth, bool clearStencil, float depthValue, uint32 stencilValue) = 0; - virtual LatteTexture* texture_createTextureEx(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) = 0; + virtual LatteTexture* texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) = 0; - virtual void texture_bindAndActivate(LatteTextureView* textureView, uint32 textureUnit) = 0; - virtual void texture_bindAndActivateRawTex(LatteTexture* texture, uint32 textureUnit) = 0; - virtual void texture_bindOnly(LatteTextureView* textureView, uint32 textureUnit) = 0; - virtual void texture_rememberBoundTexture(uint32 textureUnit) = 0; - virtual void texture_restoreBoundTexture(uint32 textureUnit) = 0; + virtual void texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) = 0; virtual void texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 effectiveSrcX, sint32 effectiveSrcY, sint32 srcSlice, LatteTexture* dst, sint32 dstMip, sint32 effectiveDstX, sint32 effectiveDstY, sint32 dstSlice, sint32 effectiveCopyWidth, sint32 effectiveCopyHeight, sint32 srcDepth) = 0; virtual LatteTextureReadbackInfo* texture_createReadback(LatteTextureView* textureView) = 0; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp index b41760bc..b5f62707 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp @@ -3,9 +3,9 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -LatteTextureVk::LatteTextureVk(class VulkanRenderer* vkRenderer, uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, +LatteTextureVk::LatteTextureVk(class VulkanRenderer* vkRenderer, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) - : LatteTexture(textureUnit, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth), m_vkr(vkRenderer) + : LatteTexture(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth), m_vkr(vkRenderer) { vkObjTex = new VKRObjectTexture(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h index 2131ed9c..714c4e17 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h @@ -9,7 +9,7 @@ class LatteTextureVk : public LatteTexture { public: - LatteTextureVk(class VulkanRenderer* vkRenderer, uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, + LatteTextureVk(class VulkanRenderer* vkRenderer, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth); ~LatteTextureVk(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 2ce5dacd..44214606 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -3326,18 +3326,13 @@ void VulkanRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, barrier_image(vkTexture, barrierSubresourceRange, VK_IMAGE_LAYOUT_GENERAL); } -LatteTexture* VulkanRenderer::texture_createTextureEx(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, +LatteTexture* VulkanRenderer::texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) { - return new LatteTextureVk(this, textureUnit, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); + return new LatteTextureVk(this, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); } -void VulkanRenderer::texture_bindAndActivate(LatteTextureView* textureView, uint32 textureUnit) -{ - m_state.boundTexture[textureUnit] = static_cast(textureView); -} - -void VulkanRenderer::texture_bindOnly(LatteTextureView* textureView, uint32 textureUnit) +void VulkanRenderer::texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) { m_state.boundTexture[textureUnit] = static_cast(textureView); } diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 84cae587..b61a0b40 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -300,15 +300,10 @@ public: void texture_loadSlice(LatteTexture* hostTexture, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 compressedImageSize) override; - LatteTexture* texture_createTextureEx(uint32 textureUnit, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) override; + LatteTexture* texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) override; - void texture_bindAndActivate(LatteTextureView* textureView, uint32 textureUnit) override; - void texture_bindOnly(LatteTextureView* textureView, uint32 textureUnit) override; + void texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) override; - void texture_bindAndActivateRawTex(LatteTexture* texture, uint32 textureUnit) override {}; - - void texture_rememberBoundTexture(uint32 textureUnit) override {}; - void texture_restoreBoundTexture(uint32 textureUnit) override {}; void texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 effectiveSrcX, sint32 effectiveSrcY, sint32 srcSlice, LatteTexture* dst, sint32 dstMip, sint32 effectiveDstX, sint32 effectiveDstY, sint32 dstSlice, sint32 effectiveCopyWidth, sint32 effectiveCopyHeight, sint32 srcDepth) override; LatteTextureReadbackInfo* texture_createReadback(LatteTextureView* textureView) override; diff --git a/src/Common/GLInclude/GLInclude.h b/src/Common/GLInclude/GLInclude.h index 6f5d33db..bf7a6bf8 100644 --- a/src/Common/GLInclude/GLInclude.h +++ b/src/Common/GLInclude/GLInclude.h @@ -42,6 +42,177 @@ typedef struct __GLXFBConfigRec *GLXFBConfig; #undef GLFUNC #undef EGLFUNC +// DSA-style helpers with fallback to legacy API if DSA is not supported + +#define DSA_FORCE_DISABLE false // set to true to simulate DSA not being supported + +static GLenum GetGLBindingFromTextureTarget(GLenum texTarget) +{ + switch(texTarget) + { + case GL_TEXTURE_1D: return GL_TEXTURE_BINDING_1D; + case GL_TEXTURE_2D: return GL_TEXTURE_BINDING_2D; + case GL_TEXTURE_3D: return GL_TEXTURE_BINDING_3D; + case GL_TEXTURE_2D_ARRAY: return GL_TEXTURE_BINDING_2D_ARRAY; + case GL_TEXTURE_CUBE_MAP: return GL_TEXTURE_BINDING_CUBE_MAP; + case GL_TEXTURE_CUBE_MAP_ARRAY: return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY; + default: + cemu_assert_unimplemented(); + return 0; + } +} + +static GLuint glCreateTextureWrapper(GLenum target) +{ + GLuint tex; + if (glCreateTextures && !DSA_FORCE_DISABLE) + { + glCreateTextures(target, 1, &tex); + return tex; + } + GLint originalTexture; + glGetIntegerv(GetGLBindingFromTextureTarget(target), &originalTexture); + glGenTextures(1, &tex); + glBindTexture(target, tex); + glBindTexture(target, originalTexture); + return tex; +} + +static void glTextureStorage1DWrapper(GLenum target, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) +{ + if (glTextureStorage1D && !DSA_FORCE_DISABLE) + { + glTextureStorage1D(texture, levels, internalformat, width); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glTexStorage1D(target, levels, internalformat, width); + glBindTexture(target, originalTexture); +} + +static void glTextureStorage2DWrapper(GLenum target, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +{ + if (glTextureStorage2D && !DSA_FORCE_DISABLE) + { + glTextureStorage2D(texture, levels, internalformat, width, height); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glTexStorage2D(target, levels, internalformat, width, height); + glBindTexture(target, originalTexture); +} + +static void glTextureStorage3DWrapper(GLenum target, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +{ + if (glTextureStorage3D && !DSA_FORCE_DISABLE) + { + glTextureStorage3D(texture, levels, internalformat, width, height, depth); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glTexStorage3D(target, levels, internalformat, width, height, depth); + glBindTexture(target, originalTexture); +} + +static void glTextureSubImage1DWrapper(GLenum target, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) +{ + if (glTextureSubImage1D && !DSA_FORCE_DISABLE) + { + glTextureSubImage1D(texture, level, xoffset, width, format, type, pixels); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glTexSubImage1D(target, level, xoffset, width, format, type, pixels); + glBindTexture(target, originalTexture); +} + +static void glCompressedTextureSubImage1DWrapper(GLenum target, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) +{ + if (glCompressedTextureSubImage1D && !DSA_FORCE_DISABLE) + { + glCompressedTextureSubImage1D(texture, level, xoffset, width, format, imageSize, data); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glCompressedTexSubImage1D(target, level, xoffset, width, format, imageSize, data); + glBindTexture(target, originalTexture); +} + +static void glTextureSubImage2DWrapper(GLenum target, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) +{ + if (glTextureSubImage2D && !DSA_FORCE_DISABLE) + { + glTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + glBindTexture(target, originalTexture); +} + +static void glCompressedTextureSubImage2DWrapper(GLenum target, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) +{ + if (glCompressedTextureSubImage2D && !DSA_FORCE_DISABLE) + { + glCompressedTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, imageSize, data); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); + glBindTexture(target, originalTexture); +} + +static void glTextureSubImage3DWrapper(GLenum target, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) +{ + if(glTextureSubImage3D && !DSA_FORCE_DISABLE) + { + glTextureSubImage3D(texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); + glBindTexture(target, originalTexture); +} + +static void glCompressedTextureSubImage3DWrapper(GLenum target, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) +{ + if(glCompressedTextureSubImage3D && !DSA_FORCE_DISABLE) + { + glCompressedTextureSubImage3D(texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); + return; + } + GLenum binding = GetGLBindingFromTextureTarget(target); + GLint originalTexture; + glGetIntegerv(binding, &originalTexture); + glBindTexture(target, texture); + glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); + glBindTexture(target, originalTexture); +} + // this prevents Windows GL.h from being included: #define __gl_h_ #define __GL_H__ diff --git a/src/Common/GLInclude/glFunctions.h b/src/Common/GLInclude/glFunctions.h index 4cf25a6b..76308fdb 100644 --- a/src/Common/GLInclude/glFunctions.h +++ b/src/Common/GLInclude/glFunctions.h @@ -171,10 +171,13 @@ GLFUNC(PFNGLTEXSTORAGE2DPROC, glTexStorage2D) GLFUNC(PFNGLTEXSTORAGE3DPROC, glTexStorage3D) GLFUNC(PFNGLTEXIMAGE3DPROC, glTexImage3D) GLFUNC(PFNGLTEXSUBIMAGE3DPROC, glTexSubImage3D) +GLFUNC(PFNGLCOMPRESSEDTEXIMAGE1DPROC, glCompressedTexImage1D) GLFUNC(PFNGLCOMPRESSEDTEXIMAGE2DPROC, glCompressedTexImage2D) GLFUNC(PFNGLCOMPRESSEDTEXIMAGE3DPROC, glCompressedTexImage3D) +GLFUNC(PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC, glCompressedTexSubImage1D) GLFUNC(PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC, glCompressedTexSubImage2D) GLFUNC(PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC, glCompressedTexSubImage3D) +GLFUNC(PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC, glCompressedTextureSubImage1D) GLFUNC(PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC, glCompressedTextureSubImage2D) GLFUNC(PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC, glCompressedTextureSubImage3D) GLFUNC(PFNGLCOPYIMAGESUBDATAPROC, glCopyImageSubData) @@ -184,12 +187,17 @@ GLFUNC(PFNGLINVALIDATETEXIMAGEPROC, glInvalidateTexImage) // texture DSA +GLFUNC(PFNGLCREATETEXTURESPROC, glCreateTextures) GLFUNC(PFNGLBINDTEXTUREUNITPROC, glBindTextureUnit) GLFUNC(PFNGLGETTEXTURELEVELPARAMETERIVPROC, glGetTextureLevelParameteriv) GLFUNC(PFNGLTEXTUREPARAMETERIPROC, glTextureParameteri) GLFUNC(PFNGLGETTEXTURESUBIMAGEPROC, glGetTextureSubImage) +GLFUNC(PFNGLTEXTURESUBIMAGE1DPROC, glTextureSubImage1D) GLFUNC(PFNGLTEXTURESUBIMAGE2DPROC, glTextureSubImage2D); GLFUNC(PFNGLTEXTURESUBIMAGE3DPROC, glTextureSubImage3D) +GLFUNC(PFNGLTEXTURESTORAGE1DPROC, glTextureStorage1D) +GLFUNC(PFNGLTEXTURESTORAGE2DPROC, glTextureStorage2D) +GLFUNC(PFNGLTEXTURESTORAGE3DPROC, glTextureStorage3D) // instancing / draw From 2167143c17168cc7376b08a8fb3a501c1bc3ab87 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:22:59 +0100 Subject: [PATCH 082/101] Latte: Support for SAMPLE_LB --- .../HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp | 4 ++++ .../LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp | 9 +++++++-- .../LegacyShaderDecompiler/LatteDecompilerInternal.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp index 30e3d7a2..cf88b901 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp @@ -666,6 +666,9 @@ void LatteDecompiler_ParseTEXClause(LatteDecompilerShader* shaderContext, LatteD uint32 offsetY = (word2 >> 5) & 0x1F; uint32 offsetZ = (word2 >> 10) & 0x1F; + sint8 lodBias = (word2 >> 21) & 0x7F; + if ((lodBias&0x40) != 0) + lodBias |= 0x80; // bufferID -> Texture index // samplerId -> Sampler index sint32 textureIndex = bufferId - 0x00; @@ -693,6 +696,7 @@ void LatteDecompiler_ParseTEXClause(LatteDecompilerShader* shaderContext, LatteD texInstruction.textureFetch.unnormalized[1] = coordTypeY == 0; texInstruction.textureFetch.unnormalized[2] = coordTypeZ == 0; texInstruction.textureFetch.unnormalized[3] = coordTypeW == 0; + texInstruction.textureFetch.lodBias = (sint8)lodBias; cfInstruction->instructionsTEX.emplace_back(texInstruction); } else if( inst0_4 == GPU7_TEX_INST_SET_CUBEMAP_INDEX ) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index a37ba011..aa7b7162 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -2561,8 +2561,13 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt // lod or lod bias parameter if( texOpcode == GPU7_TEX_INST_SAMPLE_L || texOpcode == GPU7_TEX_INST_SAMPLE_LB || texOpcode == GPU7_TEX_INST_SAMPLE_C_L) { - src->add(","); - _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 3, LATTE_DECOMPILER_DTYPE_FLOAT); + if(texOpcode == GPU7_TEX_INST_SAMPLE_LB) + src->addFmt("{}", (float)texInstruction->textureFetch.lodBias / 16.0f); + else + { + src->add(","); + _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 3, LATTE_DECOMPILER_DTYPE_FLOAT); + } } else if( texOpcode == GPU7_TEX_INST_SAMPLE_LZ || texOpcode == GPU7_TEX_INST_SAMPLE_C_LZ ) { diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h index 54112ddf..ac2a1fe1 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h @@ -57,6 +57,7 @@ struct LatteDecompilerTEXInstruction sint8 offsetY{}; sint8 offsetZ{}; bool unnormalized[4]{}; // set if texture coordinates are in [0,dim] range instead of [0,1] + sint8 lodBias{}; // divide by 16 to get actual value }textureFetch; // memRead struct From d2ba4e65c5d7802e53faac21922be1bf9347f325 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:09:30 +0100 Subject: [PATCH 083/101] Latte: 1D views are compatible with 1D textures --- src/Cafe/HW/Latte/Core/LatteTexture.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index 06afed60..d38af8ec 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -795,6 +795,8 @@ bool IsDimensionCompatibleForView(Latte::E_DIM baseDim, Latte::E_DIM viewDim) bool incompatibleDim = false; if (baseDim == Latte::E_DIM::DIM_2D && viewDim == Latte::E_DIM::DIM_2D) ; + else if (baseDim == Latte::E_DIM::DIM_1D && viewDim == Latte::E_DIM::DIM_1D) + ; else if (baseDim == Latte::E_DIM::DIM_2D && viewDim == Latte::E_DIM::DIM_2D_ARRAY) ; else if (baseDim == Latte::E_DIM::DIM_CUBEMAP && viewDim == Latte::E_DIM::DIM_CUBEMAP) From bab1616565b8fab99c52e0bedb275ae90a7b53df Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:43:37 +0100 Subject: [PATCH 084/101] nsysnet: Add support for SO_BIO and handle SO_ENOTCONN --- .../libs/nn_olv/nn_olv_UploadCommunityTypes.h | 6 ++---- src/Cafe/OS/libs/nsysnet/nsysnet.cpp | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h index 16ebd29a..2c53f118 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h @@ -256,10 +256,8 @@ namespace nn this->flags = 0; memset(this->titleText, 0, sizeof(this->titleText)); memset(this->description, 0, sizeof(this->description)); - int v2 = 0; - do - memset(this->searchKeys[v2++], 0, sizeof(this->searchKeys[v2++])); - while (v2 < 5); + for (int i = 0; i < 5; i++) + memset(this->searchKeys[i], 0, sizeof(this->searchKeys[0])); } static UploadCommunityDataParam* __ctor(UploadCommunityDataParam* _this) { diff --git a/src/Cafe/OS/libs/nsysnet/nsysnet.cpp b/src/Cafe/OS/libs/nsysnet/nsysnet.cpp index e0224148..128c19a5 100644 --- a/src/Cafe/OS/libs/nsysnet/nsysnet.cpp +++ b/src/Cafe/OS/libs/nsysnet/nsysnet.cpp @@ -13,6 +13,7 @@ #define WSAESHUTDOWN ESHUTDOWN #define WSAECONNABORTED ECONNABORTED #define WSAHOST_NOT_FOUND EAI_NONAME +#define WSAENOTCONN ENOTCONN #define GETLASTERR errno @@ -40,6 +41,7 @@ #define WU_SO_RCVBUF 0x1002 #define WU_SO_LASTERROR 0x1007 #define WU_SO_NBIO 0x1014 +#define WU_SO_BIO 0x1015 #define WU_SO_NONBLOCK 0x1016 #define WU_TCP_NODELAY 0x2004 @@ -53,6 +55,7 @@ #define WU_SO_SUCCESS 0x0000 #define WU_SO_EWOULDBLOCK 0x0006 #define WU_SO_ECONNRESET 0x0008 +#define WU_SO_ENOTCONN 0x0009 #define WU_SO_EINVAL 0x000B #define WU_SO_EINPROGRESS 0x0016 #define WU_SO_EAFNOSUPPORT 0x0021 @@ -148,8 +151,11 @@ sint32 _translateError(sint32 returnCode, sint32 wsaError, sint32 mode = _ERROR_ case WSAESHUTDOWN: _setSockError(WU_SO_ESHUTDOWN); break; + case WSAENOTCONN: + _setSockError(WU_SO_ENOTCONN); + break; default: - cemuLog_logDebug(LogType::Force, "Unhandled wsaError {}\n", wsaError); + cemuLog_logDebug(LogType::Force, "Unhandled wsaError {}", wsaError); _setSockError(99999); // unhandled error } return -1; @@ -157,7 +163,7 @@ sint32 _translateError(sint32 returnCode, sint32 wsaError, sint32 mode = _ERROR_ void nsysnetExport_socketlasterr(PPCInterpreter_t* hCPU) { - cemuLog_log(LogType::Socket, "socketlasterr() -> {}", _getSockError()); + cemuLog_logDebug(LogType::Socket, "socketlasterr() -> {}", _getSockError()); osLib_returnFromFunction(hCPU, _getSockError()); } @@ -485,9 +491,9 @@ void nsysnetExport_setsockopt(PPCInterpreter_t* hCPU) if (r != 0) cemu_assert_suspicious(); } - else if (optname == WU_SO_NBIO) + else if (optname == WU_SO_NBIO || optname == WU_SO_BIO) { - // similar to WU_SO_NONBLOCK but always sets non-blocking mode regardless of option value + // similar to WU_SO_NONBLOCK but always sets blocking (_BIO) or non-blocking (_NBIO) mode regardless of option value if (optlen == 4) { sint32 optvalLE = _swapEndianU32(*(uint32*)optval); @@ -498,9 +504,10 @@ void nsysnetExport_setsockopt(PPCInterpreter_t* hCPU) } else cemu_assert_suspicious(); - u_long mode = 1; + bool setNonBlocking = optname == WU_SO_NBIO; + u_long mode = setNonBlocking ? 1 : 0; _socket_nonblock(vs->s, mode); - vs->isNonBlocking = true; + vs->isNonBlocking = setNonBlocking; } else if (optname == WU_SO_NONBLOCK) { From 47091d836b3fd1ae1b1fcfa8b642e881f765799a Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 23 Dec 2023 22:44:10 +0200 Subject: [PATCH 085/101] Fixed build --- src/Cafe/Filesystem/FST/KeyCache.cpp | 1 - src/Cafe/GraphicPack/GraphicPack2Patches.cpp | 15 ++++---- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 2 +- .../Tools/DownloadManager/DownloadManager.cpp | 36 +++++++++---------- src/android/app/build.gradle | 3 +- src/config/CemuConfig.h | 19 +++++----- src/gui/CMakeLists.txt | 9 ++--- src/gui/input/panels/InputPanel.cpp | 2 +- vcpkg.json | 5 ++- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Cafe/Filesystem/FST/KeyCache.cpp b/src/Cafe/Filesystem/FST/KeyCache.cpp index 425e719e..c69c65ca 100644 --- a/src/Cafe/Filesystem/FST/KeyCache.cpp +++ b/src/Cafe/Filesystem/FST/KeyCache.cpp @@ -1,5 +1,4 @@ #include -#include #include "config/ActiveSettings.h" #include "util/crypto/aes128.h" diff --git a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp index 5ed0e489..6b6915d7 100644 --- a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp @@ -5,7 +5,6 @@ #include "Cafe/OS/RPL/rpl_structs.h" #include "boost/algorithm/string.hpp" -#include "gui/helpers/wxHelpers.h" // error handler void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumber, std::string_view errorMsg) { @@ -38,13 +37,13 @@ void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumb void PatchErrorHandler::showStageErrorMessageBox() { - wxString errorMsg; + std::string errorMsg; if (m_gp) { if (m_stage == STAGE::PARSER) - errorMsg.assign(formatWxString(_("Failed to load patches for graphic pack \'{}\'"), m_gp->GetName())); + errorMsg = fmt::format("Failed to load patches for graphic pack \'{}\'", m_gp->GetName()); else - errorMsg.assign(formatWxString(_("Failed to apply patches for graphic pack \'{}\'"), m_gp->GetName())); + errorMsg = fmt::format("Failed to apply patches for graphic pack \'{}\'", m_gp->GetName()); } else { @@ -52,13 +51,11 @@ void PatchErrorHandler::showStageErrorMessageBox() } if (cemuLog_isLoggingEnabled(LogType::Patches)) { - errorMsg.append("\n \n") - .append(_("Details:")) - .append("\n"); + errorMsg += "\n\nDetails:\n"; for (auto& itr : errorMessages) { - errorMsg.append(itr); - errorMsg.append("\n"); + errorMsg += itr; + errorMsg += "\n"; } } cemuLog_log(LogType::Force, "Graphic pack error: {}", errorMsg); diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index 9250282b..9438c350 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -1,9 +1,9 @@ #include "Debugger.h" #include "Cafe/OS/RPL/rpl_structs.h" +#include "Cafe/OS/RPL/rpl.h" #include "Cemu/PPCAssembler/ppcAssembler.h" #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" #include "Cemu/ExpressionParser/ExpressionParser.h" - #include "Cafe/OS/libs/coreinit/coreinit.h" #if BOOST_OS_WINDOWS diff --git a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp index 6b27e067..7637ee01 100644 --- a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp +++ b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp @@ -375,7 +375,7 @@ bool DownloadManager::syncAccountTickets() for (auto& tiv : resultTicketIds.tivs) { index++; - std::string msg = _("Downloading account ticket"); + std::string msg = "Downloading account ticket"; msg.append(fmt::format(" {0}/{1}", index, count)); setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); // skip if already cached @@ -428,7 +428,7 @@ bool DownloadManager::syncAccountTickets() bool DownloadManager::syncSystemTitleTickets() { - setStatusMessage(_("Downloading system tickets...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage("Downloading system tickets...", DLMGR_STATUS_CODE::CONNECTING); // todo - add GetAuth() function NAPI::AuthInfo authInfo; authInfo.accountId = m_authInfo.nnidAccountName; @@ -490,7 +490,7 @@ bool DownloadManager::syncSystemTitleTickets() // build list of updates for which either an installed game exists or the base title ticket is cached bool DownloadManager::syncUpdateTickets() { - setStatusMessage(_("Retrieving update information...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage("Retrieving update information...", DLMGR_STATUS_CODE::CONNECTING); // download update version list downloadTitleVersionList(); if (!m_hasTitleVersionList) @@ -512,7 +512,7 @@ bool DownloadManager::syncUpdateTickets() if (titleIdParser.GetType() != TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE) continue; - std::string msg = _("Downloading ticket"); + std::string msg = "Downloading ticket"; msg.append(fmt::format(" {0}/{1}", updateIndex, numUpdates)); updateIndex++; setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); @@ -565,12 +565,12 @@ bool DownloadManager::syncTicketCache() for (auto& ticketInfo : m_ticketCache) { index++; - std::string msg = _("Downloading meta data"); + std::string msg = "Downloading meta data"; msg.append(fmt::format(" {0}/{1}", index, count)); setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); prepareIDBE(ticketInfo.titleId); } - setStatusMessage(_("Connected. Right click entries in the list to start downloading").utf8_string(), DLMGR_STATUS_CODE::CONNECTED); + setStatusMessage("Connected. Right click entries in the list to start downloading", DLMGR_STATUS_CODE::CONNECTED); return true; } @@ -656,7 +656,7 @@ void DownloadManager::_handle_connect() // reset login state m_iasToken.serviceAccountId.clear(); m_iasToken.deviceToken.clear(); - setStatusMessage(_("Logging in...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage("Logging in...", DLMGR_STATUS_CODE::CONNECTING); // retrieve ECS AccountId + DeviceToken from cache if (s_nupFileCache) { @@ -679,7 +679,7 @@ void DownloadManager::_handle_connect() cemuLog_log(LogType::Force, "Failed to request IAS token"); cemu_assert_debug(false); m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(_("Login failed. Outdated or incomplete online files?").utf8_string(), DLMGR_STATUS_CODE::FAILED); + setStatusMessage("Login failed. Outdated or incomplete online files?", DLMGR_STATUS_CODE::FAILED); return; } } @@ -687,16 +687,16 @@ void DownloadManager::_handle_connect() if (!_connect_queryAccountStatusAndServiceURLs()) { m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(_("Failed to query account status. Invalid account information?").utf8_string(), DLMGR_STATUS_CODE::FAILED); + setStatusMessage("Failed to query account status. Invalid account information?", DLMGR_STATUS_CODE::FAILED); return; } // load ticket cache and sync - setStatusMessage(_("Updating ticket cache").utf8_string(), DLMGR_STATUS_CODE::CONNECTING); + setStatusMessage("Updating ticket cache", DLMGR_STATUS_CODE::CONNECTING); loadTicketCache(); if (!syncTicketCache()) { m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(_("Failed to request tickets (invalid NNID?)").utf8_string(), DLMGR_STATUS_CODE::FAILED); + setStatusMessage("Failed to request tickets (invalid NNID?)", DLMGR_STATUS_CODE::FAILED); return; } searchForIncompleteDownloads(); @@ -720,7 +720,7 @@ void DownloadManager::connect( if (nnidAccountName.empty()) { m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(_("This account is not linked with an NNID").utf8_string(), DLMGR_STATUS_CODE::FAILED); + setStatusMessage("This account is not linked with an NNID", DLMGR_STATUS_CODE::FAILED); return; } runManager(); @@ -730,7 +730,7 @@ void DownloadManager::connect( { cemuLog_log(LogType::Force, "DLMgr: Invalid password hash"); m_connectState.store(CONNECT_STATE::FAILED); - setStatusMessage(_("Failed. Account does not have password set").utf8_string(), DLMGR_STATUS_CODE::FAILED); + setStatusMessage("Failed. Account does not have password set", DLMGR_STATUS_CODE::FAILED); return; } m_authInfo.region = region; @@ -1058,7 +1058,7 @@ void DownloadManager::asyncPackageDownloadTMD(Package* package) std::unique_lock _l(m_mutex); if (!tmdResult.isValid) { - setPackageError(package, _("TMD download failed").utf8_string()); + setPackageError(package, "TMD download failed"); package->state.isDownloadingTMD = false; return; } @@ -1067,7 +1067,7 @@ void DownloadManager::asyncPackageDownloadTMD(Package* package) NCrypto::TMDParser tmdParser; if (!tmdParser.parse(tmdResult.tmdData.data(), tmdResult.tmdData.size())) { - setPackageError(package, _("Invalid TMD").utf8_string()); + setPackageError(package, "Invalid TMD"); package->state.isDownloadingTMD = false; return; } @@ -1176,7 +1176,7 @@ void DownloadManager::asyncPackageDownloadContentFile(Package* package, uint16 i size_t bytesWritten = callbackInfo->receiveBuffer.size(); if (callbackInfo->fileOutput->writeData(callbackInfo->receiveBuffer.data(), callbackInfo->receiveBuffer.size()) != (uint32)callbackInfo->receiveBuffer.size()) { - callbackInfo->downloadMgr->setPackageError(callbackInfo->package, _("Cannot write file. Disk full?").utf8_string()); + callbackInfo->downloadMgr->setPackageError(callbackInfo->package, "Cannot write file. Disk full?"); return false; } callbackInfo->receiveBuffer.clear(); @@ -1197,12 +1197,12 @@ void DownloadManager::asyncPackageDownloadContentFile(Package* package, uint16 i callbackInfoData.fileOutput = FileStream::createFile2(packageDownloadPath / fmt::format("{:08x}.app", contentId)); if (!callbackInfoData.fileOutput) { - setPackageError(package, _("Cannot create file").utf8_string()); + setPackageError(package, "Cannot create file"); return; } if (!NAPI::CCS_GetContentFile(titleId, contentId, CallbackInfo::writeCallback, &callbackInfoData)) { - setPackageError(package, _("Download failed").utf8_string()); + setPackageError(package, "Download failed"); delete callbackInfoData.fileOutput; return; } diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 4edf3415..56633755 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -46,7 +46,8 @@ android { '-DENABLE_OPENGL=OFF', '-DBUNDLE_SPEEX=ON', '-DENABLE_DISCORD_RPC=OFF', - '-DENABLE_WAYLAND=OFF' + '-DENABLE_NSYSHID_LIBUSB=OFF', + '-DENABLE_WAYLAND=OFF', ) // abiFilters("x86_64", "arm64-v8a") abiFilters("arm64-v8a") diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index 56b5e5be..6ab86330 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -5,7 +5,6 @@ #include "util/math/vector2.h" #include "Cafe/Account/Account.h" -#include struct GameEntry { GameEntry() = default; @@ -257,15 +256,15 @@ struct fmt::formatter : formatter { string_view name; switch (v) { - case CafeConsoleRegion::JPN: name = wxTRANSLATE("Japan"); break; - case CafeConsoleRegion::USA: name = wxTRANSLATE("USA"); break; - case CafeConsoleRegion::EUR: name = wxTRANSLATE("Europe"); break; - case CafeConsoleRegion::AUS_DEPR: name = wxTRANSLATE("Australia"); break; - case CafeConsoleRegion::CHN: name = wxTRANSLATE("China"); break; - case CafeConsoleRegion::KOR: name = wxTRANSLATE("Korea"); break; - case CafeConsoleRegion::TWN: name = wxTRANSLATE("Taiwan"); break; - case CafeConsoleRegion::Auto: name = wxTRANSLATE("Auto"); break; - default: name = wxTRANSLATE("many"); break; + case CafeConsoleRegion::JPN: name = "Japan"; break; + case CafeConsoleRegion::USA: name = "USA"; break; + case CafeConsoleRegion::EUR: name = "Europe"; break; + case CafeConsoleRegion::AUS_DEPR: name = "Australia"; break; + case CafeConsoleRegion::CHN: name = "China"; break; + case CafeConsoleRegion::KOR: name = "Korea"; break; + case CafeConsoleRegion::TWN: name = "Taiwan"; break; + case CafeConsoleRegion::Auto: name = "Auto"; break; + default: name = "many"; break; } return formatter::format(name, ctx); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 45b31ea4..b207416d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -151,7 +151,7 @@ target_link_libraries(CemuGui PRIVATE ZArchive::zarchive ) -if(UNIX AND NOT APPLE) +if(ENABLE_WXWIDGETS AND UNIX AND NOT APPLE) target_link_libraries(CemuGui PRIVATE GTK3::gtk) if (ENABLE_WAYLAND) target_link_libraries(CemuGui PRIVATE Wayland::Client CemuWaylandProtocols) @@ -167,10 +167,11 @@ if(UNIX AND NOT APPLE) target_link_libraries(CemuGui PRIVATE gamemode) endif() endif() - -# PUBLIC because wx/app.h is included in CemuApp.h -target_link_libraries(CemuGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc) +if (ENABLE_WXWIDGETS) + # PUBLIC because wx/app.h is included in CemuApp.h + target_link_libraries(CemuGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc) endif() if(WIN32) target_link_libraries(CemuGui PRIVATE bthprops) +endif() diff --git a/src/gui/input/panels/InputPanel.cpp b/src/gui/input/panels/InputPanel.cpp index dc7f02e6..adac9bc0 100644 --- a/src/gui/input/panels/InputPanel.cpp +++ b/src/gui/input/panels/InputPanel.cpp @@ -26,7 +26,7 @@ void InputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, cons const auto mapping = reinterpret_cast(element->GetClientData()); // reset mapping - if(std::exchange(m_right_down, false) || gui_isKeyDown(PlatformKeyCodes::ESCAPE)) + if(std::exchange(m_right_down, false)) { element->SetBackgroundColour(kKeyColourNormalMode); m_color_backup[element->GetId()] = kKeyColourNormalMode; diff --git a/vcpkg.json b/vcpkg.json index 39979f1b..b2c601dc 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -60,6 +60,9 @@ "default-features": false, "features": [ "openssl" ] }, - "libusb" + { + "name": "libusb", + "platform": "!android" + } ] } From 4405116324fda4895ccdc1ec6bc32ee253b16261 Mon Sep 17 00:00:00 2001 From: GaryOderNichts <12049776+GaryOderNichts@users.noreply.github.com> Date: Sun, 24 Dec 2023 00:25:01 +0100 Subject: [PATCH 086/101] GDBStub: Support watchpoints on linux (#1030) * GDBStub: Support watchpoints on linux * GDBStub: Use `TCP_NODELAY` --- src/Cafe/CMakeLists.txt | 1 + .../HW/Espresso/Debugger/GDBBreakpoints.cpp | 304 ++++++++++++++++++ .../HW/Espresso/Debugger/GDBBreakpoints.h | 211 +----------- src/Cafe/HW/Espresso/Debugger/GDBStub.cpp | 8 + src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 31 +- .../ExceptionHandler_posix.cpp | 13 + 6 files changed, 371 insertions(+), 197 deletions(-) create mode 100644 src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 29c5a0b3..9e20bb33 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -40,6 +40,7 @@ add_library(CemuCafe HW/Espresso/Debugger/DebugSymbolStorage.h HW/Espresso/Debugger/GDBStub.h HW/Espresso/Debugger/GDBStub.cpp + HW/Espresso/Debugger/GDBBreakpoints.cpp HW/Espresso/Debugger/GDBBreakpoints.h HW/Espresso/EspressoISA.h HW/Espresso/Interpreter/PPCInterpreterALU.hpp diff --git a/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp b/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp new file mode 100644 index 00000000..675050d3 --- /dev/null +++ b/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp @@ -0,0 +1,304 @@ +#include "GDBBreakpoints.h" +#include "Debugger.h" +#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" + +#if defined(ARCH_X86_64) && BOOST_OS_LINUX +#include +#include +#include + +DRType _GetDR(pid_t tid, int drIndex) +{ + size_t drOffset = offsetof(struct user, u_debugreg) + drIndex * sizeof(user::u_debugreg[0]); + + long v; + v = ptrace(PTRACE_PEEKUSER, tid, drOffset, nullptr); + if (v == -1) + perror("ptrace(PTRACE_PEEKUSER)"); + + return (DRType)v; +} + +void _SetDR(pid_t tid, int drIndex, DRType newValue) +{ + size_t drOffset = offsetof(struct user, u_debugreg) + drIndex * sizeof(user::u_debugreg[0]); + + long rc = ptrace(PTRACE_POKEUSER, tid, drOffset, newValue); + if (rc == -1) + perror("ptrace(PTRACE_POKEUSER)"); +} + +DRType _ReadDR6() +{ + pid_t tid = gettid(); + + // linux doesn't let us attach to the current thread / threads in the current thread group + // we have to create a child process which then modifies the debug registers and quits + pid_t child = fork(); + if (child == -1) + { + perror("fork"); + return 0; + } + + if (child == 0) + { + if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr)) + { + perror("attach"); + _exit(0); + } + + waitpid(tid, NULL, 0); + + uint64_t dr6 = _GetDR(tid, 6); + + if (ptrace(PTRACE_DETACH, tid, nullptr, nullptr)) + perror("detach"); + + // since the status code only uses the lower 8 bits, we have to discard the rest of DR6 + // this should be fine though, since the lower 4 bits of DR6 contain all the bp conditions + _exit(dr6 & 0xff); + } + + // wait for child process + int wstatus; + waitpid(child, &wstatus, 0); + + return (DRType)WEXITSTATUS(wstatus); +} +#endif + +GDBServer::ExecutionBreakpoint::ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason) + : m_address(address), m_removedAfterInterrupt(false), m_reason(std::move(reason)) +{ + if (type == BreakpointType::BP_SINGLE) + { + this->m_pauseThreads = true; + this->m_restoreAfterInterrupt = false; + this->m_deleteAfterAnyInterrupt = false; + this->m_pauseOnNextInterrupt = false; + this->m_visible = visible; + } + else if (type == BreakpointType::BP_PERSISTENT) + { + this->m_pauseThreads = true; + this->m_restoreAfterInterrupt = true; + this->m_deleteAfterAnyInterrupt = false; + this->m_pauseOnNextInterrupt = false; + this->m_visible = visible; + } + else if (type == BreakpointType::BP_RESTORE_POINT) + { + this->m_pauseThreads = false; + this->m_restoreAfterInterrupt = false; + this->m_deleteAfterAnyInterrupt = false; + this->m_pauseOnNextInterrupt = false; + this->m_visible = false; + } + else if (type == BreakpointType::BP_STEP_POINT) + { + this->m_pauseThreads = false; + this->m_restoreAfterInterrupt = false; + this->m_deleteAfterAnyInterrupt = true; + this->m_pauseOnNextInterrupt = true; + this->m_visible = false; + } + + this->m_origOpCode = memory_readU32(address); + memory_writeU32(address, DEBUGGER_BP_T_GDBSTUB_TW); + PPCRecompiler_invalidateRange(address, address + 4); +} + +GDBServer::ExecutionBreakpoint::~ExecutionBreakpoint() +{ + memory_writeU32(this->m_address, this->m_origOpCode); + PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4); +} + +uint32 GDBServer::ExecutionBreakpoint::GetVisibleOpCode() const +{ + if (this->m_visible) + return memory_readU32(this->m_address); + else + return this->m_origOpCode; +} + +void GDBServer::ExecutionBreakpoint::RemoveTemporarily() +{ + memory_writeU32(this->m_address, this->m_origOpCode); + PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4); + this->m_restoreAfterInterrupt = true; +} + +void GDBServer::ExecutionBreakpoint::Restore() +{ + memory_writeU32(this->m_address, DEBUGGER_BP_T_GDBSTUB_TW); + PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4); + this->m_restoreAfterInterrupt = false; +} + +namespace coreinit +{ +#if BOOST_OS_LINUX + std::vector& OSGetSchedulerThreadIds(); +#endif + + std::vector& OSGetSchedulerThreads(); +} + +GDBServer::AccessBreakpoint::AccessBreakpoint(MPTR address, AccessPointType type) + : m_address(address), m_type(type) +{ +#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS + for (auto& hThreadNH : coreinit::OSGetSchedulerThreads()) + { + HANDLE hThread = (HANDLE)hThreadNH; + CONTEXT ctx{}; + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + SuspendThread(hThread); + GetThreadContext(hThread, &ctx); + + // use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already + ctx.Dr2 = (DWORD64)memory_getPointerFromVirtualOffset(address); + ctx.Dr3 = (DWORD64)memory_getPointerFromVirtualOffset(address); + // breakpoint 2 + SetBits(ctx.Dr7, 4, 1, 1); // breakpoint #3 enabled: true + SetBits(ctx.Dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write) + SetBits(ctx.Dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes) + // breakpoint 3 + SetBits(ctx.Dr7, 6, 1, 1); // breakpoint #4 enabled: true + SetBits(ctx.Dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write) + SetBits(ctx.Dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes) + + SetThreadContext(hThread, &ctx); + ResumeThread(hThread); + } +#elif defined(ARCH_X86_64) && BOOST_OS_LINUX + // linux doesn't let us attach to threads which are in the same thread group as our current thread + // we have to create a child process which then modifies the debug registers and quits + pid_t child = fork(); + if (child == -1) + { + perror("fork"); + return; + } + + if (child == 0) + { + for (pid_t tid : coreinit::OSGetSchedulerThreadIds()) + { + long rc = ptrace(PTRACE_ATTACH, tid, nullptr, nullptr); + if (rc == -1) + perror("ptrace(PTRACE_ATTACH)"); + + waitpid(tid, nullptr, 0); + + DRType dr7 = _GetDR(tid, 7); + // use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already + DRType dr2 = (uint64)memory_getPointerFromVirtualOffset(address); + DRType dr3 = (uint64)memory_getPointerFromVirtualOffset(address); + // breakpoint 2 + SetBits(dr7, 4, 1, 1); // breakpoint #3 enabled: true + SetBits(dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write) + SetBits(dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes) + // breakpoint 3 + SetBits(dr7, 6, 1, 1); // breakpoint #4 enabled: true + SetBits(dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write) + SetBits(dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes) + + _SetDR(tid, 2, dr2); + _SetDR(tid, 3, dr3); + _SetDR(tid, 7, dr7); + + rc = ptrace(PTRACE_DETACH, tid, nullptr, nullptr); + if (rc == -1) + perror("ptrace(PTRACE_DETACH)"); + } + + // exit child process + _exit(0); + } + + // wait for child process + waitpid(child, nullptr, 0); +#else + cemuLog_log(LogType::Force, "Debugger read/write breakpoints are not supported on non-x86 CPUs yet."); +#endif +} + +GDBServer::AccessBreakpoint::~AccessBreakpoint() +{ +#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS + for (auto& hThreadNH : coreinit::OSGetSchedulerThreads()) + { + HANDLE hThread = (HANDLE)hThreadNH; + CONTEXT ctx{}; + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + SuspendThread(hThread); + GetThreadContext(hThread, &ctx); + + // reset BP 2/3 to zero + ctx.Dr2 = (DWORD64)0; + ctx.Dr3 = (DWORD64)0; + // breakpoint 2 + SetBits(ctx.Dr7, 4, 1, 0); + SetBits(ctx.Dr7, 24, 2, 0); + SetBits(ctx.Dr7, 26, 2, 0); + // breakpoint 3 + SetBits(ctx.Dr7, 6, 1, 0); + SetBits(ctx.Dr7, 28, 2, 0); + SetBits(ctx.Dr7, 30, 2, 0); + SetThreadContext(hThread, &ctx); + ResumeThread(hThread); + } +#elif defined(ARCH_X86_64) && BOOST_OS_LINUX + // linux doesn't let us attach to threads which are in the same thread group as our current thread + // we have to create a child process which then modifies the debug registers and quits + pid_t child = fork(); + if (child == -1) + { + perror("fork"); + return; + } + + if (child == 0) + { + for (pid_t tid : coreinit::OSGetSchedulerThreadIds()) + { + long rc = ptrace(PTRACE_ATTACH, tid, nullptr, nullptr); + if (rc == -1) + perror("ptrace(PTRACE_ATTACH)"); + + waitpid(tid, nullptr, 0); + + DRType dr7 = _GetDR(tid, 7); + // reset BP 2/3 to zero + DRType dr2 = 0; + DRType dr3 = 0; + // breakpoint 2 + SetBits(dr7, 4, 1, 0); + SetBits(dr7, 24, 2, 0); + SetBits(dr7, 26, 2, 0); + // breakpoint 3 + SetBits(dr7, 6, 1, 0); + SetBits(dr7, 28, 2, 0); + SetBits(dr7, 30, 2, 0); + + _SetDR(tid, 2, dr2); + _SetDR(tid, 3, dr3); + _SetDR(tid, 7, dr7); + + rc = ptrace(PTRACE_DETACH, tid, nullptr, nullptr); + if (rc == -1) + perror("ptrace(PTRACE_DETACH)"); + } + + // exit child process + _exit(0); + } + + // wait for child process + waitpid(child, nullptr, 0); +#endif +} diff --git a/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.h b/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.h index b86bd9a6..f94365c2 100644 --- a/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.h +++ b/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.h @@ -1,33 +1,18 @@ +#pragma once +#include "GDBStub.h" #include -#if defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE -#include -#include -#include +#if defined(ARCH_X86_64) && BOOST_OS_LINUX +#include // helpers for accessing debug register typedef unsigned long DRType; -DRType _GetDR(pid_t tid, int drIndex) -{ - unsigned long v; - v = ptrace (PTRACE_PEEKUSER, tid, offsetof (struct user, u_debugreg[drIndex]), 0); - return (DRType)v; -} - -void _SetDR(pid_t tid, int drIndex, DRType newValue) -{ - unsigned long v = newValue; - ptrace (PTRACE_POKEUSER, tid, offsetof (struct user, u_debugreg[drIndex]), v); -} - +DRType _GetDR(pid_t tid, int drIndex); +void _SetDR(pid_t tid, int drIndex, DRType newValue); +DRType _ReadDR6(); #endif -namespace coreinit -{ - std::vector& OSGetSchedulerThreads(); -} - enum class BreakpointType { BP_SINGLE, @@ -38,59 +23,10 @@ enum class BreakpointType class GDBServer::ExecutionBreakpoint { public: - ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason) - : m_address(address), m_removedAfterInterrupt(false), m_reason(std::move(reason)) - { - if (type == BreakpointType::BP_SINGLE) - { - this->m_pauseThreads = true; - this->m_restoreAfterInterrupt = false; - this->m_deleteAfterAnyInterrupt = false; - this->m_pauseOnNextInterrupt = false; - this->m_visible = visible; - } - else if (type == BreakpointType::BP_PERSISTENT) - { - this->m_pauseThreads = true; - this->m_restoreAfterInterrupt = true; - this->m_deleteAfterAnyInterrupt = false; - this->m_pauseOnNextInterrupt = false; - this->m_visible = visible; - } - else if (type == BreakpointType::BP_RESTORE_POINT) - { - this->m_pauseThreads = false; - this->m_restoreAfterInterrupt = false; - this->m_deleteAfterAnyInterrupt = false; - this->m_pauseOnNextInterrupt = false; - this->m_visible = false; - } - else if (type == BreakpointType::BP_STEP_POINT) - { - this->m_pauseThreads = false; - this->m_restoreAfterInterrupt = false; - this->m_deleteAfterAnyInterrupt = true; - this->m_pauseOnNextInterrupt = true; - this->m_visible = false; - } + ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason); + ~ExecutionBreakpoint(); - this->m_origOpCode = memory_readU32(address); - memory_writeU32(address, DEBUGGER_BP_T_GDBSTUB_TW); - PPCRecompiler_invalidateRange(address, address + 4); - }; - ~ExecutionBreakpoint() - { - memory_writeU32(this->m_address, this->m_origOpCode); - PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4); - }; - - [[nodiscard]] uint32 GetVisibleOpCode() const - { - if (this->m_visible) - return memory_readU32(this->m_address); - else - return this->m_origOpCode; - }; + [[nodiscard]] uint32 GetVisibleOpCode() const; [[nodiscard]] bool ShouldBreakThreads() const { return this->m_pauseThreads; @@ -118,18 +54,8 @@ public: return m_reason; }; - void RemoveTemporarily() - { - memory_writeU32(this->m_address, this->m_origOpCode); - PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4); - this->m_restoreAfterInterrupt = true; - }; - void Restore() - { - memory_writeU32(this->m_address, DEBUGGER_BP_T_GDBSTUB_TW); - PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4); - this->m_restoreAfterInterrupt = false; - }; + void RemoveTemporarily(); + void Restore(); void PauseOnNextInterrupt() { this->m_pauseOnNextInterrupt = true; @@ -162,115 +88,8 @@ enum class AccessPointType class GDBServer::AccessBreakpoint { public: - AccessBreakpoint(MPTR address, AccessPointType type) - : m_address(address), m_type(type) - { -#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS - for (auto& hThreadNH : coreinit::OSGetSchedulerThreads()) - { - HANDLE hThread = (HANDLE)hThreadNH; - CONTEXT ctx{}; - ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; - SuspendThread(hThread); - GetThreadContext(hThread, &ctx); - - // use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already - ctx.Dr2 = (DWORD64)memory_getPointerFromVirtualOffset(address); - ctx.Dr3 = (DWORD64)memory_getPointerFromVirtualOffset(address); - // breakpoint 2 - SetBits(ctx.Dr7, 4, 1, 1); // breakpoint #3 enabled: true - SetBits(ctx.Dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write) - SetBits(ctx.Dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes) - // breakpoint 3 - SetBits(ctx.Dr7, 6, 1, 1); // breakpoint #4 enabled: true - SetBits(ctx.Dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write) - SetBits(ctx.Dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes) - - SetThreadContext(hThread, &ctx); - ResumeThread(hThread); - } - // todo: port the following code to all unix platforms, they seem to differ quite a bit -#elif defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE - for (auto& hThreadNH : coreinit::OSGetSchedulerThreads()) - { - pid_t pid = (pid_t)(uintptr_t)hThreadNH; - ptrace(PTRACE_ATTACH, pid, nullptr, nullptr); - waitpid(pid, nullptr, 0); - - DRType dr7 = _GetDR(pid, 7); - // use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already - DRType dr2 = (uint64)memory_getPointerFromVirtualOffset(address); - DRType dr3 = (uint64)memory_getPointerFromVirtualOffset(address); - // breakpoint 2 - SetBits(dr7, 4, 1, 1); // breakpoint #3 enabled: true - SetBits(dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write) - SetBits(dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes) - // breakpoint 3 - SetBits(dr7, 6, 1, 1); // breakpoint #4 enabled: true - SetBits(dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write) - SetBits(dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes) - - _SetDR(pid, 2, dr2); - _SetDR(pid, 3, dr3); - _SetDR(pid, 7, dr7); - ptrace(PTRACE_DETACH, pid, nullptr, nullptr); - } -#else - cemuLog_log(LogType::Force, "Debugger read/write breakpoints are not supported on non-x86 CPUs yet."); -#endif - }; - ~AccessBreakpoint() - { -#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS - for (auto& hThreadNH : coreinit::OSGetSchedulerThreads()) - { - HANDLE hThread = (HANDLE)hThreadNH; - CONTEXT ctx{}; - ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; - SuspendThread(hThread); - GetThreadContext(hThread, &ctx); - - // reset BP 2/3 to zero - ctx.Dr2 = (DWORD64)0; - ctx.Dr3 = (DWORD64)0; - // breakpoint 2 - SetBits(ctx.Dr7, 4, 1, 0); - SetBits(ctx.Dr7, 24, 2, 0); - SetBits(ctx.Dr7, 26, 2, 0); - // breakpoint 3 - SetBits(ctx.Dr7, 6, 1, 0); - SetBits(ctx.Dr7, 28, 2, 0); - SetBits(ctx.Dr7, 30, 2, 0); - SetThreadContext(hThread, &ctx); - ResumeThread(hThread); - } -#elif defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE - for (auto& hThreadNH : coreinit::OSGetSchedulerThreads()) - { - pid_t pid = (pid_t)(uintptr_t)hThreadNH; - ptrace(PTRACE_ATTACH, pid, nullptr, nullptr); - waitpid(pid, nullptr, 0); - - DRType dr7 = _GetDR(pid, 7); - // reset BP 2/3 to zero - DRType dr2 = 0; - DRType dr3 = 0; - // breakpoint 2 - SetBits(dr7, 4, 1, 0); - SetBits(dr7, 24, 2, 0); - SetBits(dr7, 26, 2, 0); - // breakpoint 3 - SetBits(dr7, 6, 1, 0); - SetBits(dr7, 28, 2, 0); - SetBits(dr7, 30, 2, 0); - - _SetDR(pid, 2, dr2); - _SetDR(pid, 3, dr3); - _SetDR(pid, 7, dr7); - ptrace(PTRACE_DETACH, pid, nullptr, nullptr); - } -#endif - }; + AccessBreakpoint(MPTR address, AccessPointType type); + ~AccessBreakpoint(); MPTR GetAddress() const { @@ -284,4 +103,4 @@ public: private: const MPTR m_address; const AccessPointType m_type; -}; \ No newline at end of file +}; diff --git a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp index c8308594..6cddae01 100644 --- a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp +++ b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp @@ -263,6 +263,14 @@ bool GDBServer::Initialize() return false; } + int nodelayEnabled = TRUE; + if (setsockopt(m_server_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelayEnabled, sizeof(nodelayEnabled)) == SOCKET_ERROR) + { + closesocket(m_server_socket); + m_server_socket = INVALID_SOCKET; + return false; + } + memset(&m_server_addr, 0, sizeof(m_server_addr)); m_server_addr.sin_family = AF_INET; m_server_addr.sin_addr.s_addr = htonl(INADDR_ANY); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index d9b33dca..3701a4d7 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -5,6 +5,7 @@ #include "Cafe/OS/libs/coreinit/coreinit_Time.h" #include "Cafe/OS/libs/coreinit/coreinit_Alarm.h" #include "Cafe/OS/libs/snd_core/ax.h" +#include "Cafe/HW/Espresso/Debugger/GDBStub.h" #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" @@ -1153,6 +1154,18 @@ namespace coreinit } } +#if BOOST_OS_LINUX + #include + #include + + std::vector g_schedulerThreadIds; + + std::vector& OSGetSchedulerThreadIds() + { + return g_schedulerThreadIds; + } +#endif + void OSSchedulerCoreEmulationThread(void* _assignedCoreIndex) { SetThreadName(fmt::format("OSSchedulerThread[core={}]", (uintptr_t)_assignedCoreIndex).c_str()); @@ -1160,8 +1173,21 @@ namespace coreinit #if defined(ARCH_X86_64) _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero #endif + +#if BOOST_OS_LINUX + if (g_gdbstub) + { + // need to allow the GDBStub to attach to our thread + prctl(PR_SET_DUMPABLE, (unsigned long)1); + prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); + } + + pid_t tid = gettid(); + g_schedulerThreadIds.emplace_back(tid); +#endif + t_schedulerFiber = Fiber::PrepareCurrentThread(); - + // create scheduler idle fiber and switch to it g_idleLoopFiber[t_assignedCoreIndex] = new Fiber(__OSThreadCoreIdle, nullptr, nullptr); cemu_assert_debug(PPCInterpreter_getCurrentInstance() == nullptr); @@ -1211,6 +1237,9 @@ namespace coreinit threadItr.join(); sSchedulerThreads.clear(); g_schedulerThreadHandles.clear(); +#if BOOST_OS_LINUX + g_schedulerThreadIds.clear(); +#endif // clean up all fibers for (auto& it : g_idleLoopFiber) { diff --git a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp index 34430e37..cf547110 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp @@ -6,6 +6,9 @@ #include "util/helpers/StringHelpers.h" #include "ExceptionHandler.h" +#include "Cafe/HW/Espresso/Debugger/GDBStub.h" +#include "Cafe/HW/Espresso/Debugger/GDBBreakpoints.h" + #if BOOST_OS_LINUX #include "ELFSymbolTable.h" #endif @@ -61,6 +64,16 @@ void DemangleAndPrintBacktrace(char** backtrace, size_t size) // handle signals that would dump core, print stacktrace and then dump depending on config void handlerDumpingSignal(int sig, siginfo_t *info, void *context) { +#if defined(ARCH_X86_64) && BOOST_OS_LINUX + // Check for hardware breakpoints + if (info->si_signo == SIGTRAP && info->si_code == TRAP_HWBKPT) + { + uint64 dr6 = _ReadDR6(); + g_gdbstub->HandleAccessException(dr6); + return; + } +#endif + if(!CrashLog_Create()) return; // give up if crashlog was already created From 8e666d32d0147abb6ac6b9ea2610cc5cbb6f291d Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:13:45 +0200 Subject: [PATCH 087/101] Added libucontext for android & replaced x86 intrinsics with sse2neon implementations --- .gitmodules | 3 + CMakeLists.txt | 1 + dependencies/libucontext | 1 + src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 2 - .../HW/Latte/LatteAddrLib/LatteAddrLib.cpp | 7 - src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 4 - src/Common/cpu_features.cpp | 11 + src/Common/precompiled.h | 30 +- src/Common/sse2neon.h | 9236 +++++++++++++++++ src/android/app/build.gradle | 3 +- src/android/app/src/main/cpp/EmulationState.h | 4 +- src/util/CMakeLists.txt | 8 +- src/util/Fiber/FiberUnix.cpp | 15 +- src/util/crypto/aes128.cpp | 7 - vcpkg.json | 5 +- 15 files changed, 9278 insertions(+), 59 deletions(-) create mode 160000 dependencies/libucontext create mode 100644 src/Common/sse2neon.h diff --git a/.gitmodules b/.gitmodules index 0d95d984..1042b445 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "dependencies/imgui"] path = dependencies/imgui url = https://github.com/ocornut/imgui +[submodule "dependencies/libucontext"] + path = dependencies/libucontext + url = https://github.com/SSimco/libucontext diff --git a/CMakeLists.txt b/CMakeLists.txt index 17390911..3453e78b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,7 @@ find_package(pugixml REQUIRED) find_package(RapidJSON REQUIRED) find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED) if(ANDROID) + add_subdirectory(dependencies/libucontext EXCLUDE_FROM_ALL) find_package(Boost COMPONENTS context iostreams REQUIRED) endif() find_package(libzip REQUIRED) diff --git a/dependencies/libucontext b/dependencies/libucontext new file mode 160000 index 00000000..be80075e --- /dev/null +++ b/dependencies/libucontext @@ -0,0 +1 @@ +Subproject commit be80075e957c4a61a6415c280802fea9001201a2 diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index 5e092c55..83ef7c30 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -338,7 +338,6 @@ void LatteShaderCache_Load() if (g_renderer->GetType() == RendererAPI::Vulkan) LatteShaderCache_LoadVulkanPipelineCache(cacheTitleId); -#if !__ANDROID__ g_renderer->BeginFrame(true); if (g_renderer->ImguiBegin(true)) { @@ -351,7 +350,6 @@ void LatteShaderCache_Load() LatteShaderCache_drawBackgroundImage(g_shaderCacheLoaderState.textureDRCId, 854, 480); g_renderer->ImguiEnd(); } -#endif // __ANDROID__ g_renderer->SwapBuffers(true, true); if (g_shaderCacheLoaderState.textureTVId) diff --git a/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp b/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp index c111e5af..80de2895 100644 --- a/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp +++ b/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp @@ -2,9 +2,6 @@ #include "Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.h" #include "Cafe/OS/libs/gx2/GX2_Surface.h" #include -#if __ANDROID__ -#include -#endif /* Info: @@ -75,11 +72,7 @@ namespace LatteAddrLib uint32 NextPow2(uint32 dim) { -#if __ANDROID__ - return boost::core::bit_ceil(dim); -#else return std::bit_ceil(dim); -#endif } uint32 GetBitsPerPixel(E_HWSURFFMT format, uint32* pElemMode, uint32* pExpandX, uint32* pExpandY) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index d9b33dca..949ed7b2 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -1123,9 +1123,7 @@ namespace coreinit { OSHostThread* hostThread = (OSHostThread*)_thread; - #if defined(ARCH_X86_64) _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero - #endif PPCInterpreter_t* hCPU = &hostThread->ppcInstance; __OSLoadThread(hostThread->m_thread, hCPU, hostThread->selectedCore); @@ -1157,9 +1155,7 @@ namespace coreinit { SetThreadName(fmt::format("OSSchedulerThread[core={}]", (uintptr_t)_assignedCoreIndex).c_str()); t_assignedCoreIndex = (sint32)(uintptr_t)_assignedCoreIndex; - #if defined(ARCH_X86_64) _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero - #endif t_schedulerFiber = Fiber::PrepareCurrentThread(); // create scheduler idle fiber and switch to it diff --git a/src/Common/cpu_features.cpp b/src/Common/cpu_features.cpp index dfea8851..d2cb98a6 100644 --- a/src/Common/cpu_features.cpp +++ b/src/Common/cpu_features.cpp @@ -61,6 +61,17 @@ CPUFeaturesImpl::CPUFeaturesImpl() memcpy(m_cpuBrandName + 32, cpuInfo, sizeof(cpuInfo)); } #endif +#if defined(__aarch64__) + x86.ssse3 = true; + x86.sse4_1 = true; + x86.avx = true; + x86.avx2 = true; + x86.lzcnt = true; + x86.movbe = true; + x86.bmi2 = true; + x86.aesni = true; + x86.invariant_tsc = true; +#endif } std::string CPUFeaturesImpl::GetCPUName() diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 9c31706c..77234168 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -41,6 +41,11 @@ #include #endif +#if defined(__aarch64__) +#include "sse2neon.h" +#endif + + // c++ includes #include #include @@ -332,23 +337,6 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor, // On aarch64 we handle some of the x86 intrinsics by implementing them as wrappers #if defined(__aarch64__) -inline void _mm_pause() -{ - asm volatile("yield"); -} - -inline uint64 __rdtsc() -{ - uint64 t; - asm volatile("mrs %0, cntvct_el0" : "=r" (t)); - return t; -} - -inline void _mm_mfence() -{ - -} - inline unsigned char _addcarry_u64(unsigned char carry, unsigned long long a, unsigned long long b, unsigned long long *result) { *result = a + b + (unsigned long long)carry; @@ -516,24 +504,16 @@ inline std::string_view _utf8Wrapper(std::u8string_view input) // convert fs::path to utf8 encoded string inline std::string _pathToUtf8(const fs::path& path) { -#if __ANDROID__ - return path.generic_string(); -#else std::u8string strU8 = path.generic_u8string(); std::string v((const char*)strU8.data(), strU8.size()); return v; -#endif // __ANDROID__ } // convert utf8 encoded string to fs::path inline fs::path _utf8ToPath(std::string_view input) { -#if __ANDROID__ - return fs::path(input); -#else std::basic_string_view v((char8_t*)input.data(), input.size()); return fs::path(v); -#endif // __ANDROID__ } // locale-independent variant of tolower() which also matches Wii U behavior diff --git a/src/Common/sse2neon.h b/src/Common/sse2neon.h new file mode 100644 index 00000000..32f688c1 --- /dev/null +++ b/src/Common/sse2neon.h @@ -0,0 +1,9236 @@ +#ifndef SSE2NEON_H +#define SSE2NEON_H + +// This header file provides a simple API translation layer +// between SSE intrinsics to their corresponding Arm/Aarch64 NEON versions +// +// Contributors to this work are: +// John W. Ratcliff +// Brandon Rowlett +// Ken Fast +// Eric van Beurden +// Alexander Potylitsin +// Hasindu Gamaarachchi +// Jim Huang +// Mark Cheng +// Malcolm James MacLeod +// Devin Hussey (easyaspi314) +// Sebastian Pop +// Developer Ecosystem Engineering +// Danila Kutenin +// François Turban (JishinMaster) +// Pei-Hsuan Hung +// Yang-Hao Yuan +// Syoyo Fujita +// Brecht Van Lommel +// Jonathan Hue +// Cuda Chen +// Aymen Qader +// Anthony Roberts + +/* + * sse2neon is freely redistributable under the MIT License. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Tunable configurations */ + +/* Enable precise implementation of math operations + * This would slow down the computation a bit, but gives consistent result with + * x86 SSE. (e.g. would solve a hole or NaN pixel in the rendering result) + */ +/* _mm_min|max_ps|ss|pd|sd */ +#ifndef SSE2NEON_PRECISE_MINMAX +#define SSE2NEON_PRECISE_MINMAX (0) +#endif +/* _mm_rcp_ps and _mm_div_ps */ +#ifndef SSE2NEON_PRECISE_DIV +#define SSE2NEON_PRECISE_DIV (0) +#endif +/* _mm_sqrt_ps and _mm_rsqrt_ps */ +#ifndef SSE2NEON_PRECISE_SQRT +#define SSE2NEON_PRECISE_SQRT (0) +#endif +/* _mm_dp_pd */ +#ifndef SSE2NEON_PRECISE_DP +#define SSE2NEON_PRECISE_DP (0) +#endif + +/* Enable inclusion of windows.h on MSVC platforms + * This makes _mm_clflush functional on windows, as there is no builtin. + */ +#ifndef SSE2NEON_INCLUDE_WINDOWS_H +#define SSE2NEON_INCLUDE_WINDOWS_H (0) +#endif + +/* compiler specific definitions */ +#if defined(__GNUC__) || defined(__clang__) +#pragma push_macro("FORCE_INLINE") +#pragma push_macro("ALIGN_STRUCT") +#define FORCE_INLINE static inline __attribute__((always_inline)) +#define ALIGN_STRUCT(x) __attribute__((aligned(x))) +#define _sse2neon_likely(x) __builtin_expect(!!(x), 1) +#define _sse2neon_unlikely(x) __builtin_expect(!!(x), 0) +#elif defined(_MSC_VER) +#if _MSVC_TRADITIONAL +#error Using the traditional MSVC preprocessor is not supported! Use /Zc:preprocessor instead. +#endif +#ifndef FORCE_INLINE +#define FORCE_INLINE static inline +#endif +#ifndef ALIGN_STRUCT +#define ALIGN_STRUCT(x) __declspec(align(x)) +#endif +#define _sse2neon_likely(x) (x) +#define _sse2neon_unlikely(x) (x) +#else +#pragma message("Macro name collisions may happen with unsupported compilers.") +#endif + +#if defined(__GNUC__) && __GNUC__ < 10 +#warning "GCC versions earlier than 10 are not supported." +#endif + +/* C language does not allow initializing a variable with a function call. */ +#ifdef __cplusplus +#define _sse2neon_const static const +#else +#define _sse2neon_const const +#endif + +#include +#include + +#if defined(_WIN32) +/* Definitions for _mm_{malloc,free} are provided by + * from both MinGW-w64 and MSVC. + */ +#define SSE2NEON_ALLOC_DEFINED +#endif + +/* If using MSVC */ +#ifdef _MSC_VER +#include +#if SSE2NEON_INCLUDE_WINDOWS_H +#include +#include +#endif + +#if !defined(__cplusplus) +#error SSE2NEON only supports C++ compilation with this compiler +#endif + +#ifdef SSE2NEON_ALLOC_DEFINED +#include +#endif + +#if (defined(_M_AMD64) || defined(__x86_64__)) || \ + (defined(_M_ARM64) || defined(__arm64__)) +#define SSE2NEON_HAS_BITSCAN64 +#endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define _sse2neon_define0(type, s, body) \ + __extension__({ \ + type _a = (s); \ + body \ + }) +#define _sse2neon_define1(type, s, body) \ + __extension__({ \ + type _a = (s); \ + body \ + }) +#define _sse2neon_define2(type, a, b, body) \ + __extension__({ \ + type _a = (a), _b = (b); \ + body \ + }) +#define _sse2neon_return(ret) (ret) +#else +#define _sse2neon_define0(type, a, body) [=](type _a) { body }(a) +#define _sse2neon_define1(type, a, body) [](type _a) { body }(a) +#define _sse2neon_define2(type, a, b, body) \ + [](type _a, type _b) { body }((a), (b)) +#define _sse2neon_return(ret) return ret +#endif + +#define _sse2neon_init(...) \ + { \ + __VA_ARGS__ \ + } + +/* Compiler barrier */ +#if defined(_MSC_VER) +#define SSE2NEON_BARRIER() _ReadWriteBarrier() +#else +#define SSE2NEON_BARRIER() \ + do { \ + __asm__ __volatile__("" ::: "memory"); \ + (void) 0; \ + } while (0) +#endif + +/* Memory barriers + * __atomic_thread_fence does not include a compiler barrier; instead, + * the barrier is part of __atomic_load/__atomic_store's "volatile-like" + * semantics. + */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#include +#endif + +FORCE_INLINE void _sse2neon_smp_mb(void) +{ + SSE2NEON_BARRIER(); +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(__STDC_NO_ATOMICS__) + atomic_thread_fence(memory_order_seq_cst); +#elif defined(__GNUC__) || defined(__clang__) + __atomic_thread_fence(__ATOMIC_SEQ_CST); +#else /* MSVC */ + __dmb(_ARM64_BARRIER_ISH); +#endif +} + +/* Architecture-specific build options */ +/* FIXME: #pragma GCC push_options is only available on GCC */ +#if defined(__GNUC__) +#if defined(__arm__) && __ARM_ARCH == 7 +/* According to ARM C Language Extensions Architecture specification, + * __ARM_NEON is defined to a value indicating the Advanced SIMD (NEON) + * architecture supported. + */ +#if !defined(__ARM_NEON) || !defined(__ARM_NEON__) +#error "You must enable NEON instructions (e.g. -mfpu=neon) to use SSE2NEON." +#endif +#if !defined(__clang__) +#pragma GCC push_options +#pragma GCC target("fpu=neon") +#endif +#elif defined(__aarch64__) || defined(_M_ARM64) +#if !defined(__clang__) && !defined(_MSC_VER) +#pragma GCC push_options +#pragma GCC target("+simd") +#endif +#elif __ARM_ARCH == 8 +#if !defined(__ARM_NEON) || !defined(__ARM_NEON__) +#error \ + "You must enable NEON instructions (e.g. -mfpu=neon-fp-armv8) to use SSE2NEON." +#endif +#if !defined(__clang__) && !defined(_MSC_VER) +#pragma GCC push_options +#endif +#else +#error "Unsupported target. Must be either ARMv7-A+NEON or ARMv8-A." +#endif +#endif + +#include +#if (!defined(__aarch64__) && !defined(_M_ARM64)) && (__ARM_ARCH == 8) +#if defined __has_include && __has_include() +#include +#endif +#endif + +/* Apple Silicon cache lines are double of what is commonly used by Intel, AMD + * and other Arm microarchitectures use. + * From sysctl -a on Apple M1: + * hw.cachelinesize: 128 + */ +#if defined(__APPLE__) && (defined(__aarch64__) || defined(__arm64__)) +#define SSE2NEON_CACHELINE_SIZE 128 +#else +#define SSE2NEON_CACHELINE_SIZE 64 +#endif + +/* Rounding functions require either Aarch64 instructions or libm fallback */ +#if !defined(__aarch64__) && !defined(_M_ARM64) +#include +#endif + +/* On ARMv7, some registers, such as PMUSERENR and PMCCNTR, are read-only + * or even not accessible in user mode. + * To write or access to these registers in user mode, + * we have to perform syscall instead. + */ +#if (!defined(__aarch64__) && !defined(_M_ARM64)) +#include +#endif + +/* "__has_builtin" can be used to query support for built-in functions + * provided by gcc/clang and other compilers that support it. + */ +#ifndef __has_builtin /* GCC prior to 10 or non-clang compilers */ +/* Compatibility with gcc <= 9 */ +#if defined(__GNUC__) && (__GNUC__ <= 9) +#define __has_builtin(x) HAS##x +#define HAS__builtin_popcount 1 +#define HAS__builtin_popcountll 1 + +// __builtin_shuffle introduced in GCC 4.7.0 +#if (__GNUC__ >= 5) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) +#define HAS__builtin_shuffle 1 +#else +#define HAS__builtin_shuffle 0 +#endif + +#define HAS__builtin_shufflevector 0 +#define HAS__builtin_nontemporal_store 0 +#else +#define __has_builtin(x) 0 +#endif +#endif + +/** + * MACRO for shuffle parameter for _mm_shuffle_ps(). + * Argument fp3 is a digit[0123] that represents the fp from argument "b" + * of mm_shuffle_ps that will be placed in fp3 of result. fp2 is the same + * for fp2 in result. fp1 is a digit[0123] that represents the fp from + * argument "a" of mm_shuffle_ps that will be places in fp1 of result. + * fp0 is the same for fp0 of result. + */ +#define _MM_SHUFFLE(fp3, fp2, fp1, fp0) \ + (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0))) + +#if __has_builtin(__builtin_shufflevector) +#define _sse2neon_shuffle(type, a, b, ...) \ + __builtin_shufflevector(a, b, __VA_ARGS__) +#elif __has_builtin(__builtin_shuffle) +#define _sse2neon_shuffle(type, a, b, ...) \ + __extension__({ \ + type tmp = {__VA_ARGS__}; \ + __builtin_shuffle(a, b, tmp); \ + }) +#endif + +#ifdef _sse2neon_shuffle +#define vshuffle_s16(a, b, ...) _sse2neon_shuffle(int16x4_t, a, b, __VA_ARGS__) +#define vshuffleq_s16(a, b, ...) _sse2neon_shuffle(int16x8_t, a, b, __VA_ARGS__) +#define vshuffle_s32(a, b, ...) _sse2neon_shuffle(int32x2_t, a, b, __VA_ARGS__) +#define vshuffleq_s32(a, b, ...) _sse2neon_shuffle(int32x4_t, a, b, __VA_ARGS__) +#define vshuffle_s64(a, b, ...) _sse2neon_shuffle(int64x1_t, a, b, __VA_ARGS__) +#define vshuffleq_s64(a, b, ...) _sse2neon_shuffle(int64x2_t, a, b, __VA_ARGS__) +#endif + +/* Rounding mode macros. */ +#define _MM_FROUND_TO_NEAREST_INT 0x00 +#define _MM_FROUND_TO_NEG_INF 0x01 +#define _MM_FROUND_TO_POS_INF 0x02 +#define _MM_FROUND_TO_ZERO 0x03 +#define _MM_FROUND_CUR_DIRECTION 0x04 +#define _MM_FROUND_NO_EXC 0x08 +#define _MM_FROUND_RAISE_EXC 0x00 +#define _MM_FROUND_NINT (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_FLOOR (_MM_FROUND_TO_NEG_INF | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_CEIL (_MM_FROUND_TO_POS_INF | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_TRUNC (_MM_FROUND_TO_ZERO | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_RINT (_MM_FROUND_CUR_DIRECTION | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_NEARBYINT (_MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC) +#define _MM_ROUND_NEAREST 0x0000 +#define _MM_ROUND_DOWN 0x2000 +#define _MM_ROUND_UP 0x4000 +#define _MM_ROUND_TOWARD_ZERO 0x6000 +/* Flush zero mode macros. */ +#define _MM_FLUSH_ZERO_MASK 0x8000 +#define _MM_FLUSH_ZERO_ON 0x8000 +#define _MM_FLUSH_ZERO_OFF 0x0000 +/* Denormals are zeros mode macros. */ +#define _MM_DENORMALS_ZERO_MASK 0x0040 +#define _MM_DENORMALS_ZERO_ON 0x0040 +#define _MM_DENORMALS_ZERO_OFF 0x0000 + +/* indicate immediate constant argument in a given range */ +#define __constrange(a, b) const + +/* A few intrinsics accept traditional data types like ints or floats, but + * most operate on data types that are specific to SSE. + * If a vector type ends in d, it contains doubles, and if it does not have + * a suffix, it contains floats. An integer vector type can contain any type + * of integer, from chars to shorts to unsigned long longs. + */ +typedef int64x1_t __m64; +typedef float32x4_t __m128; /* 128-bit vector containing 4 floats */ +// On ARM 32-bit architecture, the float64x2_t is not supported. +// The data type __m128d should be represented in a different way for related +// intrinsic conversion. +#if defined(__aarch64__) || defined(_M_ARM64) +typedef float64x2_t __m128d; /* 128-bit vector containing 2 doubles */ +#else +typedef float32x4_t __m128d; +#endif +typedef int64x2_t __m128i; /* 128-bit vector containing integers */ + +// __int64 is defined in the Intrinsics Guide which maps to different datatype +// in different data model +#if !(defined(_WIN32) || defined(_WIN64) || defined(__int64)) +#if (defined(__x86_64__) || defined(__i386__)) +#define __int64 long long +#else +#define __int64 int64_t +#endif +#endif + +/* type-safe casting between types */ + +#define vreinterpretq_m128_f16(x) vreinterpretq_f32_f16(x) +#define vreinterpretq_m128_f32(x) (x) +#define vreinterpretq_m128_f64(x) vreinterpretq_f32_f64(x) + +#define vreinterpretq_m128_u8(x) vreinterpretq_f32_u8(x) +#define vreinterpretq_m128_u16(x) vreinterpretq_f32_u16(x) +#define vreinterpretq_m128_u32(x) vreinterpretq_f32_u32(x) +#define vreinterpretq_m128_u64(x) vreinterpretq_f32_u64(x) + +#define vreinterpretq_m128_s8(x) vreinterpretq_f32_s8(x) +#define vreinterpretq_m128_s16(x) vreinterpretq_f32_s16(x) +#define vreinterpretq_m128_s32(x) vreinterpretq_f32_s32(x) +#define vreinterpretq_m128_s64(x) vreinterpretq_f32_s64(x) + +#define vreinterpretq_f16_m128(x) vreinterpretq_f16_f32(x) +#define vreinterpretq_f32_m128(x) (x) +#define vreinterpretq_f64_m128(x) vreinterpretq_f64_f32(x) + +#define vreinterpretq_u8_m128(x) vreinterpretq_u8_f32(x) +#define vreinterpretq_u16_m128(x) vreinterpretq_u16_f32(x) +#define vreinterpretq_u32_m128(x) vreinterpretq_u32_f32(x) +#define vreinterpretq_u64_m128(x) vreinterpretq_u64_f32(x) + +#define vreinterpretq_s8_m128(x) vreinterpretq_s8_f32(x) +#define vreinterpretq_s16_m128(x) vreinterpretq_s16_f32(x) +#define vreinterpretq_s32_m128(x) vreinterpretq_s32_f32(x) +#define vreinterpretq_s64_m128(x) vreinterpretq_s64_f32(x) + +#define vreinterpretq_m128i_s8(x) vreinterpretq_s64_s8(x) +#define vreinterpretq_m128i_s16(x) vreinterpretq_s64_s16(x) +#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x) +#define vreinterpretq_m128i_s64(x) (x) + +#define vreinterpretq_m128i_u8(x) vreinterpretq_s64_u8(x) +#define vreinterpretq_m128i_u16(x) vreinterpretq_s64_u16(x) +#define vreinterpretq_m128i_u32(x) vreinterpretq_s64_u32(x) +#define vreinterpretq_m128i_u64(x) vreinterpretq_s64_u64(x) + +#define vreinterpretq_f32_m128i(x) vreinterpretq_f32_s64(x) +#define vreinterpretq_f64_m128i(x) vreinterpretq_f64_s64(x) + +#define vreinterpretq_s8_m128i(x) vreinterpretq_s8_s64(x) +#define vreinterpretq_s16_m128i(x) vreinterpretq_s16_s64(x) +#define vreinterpretq_s32_m128i(x) vreinterpretq_s32_s64(x) +#define vreinterpretq_s64_m128i(x) (x) + +#define vreinterpretq_u8_m128i(x) vreinterpretq_u8_s64(x) +#define vreinterpretq_u16_m128i(x) vreinterpretq_u16_s64(x) +#define vreinterpretq_u32_m128i(x) vreinterpretq_u32_s64(x) +#define vreinterpretq_u64_m128i(x) vreinterpretq_u64_s64(x) + +#define vreinterpret_m64_s8(x) vreinterpret_s64_s8(x) +#define vreinterpret_m64_s16(x) vreinterpret_s64_s16(x) +#define vreinterpret_m64_s32(x) vreinterpret_s64_s32(x) +#define vreinterpret_m64_s64(x) (x) + +#define vreinterpret_m64_u8(x) vreinterpret_s64_u8(x) +#define vreinterpret_m64_u16(x) vreinterpret_s64_u16(x) +#define vreinterpret_m64_u32(x) vreinterpret_s64_u32(x) +#define vreinterpret_m64_u64(x) vreinterpret_s64_u64(x) + +#define vreinterpret_m64_f16(x) vreinterpret_s64_f16(x) +#define vreinterpret_m64_f32(x) vreinterpret_s64_f32(x) +#define vreinterpret_m64_f64(x) vreinterpret_s64_f64(x) + +#define vreinterpret_u8_m64(x) vreinterpret_u8_s64(x) +#define vreinterpret_u16_m64(x) vreinterpret_u16_s64(x) +#define vreinterpret_u32_m64(x) vreinterpret_u32_s64(x) +#define vreinterpret_u64_m64(x) vreinterpret_u64_s64(x) + +#define vreinterpret_s8_m64(x) vreinterpret_s8_s64(x) +#define vreinterpret_s16_m64(x) vreinterpret_s16_s64(x) +#define vreinterpret_s32_m64(x) vreinterpret_s32_s64(x) +#define vreinterpret_s64_m64(x) (x) + +#define vreinterpret_f32_m64(x) vreinterpret_f32_s64(x) + +#if defined(__aarch64__) || defined(_M_ARM64) +#define vreinterpretq_m128d_s32(x) vreinterpretq_f64_s32(x) +#define vreinterpretq_m128d_s64(x) vreinterpretq_f64_s64(x) + +#define vreinterpretq_m128d_u64(x) vreinterpretq_f64_u64(x) + +#define vreinterpretq_m128d_f32(x) vreinterpretq_f64_f32(x) +#define vreinterpretq_m128d_f64(x) (x) + +#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f64(x) + +#define vreinterpretq_u32_m128d(x) vreinterpretq_u32_f64(x) +#define vreinterpretq_u64_m128d(x) vreinterpretq_u64_f64(x) + +#define vreinterpretq_f64_m128d(x) (x) +#define vreinterpretq_f32_m128d(x) vreinterpretq_f32_f64(x) +#else +#define vreinterpretq_m128d_s32(x) vreinterpretq_f32_s32(x) +#define vreinterpretq_m128d_s64(x) vreinterpretq_f32_s64(x) + +#define vreinterpretq_m128d_u32(x) vreinterpretq_f32_u32(x) +#define vreinterpretq_m128d_u64(x) vreinterpretq_f32_u64(x) + +#define vreinterpretq_m128d_f32(x) (x) + +#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f32(x) + +#define vreinterpretq_u32_m128d(x) vreinterpretq_u32_f32(x) +#define vreinterpretq_u64_m128d(x) vreinterpretq_u64_f32(x) + +#define vreinterpretq_f32_m128d(x) (x) +#endif + +// A struct is defined in this header file called 'SIMDVec' which can be used +// by applications which attempt to access the contents of an __m128 struct +// directly. It is important to note that accessing the __m128 struct directly +// is bad coding practice by Microsoft: @see: +// https://learn.microsoft.com/en-us/cpp/cpp/m128 +// +// However, some legacy source code may try to access the contents of an __m128 +// struct directly so the developer can use the SIMDVec as an alias for it. Any +// casting must be done manually by the developer, as you cannot cast or +// otherwise alias the base NEON data type for intrinsic operations. +// +// union intended to allow direct access to an __m128 variable using the names +// that the MSVC compiler provides. This union should really only be used when +// trying to access the members of the vector as integer values. GCC/clang +// allow native access to the float members through a simple array access +// operator (in C since 4.6, in C++ since 4.8). +// +// Ideally direct accesses to SIMD vectors should not be used since it can cause +// a performance hit. If it really is needed however, the original __m128 +// variable can be aliased with a pointer to this union and used to access +// individual components. The use of this union should be hidden behind a macro +// that is used throughout the codebase to access the members instead of always +// declaring this type of variable. +typedef union ALIGN_STRUCT(16) SIMDVec { + float m128_f32[4]; // as floats - DON'T USE. Added for convenience. + int8_t m128_i8[16]; // as signed 8-bit integers. + int16_t m128_i16[8]; // as signed 16-bit integers. + int32_t m128_i32[4]; // as signed 32-bit integers. + int64_t m128_i64[2]; // as signed 64-bit integers. + uint8_t m128_u8[16]; // as unsigned 8-bit integers. + uint16_t m128_u16[8]; // as unsigned 16-bit integers. + uint32_t m128_u32[4]; // as unsigned 32-bit integers. + uint64_t m128_u64[2]; // as unsigned 64-bit integers. +} SIMDVec; + +// casting using SIMDVec +#define vreinterpretq_nth_u64_m128i(x, n) (((SIMDVec *) &x)->m128_u64[n]) +#define vreinterpretq_nth_u32_m128i(x, n) (((SIMDVec *) &x)->m128_u32[n]) +#define vreinterpretq_nth_u8_m128i(x, n) (((SIMDVec *) &x)->m128_u8[n]) + +/* SSE macros */ +#define _MM_GET_FLUSH_ZERO_MODE _sse2neon_mm_get_flush_zero_mode +#define _MM_SET_FLUSH_ZERO_MODE _sse2neon_mm_set_flush_zero_mode +#define _MM_GET_DENORMALS_ZERO_MODE _sse2neon_mm_get_denormals_zero_mode +#define _MM_SET_DENORMALS_ZERO_MODE _sse2neon_mm_set_denormals_zero_mode + +// Function declaration +// SSE +FORCE_INLINE unsigned int _MM_GET_ROUNDING_MODE(void); +FORCE_INLINE __m128 _mm_move_ss(__m128, __m128); +FORCE_INLINE __m128 _mm_or_ps(__m128, __m128); +FORCE_INLINE __m128 _mm_set_ps1(float); +FORCE_INLINE __m128 _mm_setzero_ps(void); +// SSE2 +FORCE_INLINE __m128i _mm_and_si128(__m128i, __m128i); +FORCE_INLINE __m128i _mm_castps_si128(__m128); +FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i, __m128i); +FORCE_INLINE __m128i _mm_cvtps_epi32(__m128); +FORCE_INLINE __m128d _mm_move_sd(__m128d, __m128d); +FORCE_INLINE __m128i _mm_or_si128(__m128i, __m128i); +FORCE_INLINE __m128i _mm_set_epi32(int, int, int, int); +FORCE_INLINE __m128i _mm_set_epi64x(int64_t, int64_t); +FORCE_INLINE __m128d _mm_set_pd(double, double); +FORCE_INLINE __m128i _mm_set1_epi32(int); +FORCE_INLINE __m128i _mm_setzero_si128(void); +// SSE4.1 +FORCE_INLINE __m128d _mm_ceil_pd(__m128d); +FORCE_INLINE __m128 _mm_ceil_ps(__m128); +FORCE_INLINE __m128d _mm_floor_pd(__m128d); +FORCE_INLINE __m128 _mm_floor_ps(__m128); +FORCE_INLINE __m128d _mm_round_pd(__m128d, int); +FORCE_INLINE __m128 _mm_round_ps(__m128, int); +// SSE4.2 +FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t, uint8_t); + +/* Backwards compatibility for compilers with lack of specific type support */ + +// Older gcc does not define vld1q_u8_x4 type +#if defined(__GNUC__) && !defined(__clang__) && \ + ((__GNUC__ <= 13 && defined(__arm__)) || \ + (__GNUC__ == 10 && __GNUC_MINOR__ < 3 && defined(__aarch64__)) || \ + (__GNUC__ <= 9 && defined(__aarch64__))) +FORCE_INLINE uint8x16x4_t _sse2neon_vld1q_u8_x4(const uint8_t *p) +{ + uint8x16x4_t ret; + ret.val[0] = vld1q_u8(p + 0); + ret.val[1] = vld1q_u8(p + 16); + ret.val[2] = vld1q_u8(p + 32); + ret.val[3] = vld1q_u8(p + 48); + return ret; +} +#else +// Wraps vld1q_u8_x4 +FORCE_INLINE uint8x16x4_t _sse2neon_vld1q_u8_x4(const uint8_t *p) +{ + return vld1q_u8_x4(p); +} +#endif + +#if !defined(__aarch64__) && !defined(_M_ARM64) +/* emulate vaddv u8 variant */ +FORCE_INLINE uint8_t _sse2neon_vaddv_u8(uint8x8_t v8) +{ + const uint64x1_t v1 = vpaddl_u32(vpaddl_u16(vpaddl_u8(v8))); + return vget_lane_u8(vreinterpret_u8_u64(v1), 0); +} +#else +// Wraps vaddv_u8 +FORCE_INLINE uint8_t _sse2neon_vaddv_u8(uint8x8_t v8) +{ + return vaddv_u8(v8); +} +#endif + +#if !defined(__aarch64__) && !defined(_M_ARM64) +/* emulate vaddvq u8 variant */ +FORCE_INLINE uint8_t _sse2neon_vaddvq_u8(uint8x16_t a) +{ + uint8x8_t tmp = vpadd_u8(vget_low_u8(a), vget_high_u8(a)); + uint8_t res = 0; + for (int i = 0; i < 8; ++i) + res += tmp[i]; + return res; +} +#else +// Wraps vaddvq_u8 +FORCE_INLINE uint8_t _sse2neon_vaddvq_u8(uint8x16_t a) +{ + return vaddvq_u8(a); +} +#endif + +#if !defined(__aarch64__) && !defined(_M_ARM64) +/* emulate vaddvq u16 variant */ +FORCE_INLINE uint16_t _sse2neon_vaddvq_u16(uint16x8_t a) +{ + uint32x4_t m = vpaddlq_u16(a); + uint64x2_t n = vpaddlq_u32(m); + uint64x1_t o = vget_low_u64(n) + vget_high_u64(n); + + return vget_lane_u32((uint32x2_t) o, 0); +} +#else +// Wraps vaddvq_u16 +FORCE_INLINE uint16_t _sse2neon_vaddvq_u16(uint16x8_t a) +{ + return vaddvq_u16(a); +} +#endif + +/* Function Naming Conventions + * The naming convention of SSE intrinsics is straightforward. A generic SSE + * intrinsic function is given as follows: + * _mm__ + * + * The parts of this format are given as follows: + * 1. describes the operation performed by the intrinsic + * 2. identifies the data type of the function's primary arguments + * + * This last part, , is a little complicated. It identifies the + * content of the input values, and can be set to any of the following values: + * + ps - vectors contain floats (ps stands for packed single-precision) + * + pd - vectors contain doubles (pd stands for packed double-precision) + * + epi8/epi16/epi32/epi64 - vectors contain 8-bit/16-bit/32-bit/64-bit + * signed integers + * + epu8/epu16/epu32/epu64 - vectors contain 8-bit/16-bit/32-bit/64-bit + * unsigned integers + * + si128 - unspecified 128-bit vector or 256-bit vector + * + m128/m128i/m128d - identifies input vector types when they are different + * than the type of the returned vector + * + * For example, _mm_setzero_ps. The _mm implies that the function returns + * a 128-bit vector. The _ps at the end implies that the argument vectors + * contain floats. + * + * A complete example: Byte Shuffle - pshufb (_mm_shuffle_epi8) + * // Set packed 16-bit integers. 128 bits, 8 short, per 16 bits + * __m128i v_in = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); + * // Set packed 8-bit integers + * // 128 bits, 16 chars, per 8 bits + * __m128i v_perm = _mm_setr_epi8(1, 0, 2, 3, 8, 9, 10, 11, + * 4, 5, 12, 13, 6, 7, 14, 15); + * // Shuffle packed 8-bit integers + * __m128i v_out = _mm_shuffle_epi8(v_in, v_perm); // pshufb + */ + +/* Constants for use with _mm_prefetch. */ +enum _mm_hint { + _MM_HINT_NTA = 0, /* load data to L1 and L2 cache, mark it as NTA */ + _MM_HINT_T0 = 1, /* load data to L1 and L2 cache */ + _MM_HINT_T1 = 2, /* load data to L2 cache only */ + _MM_HINT_T2 = 3, /* load data to L2 cache only, mark it as NTA */ +}; + +// The bit field mapping to the FPCR(floating-point control register) +typedef struct { + uint16_t res0; + uint8_t res1 : 6; + uint8_t bit22 : 1; + uint8_t bit23 : 1; + uint8_t bit24 : 1; + uint8_t res2 : 7; +#if defined(__aarch64__) || defined(_M_ARM64) + uint32_t res3; +#endif +} fpcr_bitfield; + +// Takes the upper 64 bits of a and places it in the low end of the result +// Takes the lower 64 bits of b and places it into the high end of the result. +FORCE_INLINE __m128 _mm_shuffle_ps_1032(__m128 a, __m128 b) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a32, b10)); +} + +// takes the lower two 32-bit values from a and swaps them and places in high +// end of result takes the higher two 32 bit values from b and swaps them and +// places in low end of result. +FORCE_INLINE __m128 _mm_shuffle_ps_2301(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b23 = vrev64_f32(vget_high_f32(vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vcombine_f32(a01, b23)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0321(__m128 a, __m128 b) +{ + float32x2_t a21 = vget_high_f32( + vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); + float32x2_t b03 = vget_low_f32( + vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); + return vreinterpretq_m128_f32(vcombine_f32(a21, b03)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2103(__m128 a, __m128 b) +{ + float32x2_t a03 = vget_low_f32( + vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); + float32x2_t b21 = vget_high_f32( + vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); + return vreinterpretq_m128_f32(vcombine_f32(a03, b21)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1010(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1001(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a01, b10)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0101(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vcombine_f32(a01, b01)); +} + +// keeps the low 64 bits of b in the low and puts the high 64 bits of a in the +// high +FORCE_INLINE __m128 _mm_shuffle_ps_3210(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b32)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0011(__m128 a, __m128 b) +{ + float32x2_t a11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 1); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a11, b00)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0022(__m128 a, __m128 b) +{ + float32x2_t a22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a22, b00)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2200(__m128 a, __m128 b) +{ + float32x2_t a00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t b22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a00, b22)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_3202(__m128 a, __m128 b) +{ + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32x2_t a22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t a02 = vset_lane_f32(a0, a22, 1); /* TODO: use vzip ?*/ + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a02, b32)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1133(__m128 a, __m128 b) +{ + float32x2_t a33 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 1); + float32x2_t b11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 1); + return vreinterpretq_m128_f32(vcombine_f32(a33, b11)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2010(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32_t b2 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a10, b20)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2001(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32_t b2 = vgetq_lane_f32(b, 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a01, b20)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2032(__m128 a, __m128 b) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32_t b2 = vgetq_lane_f32(b, 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a32, b20)); +} + +// For MSVC, we check only if it is ARM64, as every single ARM64 processor +// supported by WoA has crypto extensions. If this changes in the future, +// this can be verified via the runtime-only method of: +// IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) +#if (defined(_M_ARM64) && !defined(__clang__)) || \ + (defined(__ARM_FEATURE_CRYPTO) && \ + (defined(__aarch64__) || __has_builtin(__builtin_arm_crypto_vmullp64))) +// Wraps vmull_p64 +FORCE_INLINE uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) +{ + poly64_t a = vget_lane_p64(vreinterpret_p64_u64(_a), 0); + poly64_t b = vget_lane_p64(vreinterpret_p64_u64(_b), 0); +#if defined(_MSC_VER) + __n64 a1 = {a}, b1 = {b}; + return vreinterpretq_u64_p128(vmull_p64(a1, b1)); +#else + return vreinterpretq_u64_p128(vmull_p64(a, b)); +#endif +} +#else // ARMv7 polyfill +// ARMv7/some A64 lacks vmull_p64, but it has vmull_p8. +// +// vmull_p8 calculates 8 8-bit->16-bit polynomial multiplies, but we need a +// 64-bit->128-bit polynomial multiply. +// +// It needs some work and is somewhat slow, but it is still faster than all +// known scalar methods. +// +// Algorithm adapted to C from +// https://www.workofard.com/2017/07/ghash-for-low-end-cores/, which is adapted +// from "Fast Software Polynomial Multiplication on ARM Processors Using the +// NEON Engine" by Danilo Camara, Conrado Gouvea, Julio Lopez and Ricardo Dahab +// (https://hal.inria.fr/hal-01506572) +static uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) +{ + poly8x8_t a = vreinterpret_p8_u64(_a); + poly8x8_t b = vreinterpret_p8_u64(_b); + + // Masks + uint8x16_t k48_32 = vcombine_u8(vcreate_u8(0x0000ffffffffffff), + vcreate_u8(0x00000000ffffffff)); + uint8x16_t k16_00 = vcombine_u8(vcreate_u8(0x000000000000ffff), + vcreate_u8(0x0000000000000000)); + + // Do the multiplies, rotating with vext to get all combinations + uint8x16_t d = vreinterpretq_u8_p16(vmull_p8(a, b)); // D = A0 * B0 + uint8x16_t e = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 1))); // E = A0 * B1 + uint8x16_t f = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 1), b)); // F = A1 * B0 + uint8x16_t g = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 2))); // G = A0 * B2 + uint8x16_t h = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 2), b)); // H = A2 * B0 + uint8x16_t i = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 3))); // I = A0 * B3 + uint8x16_t j = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 3), b)); // J = A3 * B0 + uint8x16_t k = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 4))); // L = A0 * B4 + + // Add cross products + uint8x16_t l = veorq_u8(e, f); // L = E + F + uint8x16_t m = veorq_u8(g, h); // M = G + H + uint8x16_t n = veorq_u8(i, j); // N = I + J + + // Interleave. Using vzip1 and vzip2 prevents Clang from emitting TBL + // instructions. +#if defined(__aarch64__) + uint8x16_t lm_p0 = vreinterpretq_u8_u64( + vzip1q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); + uint8x16_t lm_p1 = vreinterpretq_u8_u64( + vzip2q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); + uint8x16_t nk_p0 = vreinterpretq_u8_u64( + vzip1q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); + uint8x16_t nk_p1 = vreinterpretq_u8_u64( + vzip2q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); +#else + uint8x16_t lm_p0 = vcombine_u8(vget_low_u8(l), vget_low_u8(m)); + uint8x16_t lm_p1 = vcombine_u8(vget_high_u8(l), vget_high_u8(m)); + uint8x16_t nk_p0 = vcombine_u8(vget_low_u8(n), vget_low_u8(k)); + uint8x16_t nk_p1 = vcombine_u8(vget_high_u8(n), vget_high_u8(k)); +#endif + // t0 = (L) (P0 + P1) << 8 + // t1 = (M) (P2 + P3) << 16 + uint8x16_t t0t1_tmp = veorq_u8(lm_p0, lm_p1); + uint8x16_t t0t1_h = vandq_u8(lm_p1, k48_32); + uint8x16_t t0t1_l = veorq_u8(t0t1_tmp, t0t1_h); + + // t2 = (N) (P4 + P5) << 24 + // t3 = (K) (P6 + P7) << 32 + uint8x16_t t2t3_tmp = veorq_u8(nk_p0, nk_p1); + uint8x16_t t2t3_h = vandq_u8(nk_p1, k16_00); + uint8x16_t t2t3_l = veorq_u8(t2t3_tmp, t2t3_h); + + // De-interleave +#if defined(__aarch64__) + uint8x16_t t0 = vreinterpretq_u8_u64( + vuzp1q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); + uint8x16_t t1 = vreinterpretq_u8_u64( + vuzp2q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); + uint8x16_t t2 = vreinterpretq_u8_u64( + vuzp1q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); + uint8x16_t t3 = vreinterpretq_u8_u64( + vuzp2q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); +#else + uint8x16_t t1 = vcombine_u8(vget_high_u8(t0t1_l), vget_high_u8(t0t1_h)); + uint8x16_t t0 = vcombine_u8(vget_low_u8(t0t1_l), vget_low_u8(t0t1_h)); + uint8x16_t t3 = vcombine_u8(vget_high_u8(t2t3_l), vget_high_u8(t2t3_h)); + uint8x16_t t2 = vcombine_u8(vget_low_u8(t2t3_l), vget_low_u8(t2t3_h)); +#endif + // Shift the cross products + uint8x16_t t0_shift = vextq_u8(t0, t0, 15); // t0 << 8 + uint8x16_t t1_shift = vextq_u8(t1, t1, 14); // t1 << 16 + uint8x16_t t2_shift = vextq_u8(t2, t2, 13); // t2 << 24 + uint8x16_t t3_shift = vextq_u8(t3, t3, 12); // t3 << 32 + + // Accumulate the products + uint8x16_t cross1 = veorq_u8(t0_shift, t1_shift); + uint8x16_t cross2 = veorq_u8(t2_shift, t3_shift); + uint8x16_t mix = veorq_u8(d, cross1); + uint8x16_t r = veorq_u8(mix, cross2); + return vreinterpretq_u64_u8(r); +} +#endif // ARMv7 polyfill + +// C equivalent: +// __m128i _mm_shuffle_epi32_default(__m128i a, +// __constrange(0, 255) int imm) { +// __m128i ret; +// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; +// ret[2] = a[(imm >> 4) & 0x03]; ret[3] = a[(imm >> 6) & 0x03]; +// return ret; +// } +#define _mm_shuffle_epi32_default(a, imm) \ + vreinterpretq_m128i_s32(vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 6) & 0x3), \ + vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 4) & 0x3), \ + vsetq_lane_s32(vgetq_lane_s32(vreinterpretq_s32_m128i(a), \ + ((imm) >> 2) & 0x3), \ + vmovq_n_s32(vgetq_lane_s32( \ + vreinterpretq_s32_m128i(a), (imm) & (0x3))), \ + 1), \ + 2), \ + 3)) + +// Takes the upper 64 bits of a and places it in the low end of the result +// Takes the lower 64 bits of a and places it into the high end of the result. +FORCE_INLINE __m128i _mm_shuffle_epi_1032(__m128i a) +{ + int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a32, a10)); +} + +// takes the lower two 32-bit values from a and swaps them and places in low end +// of result takes the higher two 32 bit values from a and swaps them and places +// in high end of result. +FORCE_INLINE __m128i _mm_shuffle_epi_2301(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + int32x2_t a23 = vrev64_s32(vget_high_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a23)); +} + +// rotates the least significant 32 bits into the most significant 32 bits, and +// shifts the rest down +FORCE_INLINE __m128i _mm_shuffle_epi_0321(__m128i a) +{ + return vreinterpretq_m128i_s32( + vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 1)); +} + +// rotates the most significant 32 bits into the least significant 32 bits, and +// shifts the rest up +FORCE_INLINE __m128i _mm_shuffle_epi_2103(__m128i a) +{ + return vreinterpretq_m128i_s32( + vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 3)); +} + +// gets the lower 64 bits of a, and places it in the upper 64 bits +// gets the lower 64 bits of a and places it in the lower 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_1010(__m128i a) +{ + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a10, a10)); +} + +// gets the lower 64 bits of a, swaps the 0 and 1 elements, and places it in the +// lower 64 bits gets the lower 64 bits of a, and places it in the upper 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_1001(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a10)); +} + +// gets the lower 64 bits of a, swaps the 0 and 1 elements and places it in the +// upper 64 bits gets the lower 64 bits of a, swaps the 0 and 1 elements, and +// places it in the lower 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_0101(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a01)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_2211(__m128i a) +{ + int32x2_t a11 = vdup_lane_s32(vget_low_s32(vreinterpretq_s32_m128i(a)), 1); + int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); + return vreinterpretq_m128i_s32(vcombine_s32(a11, a22)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_0122(__m128i a) +{ + int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a22, a01)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_3332(__m128i a) +{ + int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t a33 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 1); + return vreinterpretq_m128i_s32(vcombine_s32(a32, a33)); +} + +#if defined(__aarch64__) || defined(_M_ARM64) +#define _mm_shuffle_epi32_splat(a, imm) \ + vreinterpretq_m128i_s32(vdupq_laneq_s32(vreinterpretq_s32_m128i(a), (imm))) +#else +#define _mm_shuffle_epi32_splat(a, imm) \ + vreinterpretq_m128i_s32( \ + vdupq_n_s32(vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)))) +#endif + +// NEON does not support a general purpose permute intrinsic. +// Shuffle single-precision (32-bit) floating-point elements in a using the +// control in imm8, and store the results in dst. +// +// C equivalent: +// __m128 _mm_shuffle_ps_default(__m128 a, __m128 b, +// __constrange(0, 255) int imm) { +// __m128 ret; +// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; +// ret[2] = b[(imm >> 4) & 0x03]; ret[3] = b[(imm >> 6) & 0x03]; +// return ret; +// } +// +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_ps +#define _mm_shuffle_ps_default(a, b, imm) \ + vreinterpretq_m128_f32(vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 6) & 0x3), \ + vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 4) & 0x3), \ + vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(a), ((imm) >> 2) & 0x3), \ + vmovq_n_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(a), (imm) & (0x3))), \ + 1), \ + 2), \ + 3)) + +// Shuffle 16-bit integers in the low 64 bits of a using the control in imm8. +// Store the results in the low 64 bits of dst, with the high 64 bits being +// copied from a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shufflelo_epi16 +#define _mm_shufflelo_epi16_function(a, imm) \ + _sse2neon_define1( \ + __m128i, a, int16x8_t ret = vreinterpretq_s16_m128i(_a); \ + int16x4_t lowBits = vget_low_s16(ret); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, (imm) & (0x3)), ret, 0); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 2) & 0x3), ret, \ + 1); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 4) & 0x3), ret, \ + 2); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 6) & 0x3), ret, \ + 3); \ + _sse2neon_return(vreinterpretq_m128i_s16(ret));) + +// Shuffle 16-bit integers in the high 64 bits of a using the control in imm8. +// Store the results in the high 64 bits of dst, with the low 64 bits being +// copied from a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shufflehi_epi16 +#define _mm_shufflehi_epi16_function(a, imm) \ + _sse2neon_define1( \ + __m128i, a, int16x8_t ret = vreinterpretq_s16_m128i(_a); \ + int16x4_t highBits = vget_high_s16(ret); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, (imm) & (0x3)), ret, 4); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 2) & 0x3), ret, \ + 5); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 4) & 0x3), ret, \ + 6); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 6) & 0x3), ret, \ + 7); \ + _sse2neon_return(vreinterpretq_m128i_s16(ret));) + +/* MMX */ + +//_mm_empty is a no-op on arm +FORCE_INLINE void _mm_empty(void) {} + +/* SSE */ + +// Add packed single-precision (32-bit) floating-point elements in a and b, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_ps +FORCE_INLINE __m128 _mm_add_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Add the lower single-precision (32-bit) floating-point element in a and b, +// store the result in the lower element of dst, and copy the upper 3 packed +// elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_ss +FORCE_INLINE __m128 _mm_add_ss(__m128 a, __m128 b) +{ + float32_t b0 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); + float32x4_t value = vsetq_lane_f32(b0, vdupq_n_f32(0), 0); + // the upper values in the result must be the remnants of . + return vreinterpretq_m128_f32(vaddq_f32(a, value)); +} + +// Compute the bitwise AND of packed single-precision (32-bit) floating-point +// elements in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_and_ps +FORCE_INLINE __m128 _mm_and_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vandq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Compute the bitwise NOT of packed single-precision (32-bit) floating-point +// elements in a and then AND with b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_andnot_ps +FORCE_INLINE __m128 _mm_andnot_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vbicq_s32(vreinterpretq_s32_m128(b), + vreinterpretq_s32_m128(a))); // *NOTE* argument swap +} + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_pu16 +FORCE_INLINE __m64 _mm_avg_pu16(__m64 a, __m64 b) +{ + return vreinterpret_m64_u16( + vrhadd_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b))); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_pu8 +FORCE_INLINE __m64 _mm_avg_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vrhadd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for equality, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_ps +FORCE_INLINE __m128 _mm_cmpeq_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for equality, store the result in the lower element of dst, and copy the +// upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_ss +FORCE_INLINE __m128 _mm_cmpeq_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpeq_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for greater-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_ps +FORCE_INLINE __m128 _mm_cmpge_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for greater-than-or-equal, store the result in the lower element of dst, +// and copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_ss +FORCE_INLINE __m128 _mm_cmpge_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpge_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for greater-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_ps +FORCE_INLINE __m128 _mm_cmpgt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for greater-than, store the result in the lower element of dst, and copy +// the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_ss +FORCE_INLINE __m128 _mm_cmpgt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpgt_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for less-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_ps +FORCE_INLINE __m128 _mm_cmple_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for less-than-or-equal, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_ss +FORCE_INLINE __m128 _mm_cmple_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmple_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for less-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_ps +FORCE_INLINE __m128 _mm_cmplt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for less-than, store the result in the lower element of dst, and copy the +// upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_ss +FORCE_INLINE __m128 _mm_cmplt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmplt_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for not-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_ps +FORCE_INLINE __m128 _mm_cmpneq_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for not-equal, store the result in the lower element of dst, and copy the +// upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_ss +FORCE_INLINE __m128 _mm_cmpneq_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpneq_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for not-greater-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_ps +FORCE_INLINE __m128 _mm_cmpnge_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for not-greater-than-or-equal, store the result in the lower element of +// dst, and copy the upper 3 packed elements from a to the upper elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_ss +FORCE_INLINE __m128 _mm_cmpnge_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpnge_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for not-greater-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpngt_ps +FORCE_INLINE __m128 _mm_cmpngt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for not-greater-than, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpngt_ss +FORCE_INLINE __m128 _mm_cmpngt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpngt_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for not-less-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_ps +FORCE_INLINE __m128 _mm_cmpnle_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for not-less-than-or-equal, store the result in the lower element of dst, +// and copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_ss +FORCE_INLINE __m128 _mm_cmpnle_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpnle_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// for not-less-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_ps +FORCE_INLINE __m128 _mm_cmpnlt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b for not-less-than, store the result in the lower element of dst, and copy +// the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_ss +FORCE_INLINE __m128 _mm_cmpnlt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpnlt_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// to see if neither is NaN, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_ps +// +// See also: +// http://stackoverflow.com/questions/8627331/what-does-ordered-unordered-comparison-mean +// http://stackoverflow.com/questions/29349621/neon-isnanval-intrinsics +FORCE_INLINE __m128 _mm_cmpord_ps(__m128 a, __m128 b) +{ + // Note: NEON does not have ordered compare builtin + // Need to compare a eq a and b eq b to check for NaN + // Do AND of results to get final + uint32x4_t ceqaa = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t ceqbb = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_u32(vandq_u32(ceqaa, ceqbb)); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b to see if neither is NaN, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_ss +FORCE_INLINE __m128 _mm_cmpord_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpord_ps(a, b)); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b +// to see if either is NaN, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_ps +FORCE_INLINE __m128 _mm_cmpunord_ps(__m128 a, __m128 b) +{ + uint32x4_t f32a = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t f32b = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_u32(vmvnq_u32(vandq_u32(f32a, f32b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b to see if either is NaN, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_ss +FORCE_INLINE __m128 _mm_cmpunord_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpunord_ps(a, b)); +} + +// Compare the lower single-precision (32-bit) floating-point element in a and b +// for equality, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comieq_ss +FORCE_INLINE int _mm_comieq_ss(__m128 a, __m128 b) +{ + uint32x4_t a_eq_b = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_eq_b, 0) & 0x1; +} + +// Compare the lower single-precision (32-bit) floating-point element in a and b +// for greater-than-or-equal, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comige_ss +FORCE_INLINE int _mm_comige_ss(__m128 a, __m128 b) +{ + uint32x4_t a_ge_b = + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_ge_b, 0) & 0x1; +} + +// Compare the lower single-precision (32-bit) floating-point element in a and b +// for greater-than, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comigt_ss +FORCE_INLINE int _mm_comigt_ss(__m128 a, __m128 b) +{ + uint32x4_t a_gt_b = + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_gt_b, 0) & 0x1; +} + +// Compare the lower single-precision (32-bit) floating-point element in a and b +// for less-than-or-equal, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comile_ss +FORCE_INLINE int _mm_comile_ss(__m128 a, __m128 b) +{ + uint32x4_t a_le_b = + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_le_b, 0) & 0x1; +} + +// Compare the lower single-precision (32-bit) floating-point element in a and b +// for less-than, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comilt_ss +FORCE_INLINE int _mm_comilt_ss(__m128 a, __m128 b) +{ + uint32x4_t a_lt_b = + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_lt_b, 0) & 0x1; +} + +// Compare the lower single-precision (32-bit) floating-point element in a and b +// for not-equal, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comineq_ss +FORCE_INLINE int _mm_comineq_ss(__m128 a, __m128 b) +{ + return !_mm_comieq_ss(a, b); +} + +// Convert packed signed 32-bit integers in b to packed single-precision +// (32-bit) floating-point elements, store the results in the lower 2 elements +// of dst, and copy the upper 2 packed elements from a to the upper elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_pi2ps +FORCE_INLINE __m128 _mm_cvt_pi2ps(__m128 a, __m64 b) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), + vget_high_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_ps2pi +FORCE_INLINE __m64 _mm_cvt_ps2pi(__m128 a) +{ +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + return vreinterpret_m64_s32( + vget_low_s32(vcvtnq_s32_f32(vrndiq_f32(vreinterpretq_f32_m128(a))))); +#else + return vreinterpret_m64_s32(vcvt_s32_f32(vget_low_f32( + vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION))))); +#endif +} + +// Convert the signed 32-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_si2ss +FORCE_INLINE __m128 _mm_cvt_si2ss(__m128 a, int b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_ss2si +FORCE_INLINE int _mm_cvt_ss2si(__m128 a) +{ +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + return vgetq_lane_s32(vcvtnq_s32_f32(vrndiq_f32(vreinterpretq_f32_m128(a))), + 0); +#else + float32_t data = vgetq_lane_f32( + vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION)), 0); + return (int32_t) data; +#endif +} + +// Convert packed 16-bit integers in a to packed single-precision (32-bit) +// floating-point elements, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi16_ps +FORCE_INLINE __m128 _mm_cvtpi16_ps(__m64 a) +{ + return vreinterpretq_m128_f32( + vcvtq_f32_s32(vmovl_s16(vreinterpret_s16_m64(a)))); +} + +// Convert packed 32-bit integers in b to packed single-precision (32-bit) +// floating-point elements, store the results in the lower 2 elements of dst, +// and copy the upper 2 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi32_ps +FORCE_INLINE __m128 _mm_cvtpi32_ps(__m128 a, __m64 b) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), + vget_high_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert packed signed 32-bit integers in a to packed single-precision +// (32-bit) floating-point elements, store the results in the lower 2 elements +// of dst, then convert the packed signed 32-bit integers in b to +// single-precision (32-bit) floating-point element, and store the results in +// the upper 2 elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi32x2_ps +FORCE_INLINE __m128 _mm_cvtpi32x2_ps(__m64 a, __m64 b) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32( + vcombine_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b)))); +} + +// Convert the lower packed 8-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi8_ps +FORCE_INLINE __m128 _mm_cvtpi8_ps(__m64 a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32( + vmovl_s16(vget_low_s16(vmovl_s8(vreinterpret_s8_m64(a)))))); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 16-bit integers, and store the results in dst. Note: this intrinsic +// will generate 0x7FFF, rather than 0x8000, for input values between 0x7FFF and +// 0x7FFFFFFF. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pi16 +FORCE_INLINE __m64 _mm_cvtps_pi16(__m128 a) +{ + return vreinterpret_m64_s16( + vqmovn_s32(vreinterpretq_s32_m128i(_mm_cvtps_epi32(a)))); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pi32 +#define _mm_cvtps_pi32(a) _mm_cvt_ps2pi(a) + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 8-bit integers, and store the results in lower 4 elements of dst. +// Note: this intrinsic will generate 0x7F, rather than 0x80, for input values +// between 0x7F and 0x7FFFFFFF. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pi8 +FORCE_INLINE __m64 _mm_cvtps_pi8(__m128 a) +{ + return vreinterpret_m64_s8(vqmovn_s16( + vcombine_s16(vreinterpret_s16_m64(_mm_cvtps_pi16(a)), vdup_n_s16(0)))); +} + +// Convert packed unsigned 16-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpu16_ps +FORCE_INLINE __m128 _mm_cvtpu16_ps(__m64 a) +{ + return vreinterpretq_m128_f32( + vcvtq_f32_u32(vmovl_u16(vreinterpret_u16_m64(a)))); +} + +// Convert the lower packed unsigned 8-bit integers in a to packed +// single-precision (32-bit) floating-point elements, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpu8_ps +FORCE_INLINE __m128 _mm_cvtpu8_ps(__m64 a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_u32( + vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_m64(a)))))); +} + +// Convert the signed 32-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi32_ss +#define _mm_cvtsi32_ss(a, b) _mm_cvt_si2ss(a, b) + +// Convert the signed 64-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64_ss +FORCE_INLINE __m128 _mm_cvtsi64_ss(__m128 a, int64_t b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); +} + +// Copy the lower single-precision (32-bit) floating-point element of a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_f32 +FORCE_INLINE float _mm_cvtss_f32(__m128 a) +{ + return vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_si32 +#define _mm_cvtss_si32(a) _mm_cvt_ss2si(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_si64 +FORCE_INLINE int64_t _mm_cvtss_si64(__m128 a) +{ +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + return (int64_t) vgetq_lane_f32(vrndiq_f32(vreinterpretq_f32_m128(a)), 0); +#else + float32_t data = vgetq_lane_f32( + vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION)), 0); + return (int64_t) data; +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtt_ps2pi +FORCE_INLINE __m64 _mm_cvtt_ps2pi(__m128 a) +{ + return vreinterpret_m64_s32( + vget_low_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtt_ss2si +FORCE_INLINE int _mm_cvtt_ss2si(__m128 a) +{ + return vgetq_lane_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)), 0); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttps_pi32 +#define _mm_cvttps_pi32(a) _mm_cvtt_ps2pi(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttss_si32 +#define _mm_cvttss_si32(a) _mm_cvtt_ss2si(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttss_si64 +FORCE_INLINE int64_t _mm_cvttss_si64(__m128 a) +{ + return (int64_t) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); +} + +// Divide packed single-precision (32-bit) floating-point elements in a by +// packed elements in b, and store the results in dst. +// Due to ARMv7-A NEON's lack of a precise division intrinsic, we implement +// division by multiplying a by b's reciprocal before using the Newton-Raphson +// method to approximate the results. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_ps +FORCE_INLINE __m128 _mm_div_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vdivq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(b)); + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(b))); + // Additional Netwon-Raphson iteration for accuracy + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(a), recip)); +#endif +} + +// Divide the lower single-precision (32-bit) floating-point element in a by the +// lower single-precision (32-bit) floating-point element in b, store the result +// in the lower element of dst, and copy the upper 3 packed elements from a to +// the upper elements of dst. +// Warning: ARMv7-A does not produce the same result compared to Intel and not +// IEEE-compliant. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_ss +FORCE_INLINE __m128 _mm_div_ss(__m128 a, __m128 b) +{ + float32_t value = + vgetq_lane_f32(vreinterpretq_f32_m128(_mm_div_ps(a, b)), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Extract a 16-bit integer from a, selected with imm8, and store the result in +// the lower element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_pi16 +#define _mm_extract_pi16(a, imm) \ + (int32_t) vget_lane_u16(vreinterpret_u16_m64(a), (imm)) + +// Free aligned memory that was allocated with _mm_malloc. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_free +#if !defined(SSE2NEON_ALLOC_DEFINED) +FORCE_INLINE void _mm_free(void *addr) +{ + free(addr); +} +#endif + +FORCE_INLINE uint64_t _sse2neon_get_fpcr(void) +{ + uint64_t value; +#if defined(_MSC_VER) + value = _ReadStatusReg(ARM64_FPCR); +#else + __asm__ __volatile__("mrs %0, FPCR" : "=r"(value)); /* read */ +#endif + return value; +} + +FORCE_INLINE void _sse2neon_set_fpcr(uint64_t value) +{ +#if defined(_MSC_VER) + _WriteStatusReg(ARM64_FPCR, value); +#else + __asm__ __volatile__("msr FPCR, %0" ::"r"(value)); /* write */ +#endif +} + +// Macro: Get the flush zero bits from the MXCSR control and status register. +// The flush zero may contain any of the following flags: _MM_FLUSH_ZERO_ON or +// _MM_FLUSH_ZERO_OFF +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_GET_FLUSH_ZERO_MODE +FORCE_INLINE unsigned int _sse2neon_mm_get_flush_zero_mode(void) +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) || defined(_M_ARM64) + r.value = _sse2neon_get_fpcr(); +#else + __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + return r.field.bit24 ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF; +} + +// Macro: Get the rounding mode bits from the MXCSR control and status register. +// The rounding mode may contain any of the following flags: _MM_ROUND_NEAREST, +// _MM_ROUND_DOWN, _MM_ROUND_UP, _MM_ROUND_TOWARD_ZERO +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_GET_ROUNDING_MODE +FORCE_INLINE unsigned int _MM_GET_ROUNDING_MODE(void) +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) || defined(_M_ARM64) + r.value = _sse2neon_get_fpcr(); +#else + __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + if (r.field.bit22) { + return r.field.bit23 ? _MM_ROUND_TOWARD_ZERO : _MM_ROUND_UP; + } else { + return r.field.bit23 ? _MM_ROUND_DOWN : _MM_ROUND_NEAREST; + } +} + +// Copy a to dst, and insert the 16-bit integer i into dst at the location +// specified by imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_pi16 +#define _mm_insert_pi16(a, b, imm) \ + vreinterpret_m64_s16(vset_lane_s16((b), vreinterpret_s16_m64(a), (imm))) + +// Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point +// elements) from memory into dst. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_ps +FORCE_INLINE __m128 _mm_load_ps(const float *p) +{ + return vreinterpretq_m128_f32(vld1q_f32(p)); +} + +// Load a single-precision (32-bit) floating-point element from memory into all +// elements of dst. +// +// dst[31:0] := MEM[mem_addr+31:mem_addr] +// dst[63:32] := MEM[mem_addr+31:mem_addr] +// dst[95:64] := MEM[mem_addr+31:mem_addr] +// dst[127:96] := MEM[mem_addr+31:mem_addr] +// +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_ps1 +#define _mm_load_ps1 _mm_load1_ps + +// Load a single-precision (32-bit) floating-point element from memory into the +// lower of dst, and zero the upper 3 elements. mem_addr does not need to be +// aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_ss +FORCE_INLINE __m128 _mm_load_ss(const float *p) +{ + return vreinterpretq_m128_f32(vsetq_lane_f32(*p, vdupq_n_f32(0), 0)); +} + +// Load a single-precision (32-bit) floating-point element from memory into all +// elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load1_ps +FORCE_INLINE __m128 _mm_load1_ps(const float *p) +{ + return vreinterpretq_m128_f32(vld1q_dup_f32(p)); +} + +// Load 2 single-precision (32-bit) floating-point elements from memory into the +// upper 2 elements of dst, and copy the lower 2 elements from a to dst. +// mem_addr does not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadh_pi +FORCE_INLINE __m128 _mm_loadh_pi(__m128 a, __m64 const *p) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vget_low_f32(a), vld1_f32((const float32_t *) p))); +} + +// Load 2 single-precision (32-bit) floating-point elements from memory into the +// lower 2 elements of dst, and copy the upper 2 elements from a to dst. +// mem_addr does not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadl_pi +FORCE_INLINE __m128 _mm_loadl_pi(__m128 a, __m64 const *p) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vld1_f32((const float32_t *) p), vget_high_f32(a))); +} + +// Load 4 single-precision (32-bit) floating-point elements from memory into dst +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadr_ps +FORCE_INLINE __m128 _mm_loadr_ps(const float *p) +{ + float32x4_t v = vrev64q_f32(vld1q_f32(p)); + return vreinterpretq_m128_f32(vextq_f32(v, v, 2)); +} + +// Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point +// elements) from memory into dst. mem_addr does not need to be aligned on any +// particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_ps +FORCE_INLINE __m128 _mm_loadu_ps(const float *p) +{ + // for neon, alignment doesn't matter, so _mm_load_ps and _mm_loadu_ps are + // equivalent for neon + return vreinterpretq_m128_f32(vld1q_f32(p)); +} + +// Load unaligned 16-bit integer from memory into the first element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si16 +FORCE_INLINE __m128i _mm_loadu_si16(const void *p) +{ + return vreinterpretq_m128i_s16( + vsetq_lane_s16(*(const int16_t *) p, vdupq_n_s16(0), 0)); +} + +// Load unaligned 64-bit integer from memory into the first element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si64 +FORCE_INLINE __m128i _mm_loadu_si64(const void *p) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vld1_s64((const int64_t *) p), vdup_n_s64(0))); +} + +// Allocate size bytes of memory, aligned to the alignment specified in align, +// and return a pointer to the allocated memory. _mm_free should be used to free +// memory that is allocated with _mm_malloc. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_malloc +#if !defined(SSE2NEON_ALLOC_DEFINED) +FORCE_INLINE void *_mm_malloc(size_t size, size_t align) +{ + void *ptr; + if (align == 1) + return malloc(size); + if (align == 2 || (sizeof(void *) == 8 && align == 4)) + align = sizeof(void *); + if (!posix_memalign(&ptr, align, size)) + return ptr; + return NULL; +} +#endif + +// Conditionally store 8-bit integer elements from a into memory using mask +// (elements are not stored when the highest bit is not set in the corresponding +// element) and a non-temporal memory hint. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maskmove_si64 +FORCE_INLINE void _mm_maskmove_si64(__m64 a, __m64 mask, char *mem_addr) +{ + int8x8_t shr_mask = vshr_n_s8(vreinterpret_s8_m64(mask), 7); + __m128 b = _mm_load_ps((const float *) mem_addr); + int8x8_t masked = + vbsl_s8(vreinterpret_u8_s8(shr_mask), vreinterpret_s8_m64(a), + vreinterpret_s8_u64(vget_low_u64(vreinterpretq_u64_m128(b)))); + vst1_s8((int8_t *) mem_addr, masked); +} + +// Conditionally store 8-bit integer elements from a into memory using mask +// (elements are not stored when the highest bit is not set in the corresponding +// element) and a non-temporal memory hint. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_maskmovq +#define _m_maskmovq(a, mask, mem_addr) _mm_maskmove_si64(a, mask, mem_addr) + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_pi16 +FORCE_INLINE __m64 _mm_max_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vmax_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b, +// and store packed maximum values in dst. dst does not follow the IEEE Standard +// for Floating-Point Arithmetic (IEEE 754) maximum value when inputs are NaN or +// signed-zero values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_ps +FORCE_INLINE __m128 _mm_max_ps(__m128 a, __m128 b) +{ +#if SSE2NEON_PRECISE_MINMAX + float32x4_t _a = vreinterpretq_f32_m128(a); + float32x4_t _b = vreinterpretq_f32_m128(b); + return vreinterpretq_m128_f32(vbslq_f32(vcgtq_f32(_a, _b), _a, _b)); +#else + return vreinterpretq_m128_f32( + vmaxq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#endif +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_pu8 +FORCE_INLINE __m64 _mm_max_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vmax_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b, store the maximum value in the lower element of dst, and copy the upper 3 +// packed elements from a to the upper element of dst. dst does not follow the +// IEEE Standard for Floating-Point Arithmetic (IEEE 754) maximum value when +// inputs are NaN or signed-zero values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_ss +FORCE_INLINE __m128 _mm_max_ss(__m128 a, __m128 b) +{ + float32_t value = vgetq_lane_f32(_mm_max_ps(a, b), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_pi16 +FORCE_INLINE __m64 _mm_min_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vmin_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Compare packed single-precision (32-bit) floating-point elements in a and b, +// and store packed minimum values in dst. dst does not follow the IEEE Standard +// for Floating-Point Arithmetic (IEEE 754) minimum value when inputs are NaN or +// signed-zero values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_ps +FORCE_INLINE __m128 _mm_min_ps(__m128 a, __m128 b) +{ +#if SSE2NEON_PRECISE_MINMAX + float32x4_t _a = vreinterpretq_f32_m128(a); + float32x4_t _b = vreinterpretq_f32_m128(b); + return vreinterpretq_m128_f32(vbslq_f32(vcltq_f32(_a, _b), _a, _b)); +#else + return vreinterpretq_m128_f32( + vminq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#endif +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_pu8 +FORCE_INLINE __m64 _mm_min_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vmin_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Compare the lower single-precision (32-bit) floating-point elements in a and +// b, store the minimum value in the lower element of dst, and copy the upper 3 +// packed elements from a to the upper element of dst. dst does not follow the +// IEEE Standard for Floating-Point Arithmetic (IEEE 754) minimum value when +// inputs are NaN or signed-zero values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_ss +FORCE_INLINE __m128 _mm_min_ss(__m128 a, __m128 b) +{ + float32_t value = vgetq_lane_f32(_mm_min_ps(a, b), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Move the lower single-precision (32-bit) floating-point element from b to the +// lower element of dst, and copy the upper 3 packed elements from a to the +// upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_move_ss +FORCE_INLINE __m128 _mm_move_ss(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32(vgetq_lane_f32(vreinterpretq_f32_m128(b), 0), + vreinterpretq_f32_m128(a), 0)); +} + +// Move the upper 2 single-precision (32-bit) floating-point elements from b to +// the lower 2 elements of dst, and copy the upper 2 elements from a to the +// upper 2 elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movehl_ps +FORCE_INLINE __m128 _mm_movehl_ps(__m128 a, __m128 b) +{ +#if defined(aarch64__) + return vreinterpretq_m128_u64( + vzip2q_u64(vreinterpretq_u64_m128(b), vreinterpretq_u64_m128(a))); +#else + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(b32, a32)); +#endif +} + +// Move the lower 2 single-precision (32-bit) floating-point elements from b to +// the upper 2 elements of dst, and copy the lower 2 elements from a to the +// lower 2 elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movelh_ps +FORCE_INLINE __m128 _mm_movelh_ps(__m128 __A, __m128 __B) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(__A)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(__B)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); +} + +// Create mask from the most significant bit of each 8-bit element in a, and +// store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_pi8 +FORCE_INLINE int _mm_movemask_pi8(__m64 a) +{ + uint8x8_t input = vreinterpret_u8_m64(a); +#if defined(__aarch64__) || defined(_M_ARM64) + static const int8_t shift[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + uint8x8_t tmp = vshr_n_u8(input, 7); + return vaddv_u8(vshl_u8(tmp, vld1_s8(shift))); +#else + // Refer the implementation of `_mm_movemask_epi8` + uint16x4_t high_bits = vreinterpret_u16_u8(vshr_n_u8(input, 7)); + uint32x2_t paired16 = + vreinterpret_u32_u16(vsra_n_u16(high_bits, high_bits, 7)); + uint8x8_t paired32 = + vreinterpret_u8_u32(vsra_n_u32(paired16, paired16, 14)); + return vget_lane_u8(paired32, 0) | ((int) vget_lane_u8(paired32, 4) << 4); +#endif +} + +// Set each bit of mask dst based on the most significant bit of the +// corresponding packed single-precision (32-bit) floating-point element in a. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_ps +FORCE_INLINE int _mm_movemask_ps(__m128 a) +{ + uint32x4_t input = vreinterpretq_u32_m128(a); +#if defined(__aarch64__) || defined(_M_ARM64) + static const int32_t shift[4] = {0, 1, 2, 3}; + uint32x4_t tmp = vshrq_n_u32(input, 31); + return vaddvq_u32(vshlq_u32(tmp, vld1q_s32(shift))); +#else + // Uses the exact same method as _mm_movemask_epi8, see that for details. + // Shift out everything but the sign bits with a 32-bit unsigned shift + // right. + uint64x2_t high_bits = vreinterpretq_u64_u32(vshrq_n_u32(input, 31)); + // Merge the two pairs together with a 64-bit unsigned shift right + add. + uint8x16_t paired = + vreinterpretq_u8_u64(vsraq_n_u64(high_bits, high_bits, 31)); + // Extract the result. + return vgetq_lane_u8(paired, 0) | (vgetq_lane_u8(paired, 8) << 2); +#endif +} + +// Multiply packed single-precision (32-bit) floating-point elements in a and b, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_ps +FORCE_INLINE __m128 _mm_mul_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vmulq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Multiply the lower single-precision (32-bit) floating-point element in a and +// b, store the result in the lower element of dst, and copy the upper 3 packed +// elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_ss +FORCE_INLINE __m128 _mm_mul_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_mul_ps(a, b)); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhi_pu16 +FORCE_INLINE __m64 _mm_mulhi_pu16(__m64 a, __m64 b) +{ + return vreinterpret_m64_u16(vshrn_n_u32( + vmull_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b)), 16)); +} + +// Compute the bitwise OR of packed single-precision (32-bit) floating-point +// elements in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_or_ps +FORCE_INLINE __m128 _mm_or_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vorrq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pavgb +#define _m_pavgb(a, b) _mm_avg_pu8(a, b) + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pavgw +#define _m_pavgw(a, b) _mm_avg_pu16(a, b) + +// Extract a 16-bit integer from a, selected with imm8, and store the result in +// the lower element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pextrw +#define _m_pextrw(a, imm) _mm_extract_pi16(a, imm) + +// Copy a to dst, and insert the 16-bit integer i into dst at the location +// specified by imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=m_pinsrw +#define _m_pinsrw(a, i, imm) _mm_insert_pi16(a, i, imm) + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmaxsw +#define _m_pmaxsw(a, b) _mm_max_pi16(a, b) + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmaxub +#define _m_pmaxub(a, b) _mm_max_pu8(a, b) + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pminsw +#define _m_pminsw(a, b) _mm_min_pi16(a, b) + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pminub +#define _m_pminub(a, b) _mm_min_pu8(a, b) + +// Create mask from the most significant bit of each 8-bit element in a, and +// store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmovmskb +#define _m_pmovmskb(a) _mm_movemask_pi8(a) + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmulhuw +#define _m_pmulhuw(a, b) _mm_mulhi_pu16(a, b) + +// Fetch the line of data from memory that contains address p to a location in +// the cache hierarchy specified by the locality hint i. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_prefetch +FORCE_INLINE void _mm_prefetch(char const *p, int i) +{ + (void) i; +#if defined(_MSC_VER) + switch (i) { + case _MM_HINT_NTA: + __prefetch2(p, 1); + break; + case _MM_HINT_T0: + __prefetch2(p, 0); + break; + case _MM_HINT_T1: + __prefetch2(p, 2); + break; + case _MM_HINT_T2: + __prefetch2(p, 4); + break; + } +#else + switch (i) { + case _MM_HINT_NTA: + __builtin_prefetch(p, 0, 0); + break; + case _MM_HINT_T0: + __builtin_prefetch(p, 0, 3); + break; + case _MM_HINT_T1: + __builtin_prefetch(p, 0, 2); + break; + case _MM_HINT_T2: + __builtin_prefetch(p, 0, 1); + break; + } +#endif +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce four +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=m_psadbw +#define _m_psadbw(a, b) _mm_sad_pu8(a, b) + +// Shuffle 16-bit integers in a using the control in imm8, and store the results +// in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pshufw +#define _m_pshufw(a, imm) _mm_shuffle_pi16(a, imm) + +// Compute the approximate reciprocal of packed single-precision (32-bit) +// floating-point elements in a, and store the results in dst. The maximum +// relative error for this approximation is less than 1.5*2^-12. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rcp_ps +FORCE_INLINE __m128 _mm_rcp_ps(__m128 in) +{ + float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(in)); + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(in))); + return vreinterpretq_m128_f32(recip); +} + +// Compute the approximate reciprocal of the lower single-precision (32-bit) +// floating-point element in a, store the result in the lower element of dst, +// and copy the upper 3 packed elements from a to the upper elements of dst. The +// maximum relative error for this approximation is less than 1.5*2^-12. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rcp_ss +FORCE_INLINE __m128 _mm_rcp_ss(__m128 a) +{ + return _mm_move_ss(a, _mm_rcp_ps(a)); +} + +// Compute the approximate reciprocal square root of packed single-precision +// (32-bit) floating-point elements in a, and store the results in dst. The +// maximum relative error for this approximation is less than 1.5*2^-12. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rsqrt_ps +FORCE_INLINE __m128 _mm_rsqrt_ps(__m128 in) +{ + float32x4_t out = vrsqrteq_f32(vreinterpretq_f32_m128(in)); + + // Generate masks for detecting whether input has any 0.0f/-0.0f + // (which becomes positive/negative infinity by IEEE-754 arithmetic rules). + const uint32x4_t pos_inf = vdupq_n_u32(0x7F800000); + const uint32x4_t neg_inf = vdupq_n_u32(0xFF800000); + const uint32x4_t has_pos_zero = + vceqq_u32(pos_inf, vreinterpretq_u32_f32(out)); + const uint32x4_t has_neg_zero = + vceqq_u32(neg_inf, vreinterpretq_u32_f32(out)); + + out = vmulq_f32( + out, vrsqrtsq_f32(vmulq_f32(vreinterpretq_f32_m128(in), out), out)); + + // Set output vector element to infinity/negative-infinity if + // the corresponding input vector element is 0.0f/-0.0f. + out = vbslq_f32(has_pos_zero, (float32x4_t) pos_inf, out); + out = vbslq_f32(has_neg_zero, (float32x4_t) neg_inf, out); + + return vreinterpretq_m128_f32(out); +} + +// Compute the approximate reciprocal square root of the lower single-precision +// (32-bit) floating-point element in a, store the result in the lower element +// of dst, and copy the upper 3 packed elements from a to the upper elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rsqrt_ss +FORCE_INLINE __m128 _mm_rsqrt_ss(__m128 in) +{ + return vsetq_lane_f32(vgetq_lane_f32(_mm_rsqrt_ps(in), 0), in, 0); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce four +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_pu8 +FORCE_INLINE __m64 _mm_sad_pu8(__m64 a, __m64 b) +{ + uint64x1_t t = vpaddl_u32(vpaddl_u16( + vpaddl_u8(vabd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))))); + return vreinterpret_m64_u16( + vset_lane_u16((int) vget_lane_u64(t, 0), vdup_n_u16(0), 0)); +} + +// Macro: Set the flush zero bits of the MXCSR control and status register to +// the value in unsigned 32-bit integer a. The flush zero may contain any of the +// following flags: _MM_FLUSH_ZERO_ON or _MM_FLUSH_ZERO_OFF +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_SET_FLUSH_ZERO_MODE +FORCE_INLINE void _sse2neon_mm_set_flush_zero_mode(unsigned int flag) +{ + // AArch32 Advanced SIMD arithmetic always uses the Flush-to-zero setting, + // regardless of the value of the FZ bit. + union { + fpcr_bitfield field; +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) || defined(_M_ARM64) + r.value = _sse2neon_get_fpcr(); +#else + __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + r.field.bit24 = (flag & _MM_FLUSH_ZERO_MASK) == _MM_FLUSH_ZERO_ON; + +#if defined(__aarch64__) || defined(_M_ARM64) + _sse2neon_set_fpcr(r.value); +#else + __asm__ __volatile__("vmsr FPSCR, %0" ::"r"(r)); /* write */ +#endif +} + +// Set packed single-precision (32-bit) floating-point elements in dst with the +// supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_ps +FORCE_INLINE __m128 _mm_set_ps(float w, float z, float y, float x) +{ + float ALIGN_STRUCT(16) data[4] = {x, y, z, w}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Broadcast single-precision (32-bit) floating-point value a to all elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_ps1 +FORCE_INLINE __m128 _mm_set_ps1(float _w) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(_w)); +} + +// Macro: Set the rounding mode bits of the MXCSR control and status register to +// the value in unsigned 32-bit integer a. The rounding mode may contain any of +// the following flags: _MM_ROUND_NEAREST, _MM_ROUND_DOWN, _MM_ROUND_UP, +// _MM_ROUND_TOWARD_ZERO +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_SET_ROUNDING_MODE +FORCE_INLINE void _MM_SET_ROUNDING_MODE(int rounding) +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) || defined(_M_ARM64) + r.value = _sse2neon_get_fpcr(); +#else + __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + switch (rounding) { + case _MM_ROUND_TOWARD_ZERO: + r.field.bit22 = 1; + r.field.bit23 = 1; + break; + case _MM_ROUND_DOWN: + r.field.bit22 = 0; + r.field.bit23 = 1; + break; + case _MM_ROUND_UP: + r.field.bit22 = 1; + r.field.bit23 = 0; + break; + default: //_MM_ROUND_NEAREST + r.field.bit22 = 0; + r.field.bit23 = 0; + } + +#if defined(__aarch64__) || defined(_M_ARM64) + _sse2neon_set_fpcr(r.value); +#else + __asm__ __volatile__("vmsr FPSCR, %0" ::"r"(r)); /* write */ +#endif +} + +// Copy single-precision (32-bit) floating-point element a to the lower element +// of dst, and zero the upper 3 elements. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_ss +FORCE_INLINE __m128 _mm_set_ss(float a) +{ + return vreinterpretq_m128_f32(vsetq_lane_f32(a, vdupq_n_f32(0), 0)); +} + +// Broadcast single-precision (32-bit) floating-point value a to all elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_ps +FORCE_INLINE __m128 _mm_set1_ps(float _w) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(_w)); +} + +// Set the MXCSR control and status register with the value in unsigned 32-bit +// integer a. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setcsr +// FIXME: _mm_setcsr() implementation supports changing the rounding mode only. +FORCE_INLINE void _mm_setcsr(unsigned int a) +{ + _MM_SET_ROUNDING_MODE(a); +} + +// Get the unsigned 32-bit value of the MXCSR control and status register. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_getcsr +// FIXME: _mm_getcsr() implementation supports reading the rounding mode only. +FORCE_INLINE unsigned int _mm_getcsr(void) +{ + return _MM_GET_ROUNDING_MODE(); +} + +// Set packed single-precision (32-bit) floating-point elements in dst with the +// supplied values in reverse order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_ps +FORCE_INLINE __m128 _mm_setr_ps(float w, float z, float y, float x) +{ + float ALIGN_STRUCT(16) data[4] = {w, z, y, x}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Return vector of type __m128 with all elements set to zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setzero_ps +FORCE_INLINE __m128 _mm_setzero_ps(void) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(0)); +} + +// Shuffle 16-bit integers in a using the control in imm8, and store the results +// in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_pi16 +#ifdef _sse2neon_shuffle +#define _mm_shuffle_pi16(a, imm) \ + vreinterpret_m64_s16(vshuffle_s16( \ + vreinterpret_s16_m64(a), vreinterpret_s16_m64(a), (imm & 0x3), \ + ((imm >> 2) & 0x3), ((imm >> 4) & 0x3), ((imm >> 6) & 0x3))) +#else +#define _mm_shuffle_pi16(a, imm) \ + _sse2neon_define1( \ + __m64, a, int16x4_t ret; \ + ret = vmov_n_s16( \ + vget_lane_s16(vreinterpret_s16_m64(_a), (imm) & (0x3))); \ + ret = vset_lane_s16( \ + vget_lane_s16(vreinterpret_s16_m64(_a), ((imm) >> 2) & 0x3), ret, \ + 1); \ + ret = vset_lane_s16( \ + vget_lane_s16(vreinterpret_s16_m64(_a), ((imm) >> 4) & 0x3), ret, \ + 2); \ + ret = vset_lane_s16( \ + vget_lane_s16(vreinterpret_s16_m64(_a), ((imm) >> 6) & 0x3), ret, \ + 3); \ + _sse2neon_return(vreinterpret_m64_s16(ret));) +#endif + +// Perform a serializing operation on all store-to-memory instructions that were +// issued prior to this instruction. Guarantees that every store instruction +// that precedes, in program order, is globally visible before any store +// instruction which follows the fence in program order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sfence +FORCE_INLINE void _mm_sfence(void) +{ + _sse2neon_smp_mb(); +} + +// Perform a serializing operation on all load-from-memory and store-to-memory +// instructions that were issued prior to this instruction. Guarantees that +// every memory access that precedes, in program order, the memory fence +// instruction is globally visible before any memory instruction which follows +// the fence in program order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mfence +FORCE_INLINE void _mm_mfence(void) +{ + _sse2neon_smp_mb(); +} + +// Perform a serializing operation on all load-from-memory instructions that +// were issued prior to this instruction. Guarantees that every load instruction +// that precedes, in program order, is globally visible before any load +// instruction which follows the fence in program order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_lfence +FORCE_INLINE void _mm_lfence(void) +{ + _sse2neon_smp_mb(); +} + +// FORCE_INLINE __m128 _mm_shuffle_ps(__m128 a, __m128 b, __constrange(0,255) +// int imm) +#ifdef _sse2neon_shuffle +#define _mm_shuffle_ps(a, b, imm) \ + __extension__({ \ + float32x4_t _input1 = vreinterpretq_f32_m128(a); \ + float32x4_t _input2 = vreinterpretq_f32_m128(b); \ + float32x4_t _shuf = \ + vshuffleq_s32(_input1, _input2, (imm) & (0x3), ((imm) >> 2) & 0x3, \ + (((imm) >> 4) & 0x3) + 4, (((imm) >> 6) & 0x3) + 4); \ + vreinterpretq_m128_f32(_shuf); \ + }) +#else // generic +#define _mm_shuffle_ps(a, b, imm) \ + _sse2neon_define2( \ + __m128, a, b, __m128 ret; switch (imm) { \ + case _MM_SHUFFLE(1, 0, 3, 2): \ + ret = _mm_shuffle_ps_1032(_a, _b); \ + break; \ + case _MM_SHUFFLE(2, 3, 0, 1): \ + ret = _mm_shuffle_ps_2301(_a, _b); \ + break; \ + case _MM_SHUFFLE(0, 3, 2, 1): \ + ret = _mm_shuffle_ps_0321(_a, _b); \ + break; \ + case _MM_SHUFFLE(2, 1, 0, 3): \ + ret = _mm_shuffle_ps_2103(_a, _b); \ + break; \ + case _MM_SHUFFLE(1, 0, 1, 0): \ + ret = _mm_movelh_ps(_a, _b); \ + break; \ + case _MM_SHUFFLE(1, 0, 0, 1): \ + ret = _mm_shuffle_ps_1001(_a, _b); \ + break; \ + case _MM_SHUFFLE(0, 1, 0, 1): \ + ret = _mm_shuffle_ps_0101(_a, _b); \ + break; \ + case _MM_SHUFFLE(3, 2, 1, 0): \ + ret = _mm_shuffle_ps_3210(_a, _b); \ + break; \ + case _MM_SHUFFLE(0, 0, 1, 1): \ + ret = _mm_shuffle_ps_0011(_a, _b); \ + break; \ + case _MM_SHUFFLE(0, 0, 2, 2): \ + ret = _mm_shuffle_ps_0022(_a, _b); \ + break; \ + case _MM_SHUFFLE(2, 2, 0, 0): \ + ret = _mm_shuffle_ps_2200(_a, _b); \ + break; \ + case _MM_SHUFFLE(3, 2, 0, 2): \ + ret = _mm_shuffle_ps_3202(_a, _b); \ + break; \ + case _MM_SHUFFLE(3, 2, 3, 2): \ + ret = _mm_movehl_ps(_b, _a); \ + break; \ + case _MM_SHUFFLE(1, 1, 3, 3): \ + ret = _mm_shuffle_ps_1133(_a, _b); \ + break; \ + case _MM_SHUFFLE(2, 0, 1, 0): \ + ret = _mm_shuffle_ps_2010(_a, _b); \ + break; \ + case _MM_SHUFFLE(2, 0, 0, 1): \ + ret = _mm_shuffle_ps_2001(_a, _b); \ + break; \ + case _MM_SHUFFLE(2, 0, 3, 2): \ + ret = _mm_shuffle_ps_2032(_a, _b); \ + break; \ + default: \ + ret = _mm_shuffle_ps_default(_a, _b, (imm)); \ + break; \ + } _sse2neon_return(ret);) +#endif + +// Compute the square root of packed single-precision (32-bit) floating-point +// elements in a, and store the results in dst. +// Due to ARMv7-A NEON's lack of a precise square root intrinsic, we implement +// square root by multiplying input in with its reciprocal square root before +// using the Newton-Raphson method to approximate the results. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_ps +FORCE_INLINE __m128 _mm_sqrt_ps(__m128 in) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32(vsqrtq_f32(vreinterpretq_f32_m128(in))); +#else + float32x4_t recip = vrsqrteq_f32(vreinterpretq_f32_m128(in)); + + // Test for vrsqrteq_f32(0) -> positive infinity case. + // Change to zero, so that s * 1/sqrt(s) result is zero too. + const uint32x4_t pos_inf = vdupq_n_u32(0x7F800000); + const uint32x4_t div_by_zero = + vceqq_u32(pos_inf, vreinterpretq_u32_f32(recip)); + recip = vreinterpretq_f32_u32( + vandq_u32(vmvnq_u32(div_by_zero), vreinterpretq_u32_f32(recip))); + + recip = vmulq_f32( + vrsqrtsq_f32(vmulq_f32(recip, recip), vreinterpretq_f32_m128(in)), + recip); + // Additional Netwon-Raphson iteration for accuracy + recip = vmulq_f32( + vrsqrtsq_f32(vmulq_f32(recip, recip), vreinterpretq_f32_m128(in)), + recip); + + // sqrt(s) = s * 1/sqrt(s) + return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(in), recip)); +#endif +} + +// Compute the square root of the lower single-precision (32-bit) floating-point +// element in a, store the result in the lower element of dst, and copy the +// upper 3 packed elements from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_ss +FORCE_INLINE __m128 _mm_sqrt_ss(__m128 in) +{ + float32_t value = + vgetq_lane_f32(vreinterpretq_f32_m128(_mm_sqrt_ps(in)), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(in), 0)); +} + +// Store 128-bits (composed of 4 packed single-precision (32-bit) floating-point +// elements) from a into memory. mem_addr must be aligned on a 16-byte boundary +// or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_ps +FORCE_INLINE void _mm_store_ps(float *p, __m128 a) +{ + vst1q_f32(p, vreinterpretq_f32_m128(a)); +} + +// Store the lower single-precision (32-bit) floating-point element from a into +// 4 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_ps1 +FORCE_INLINE void _mm_store_ps1(float *p, __m128 a) +{ + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + vst1q_f32(p, vdupq_n_f32(a0)); +} + +// Store the lower single-precision (32-bit) floating-point element from a into +// memory. mem_addr does not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_ss +FORCE_INLINE void _mm_store_ss(float *p, __m128 a) +{ + vst1q_lane_f32(p, vreinterpretq_f32_m128(a), 0); +} + +// Store the lower single-precision (32-bit) floating-point element from a into +// 4 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store1_ps +#define _mm_store1_ps _mm_store_ps1 + +// Store the upper 2 single-precision (32-bit) floating-point elements from a +// into memory. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeh_pi +FORCE_INLINE void _mm_storeh_pi(__m64 *p, __m128 a) +{ + *p = vreinterpret_m64_f32(vget_high_f32(a)); +} + +// Store the lower 2 single-precision (32-bit) floating-point elements from a +// into memory. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storel_pi +FORCE_INLINE void _mm_storel_pi(__m64 *p, __m128 a) +{ + *p = vreinterpret_m64_f32(vget_low_f32(a)); +} + +// Store 4 single-precision (32-bit) floating-point elements from a into memory +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storer_ps +FORCE_INLINE void _mm_storer_ps(float *p, __m128 a) +{ + float32x4_t tmp = vrev64q_f32(vreinterpretq_f32_m128(a)); + float32x4_t rev = vextq_f32(tmp, tmp, 2); + vst1q_f32(p, rev); +} + +// Store 128-bits (composed of 4 packed single-precision (32-bit) floating-point +// elements) from a into memory. mem_addr does not need to be aligned on any +// particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_ps +FORCE_INLINE void _mm_storeu_ps(float *p, __m128 a) +{ + vst1q_f32(p, vreinterpretq_f32_m128(a)); +} + +// Stores 16-bits of integer data a at the address p. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si16 +FORCE_INLINE void _mm_storeu_si16(void *p, __m128i a) +{ + vst1q_lane_s16((int16_t *) p, vreinterpretq_s16_m128i(a), 0); +} + +// Stores 64-bits of integer data a at the address p. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si64 +FORCE_INLINE void _mm_storeu_si64(void *p, __m128i a) +{ + vst1q_lane_s64((int64_t *) p, vreinterpretq_s64_m128i(a), 0); +} + +// Store 64-bits of integer data from a into memory using a non-temporal memory +// hint. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_pi +FORCE_INLINE void _mm_stream_pi(__m64 *p, __m64 a) +{ + vst1_s64((int64_t *) p, vreinterpret_s64_m64(a)); +} + +// Store 128-bits (composed of 4 packed single-precision (32-bit) floating- +// point elements) from a into memory using a non-temporal memory hint. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_ps +FORCE_INLINE void _mm_stream_ps(float *p, __m128 a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, (float32x4_t *) p); +#else + vst1q_f32(p, vreinterpretq_f32_m128(a)); +#endif +} + +// Subtract packed single-precision (32-bit) floating-point elements in b from +// packed single-precision (32-bit) floating-point elements in a, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_ps +FORCE_INLINE __m128 _mm_sub_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vsubq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Subtract the lower single-precision (32-bit) floating-point element in b from +// the lower single-precision (32-bit) floating-point element in a, store the +// result in the lower element of dst, and copy the upper 3 packed elements from +// a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_ss +FORCE_INLINE __m128 _mm_sub_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_sub_ps(a, b)); +} + +// Macro: Transpose the 4x4 matrix formed by the 4 rows of single-precision +// (32-bit) floating-point elements in row0, row1, row2, and row3, and store the +// transposed matrix in these vectors (row0 now contains column 0, etc.). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=MM_TRANSPOSE4_PS +#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3) \ + do { \ + float32x4x2_t ROW01 = vtrnq_f32(row0, row1); \ + float32x4x2_t ROW23 = vtrnq_f32(row2, row3); \ + row0 = vcombine_f32(vget_low_f32(ROW01.val[0]), \ + vget_low_f32(ROW23.val[0])); \ + row1 = vcombine_f32(vget_low_f32(ROW01.val[1]), \ + vget_low_f32(ROW23.val[1])); \ + row2 = vcombine_f32(vget_high_f32(ROW01.val[0]), \ + vget_high_f32(ROW23.val[0])); \ + row3 = vcombine_f32(vget_high_f32(ROW01.val[1]), \ + vget_high_f32(ROW23.val[1])); \ + } while (0) + +// according to the documentation, these intrinsics behave the same as the +// non-'u' versions. We'll just alias them here. +#define _mm_ucomieq_ss _mm_comieq_ss +#define _mm_ucomige_ss _mm_comige_ss +#define _mm_ucomigt_ss _mm_comigt_ss +#define _mm_ucomile_ss _mm_comile_ss +#define _mm_ucomilt_ss _mm_comilt_ss +#define _mm_ucomineq_ss _mm_comineq_ss + +// Return vector of type __m128i with undefined elements. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_undefined_si128 +FORCE_INLINE __m128i _mm_undefined_si128(void) +{ +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + __m128i a; +#if defined(_MSC_VER) + a = _mm_setzero_si128(); +#endif + return a; +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +// Return vector of type __m128 with undefined elements. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_undefined_ps +FORCE_INLINE __m128 _mm_undefined_ps(void) +{ +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + __m128 a; +#if defined(_MSC_VER) + a = _mm_setzero_ps(); +#endif + return a; +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +// Unpack and interleave single-precision (32-bit) floating-point elements from +// the high half a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_ps +FORCE_INLINE __m128 _mm_unpackhi_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vzip2q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a1 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b1 = vget_high_f32(vreinterpretq_f32_m128(b)); + float32x2x2_t result = vzip_f32(a1, b1); + return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave single-precision (32-bit) floating-point elements from +// the low half of a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_ps +FORCE_INLINE __m128 _mm_unpacklo_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vzip1q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a1 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b1 = vget_low_f32(vreinterpretq_f32_m128(b)); + float32x2x2_t result = vzip_f32(a1, b1); + return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); +#endif +} + +// Compute the bitwise XOR of packed single-precision (32-bit) floating-point +// elements in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_xor_ps +FORCE_INLINE __m128 _mm_xor_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + veorq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +/* SSE2 */ + +// Add packed 16-bit integers in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi16 +FORCE_INLINE __m128i _mm_add_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Add packed 32-bit integers in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi32 +FORCE_INLINE __m128i _mm_add_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vaddq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Add packed 64-bit integers in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi64 +FORCE_INLINE __m128i _mm_add_epi64(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s64( + vaddq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +} + +// Add packed 8-bit integers in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi8 +FORCE_INLINE __m128i _mm_add_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Add packed double-precision (64-bit) floating-point elements in a and b, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_pd +FORCE_INLINE __m128d _mm_add_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] + db[0]; + c[1] = da[1] + db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Add the lower double-precision (64-bit) floating-point element in a and b, +// store the result in the lower element of dst, and copy the upper element from +// a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_sd +FORCE_INLINE __m128d _mm_add_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_add_pd(a, b)); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] + db[0]; + c[1] = da[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Add 64-bit integers a and b, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_si64 +FORCE_INLINE __m64 _mm_add_si64(__m64 a, __m64 b) +{ + return vreinterpret_m64_s64( + vadd_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); +} + +// Add packed signed 16-bit integers in a and b using saturation, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epi16 +FORCE_INLINE __m128i _mm_adds_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vqaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Add packed signed 8-bit integers in a and b using saturation, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epi8 +FORCE_INLINE __m128i _mm_adds_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vqaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Add packed unsigned 16-bit integers in a and b using saturation, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epu16 +FORCE_INLINE __m128i _mm_adds_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vqaddq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Add packed unsigned 8-bit integers in a and b using saturation, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epu8 +FORCE_INLINE __m128i _mm_adds_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vqaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Compute the bitwise AND of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_and_pd +FORCE_INLINE __m128d _mm_and_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + vandq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_and_si128 +FORCE_INLINE __m128i _mm_and_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vandq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compute the bitwise NOT of packed double-precision (64-bit) floating-point +// elements in a and then AND with b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_andnot_pd +FORCE_INLINE __m128d _mm_andnot_pd(__m128d a, __m128d b) +{ + // *NOTE* argument swap + return vreinterpretq_m128d_s64( + vbicq_s64(vreinterpretq_s64_m128d(b), vreinterpretq_s64_m128d(a))); +} + +// Compute the bitwise NOT of 128 bits (representing integer data) in a and then +// AND with b, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_andnot_si128 +FORCE_INLINE __m128i _mm_andnot_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vbicq_s32(vreinterpretq_s32_m128i(b), + vreinterpretq_s32_m128i(a))); // *NOTE* argument swap +} + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_epu16 +FORCE_INLINE __m128i _mm_avg_epu16(__m128i a, __m128i b) +{ + return (__m128i) vrhaddq_u16(vreinterpretq_u16_m128i(a), + vreinterpretq_u16_m128i(b)); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_epu8 +FORCE_INLINE __m128i _mm_avg_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vrhaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Shift a left by imm8 bytes while shifting in zeros, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_bslli_si128 +#define _mm_bslli_si128(a, imm) _mm_slli_si128(a, imm) + +// Shift a right by imm8 bytes while shifting in zeros, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_bsrli_si128 +#define _mm_bsrli_si128(a, imm) _mm_srli_si128(a, imm) + +// Cast vector of type __m128d to type __m128. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castpd_ps +FORCE_INLINE __m128 _mm_castpd_ps(__m128d a) +{ + return vreinterpretq_m128_s64(vreinterpretq_s64_m128d(a)); +} + +// Cast vector of type __m128d to type __m128i. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castpd_si128 +FORCE_INLINE __m128i _mm_castpd_si128(__m128d a) +{ + return vreinterpretq_m128i_s64(vreinterpretq_s64_m128d(a)); +} + +// Cast vector of type __m128 to type __m128d. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castps_pd +FORCE_INLINE __m128d _mm_castps_pd(__m128 a) +{ + return vreinterpretq_m128d_s32(vreinterpretq_s32_m128(a)); +} + +// Cast vector of type __m128 to type __m128i. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castps_si128 +FORCE_INLINE __m128i _mm_castps_si128(__m128 a) +{ + return vreinterpretq_m128i_s32(vreinterpretq_s32_m128(a)); +} + +// Cast vector of type __m128i to type __m128d. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castsi128_pd +FORCE_INLINE __m128d _mm_castsi128_pd(__m128i a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vreinterpretq_f64_m128i(a)); +#else + return vreinterpretq_m128d_f32(vreinterpretq_f32_m128i(a)); +#endif +} + +// Cast vector of type __m128i to type __m128. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castsi128_ps +FORCE_INLINE __m128 _mm_castsi128_ps(__m128i a) +{ + return vreinterpretq_m128_s32(vreinterpretq_s32_m128i(a)); +} + +// Invalidate and flush the cache line that contains p from all levels of the +// cache hierarchy. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clflush +#if defined(__APPLE__) +#include +#endif +FORCE_INLINE void _mm_clflush(void const *p) +{ + (void) p; + + /* sys_icache_invalidate is supported since macOS 10.5. + * However, it does not work on non-jailbroken iOS devices, although the + * compilation is successful. + */ +#if defined(__APPLE__) + sys_icache_invalidate((void *) (uintptr_t) p, SSE2NEON_CACHELINE_SIZE); +#elif defined(__GNUC__) || defined(__clang__) + uintptr_t ptr = (uintptr_t) p; + __builtin___clear_cache((char *) ptr, + (char *) ptr + SSE2NEON_CACHELINE_SIZE); +#elif (_MSC_VER) && SSE2NEON_INCLUDE_WINDOWS_H + FlushInstructionCache(GetCurrentProcess(), p, SSE2NEON_CACHELINE_SIZE); +#endif +} + +// Compare packed 16-bit integers in a and b for equality, and store the results +// in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_epi16 +FORCE_INLINE __m128i _mm_cmpeq_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vceqq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed 32-bit integers in a and b for equality, and store the results +// in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_epi32 +FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vceqq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed 8-bit integers in a and b for equality, and store the results +// in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_epi8 +FORCE_INLINE __m128i _mm_cmpeq_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vceqq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for equality, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_pd +FORCE_INLINE __m128d _mm_cmpeq_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64( + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128d_u32(vandq_u32(cmp, swapped)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for equality, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_sd +FORCE_INLINE __m128d _mm_cmpeq_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpeq_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for greater-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_pd +FORCE_INLINE __m128d _mm_cmpge_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64( + vcgeq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) >= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) >= (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for greater-than-or-equal, store the result in the lower element of dst, +// and copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_sd +FORCE_INLINE __m128d _mm_cmpge_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_cmpge_pd(a, b)); +#else + // expand "_mm_cmpge_pd()" to reduce unnecessary operations + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) >= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed signed 16-bit integers in a and b for greater-than, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_epi16 +FORCE_INLINE __m128i _mm_cmpgt_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcgtq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed signed 32-bit integers in a and b for greater-than, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_epi32 +FORCE_INLINE __m128i _mm_cmpgt_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vcgtq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b for greater-than, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_epi8 +FORCE_INLINE __m128i _mm_cmpgt_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vcgtq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for greater-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_pd +FORCE_INLINE __m128d _mm_cmpgt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64( + vcgtq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) > (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) > (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for greater-than, store the result in the lower element of dst, and copy +// the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_sd +FORCE_INLINE __m128d _mm_cmpgt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_cmpgt_pd(a, b)); +#else + // expand "_mm_cmpge_pd()" to reduce unnecessary operations + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) > (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for less-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_pd +FORCE_INLINE __m128d _mm_cmple_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64( + vcleq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) <= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) <= (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for less-than-or-equal, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_sd +FORCE_INLINE __m128d _mm_cmple_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_cmple_pd(a, b)); +#else + // expand "_mm_cmpge_pd()" to reduce unnecessary operations + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) <= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed signed 16-bit integers in a and b for less-than, and store the +// results in dst. Note: This intrinsic emits the pcmpgtw instruction with the +// order of the operands switched. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_epi16 +FORCE_INLINE __m128i _mm_cmplt_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcltq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed signed 32-bit integers in a and b for less-than, and store the +// results in dst. Note: This intrinsic emits the pcmpgtd instruction with the +// order of the operands switched. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_epi32 +FORCE_INLINE __m128i _mm_cmplt_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vcltq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b for less-than, and store the +// results in dst. Note: This intrinsic emits the pcmpgtb instruction with the +// order of the operands switched. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_epi8 +FORCE_INLINE __m128i _mm_cmplt_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vcltq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for less-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_pd +FORCE_INLINE __m128d _mm_cmplt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64( + vcltq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) < (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) < (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for less-than, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_sd +FORCE_INLINE __m128d _mm_cmplt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_cmplt_pd(a, b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) < (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_pd +FORCE_INLINE __m128d _mm_cmpneq_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_s32(vmvnq_s32(vreinterpretq_s32_u64( + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))))); +#else + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128d_u32(vmvnq_u32(vandq_u32(cmp, swapped))); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-equal, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_sd +FORCE_INLINE __m128d _mm_cmpneq_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpneq_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-greater-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_pd +FORCE_INLINE __m128d _mm_cmpnge_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64(veorq_u64( + vcgeq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) >= (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) >= (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-greater-than-or-equal, store the result in the lower element of +// dst, and copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_sd +FORCE_INLINE __m128d _mm_cmpnge_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpnge_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-greater-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_cmpngt_pd +FORCE_INLINE __m128d _mm_cmpngt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64(veorq_u64( + vcgtq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) > (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) > (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-greater-than, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpngt_sd +FORCE_INLINE __m128d _mm_cmpngt_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpngt_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-less-than-or-equal, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_pd +FORCE_INLINE __m128d _mm_cmpnle_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64(veorq_u64( + vcleq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) <= (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) <= (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-less-than-or-equal, store the result in the lower element of dst, +// and copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_sd +FORCE_INLINE __m128d _mm_cmpnle_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpnle_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-less-than, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_pd +FORCE_INLINE __m128d _mm_cmpnlt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_u64(veorq_u64( + vcltq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) < (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) < (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-less-than, store the result in the lower element of dst, and copy +// the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_sd +FORCE_INLINE __m128d _mm_cmpnlt_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpnlt_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// to see if neither is NaN, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_pd +FORCE_INLINE __m128d _mm_cmpord_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + // Excluding NaNs, any two floating point numbers can be compared. + uint64x2_t not_nan_a = + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(a)); + uint64x2_t not_nan_b = + vceqq_f64(vreinterpretq_f64_m128d(b), vreinterpretq_f64_m128d(b)); + return vreinterpretq_m128d_u64(vandq_u64(not_nan_a, not_nan_b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? ~UINT64_C(0) + : UINT64_C(0); + d[1] = ((*(double *) &a1) == (*(double *) &a1) && + (*(double *) &b1) == (*(double *) &b1)) + ? ~UINT64_C(0) + : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b to see if neither is NaN, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_sd +FORCE_INLINE __m128d _mm_cmpord_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_cmpord_pd(a, b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? ~UINT64_C(0) + : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// to see if either is NaN, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_pd +FORCE_INLINE __m128d _mm_cmpunord_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + // Two NaNs are not equal in comparison operation. + uint64x2_t not_nan_a = + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(a)); + uint64x2_t not_nan_b = + vceqq_f64(vreinterpretq_f64_m128d(b), vreinterpretq_f64_m128d(b)); + return vreinterpretq_m128d_s32( + vmvnq_s32(vreinterpretq_s32_u64(vandq_u64(not_nan_a, not_nan_b)))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? UINT64_C(0) + : ~UINT64_C(0); + d[1] = ((*(double *) &a1) == (*(double *) &a1) && + (*(double *) &b1) == (*(double *) &b1)) + ? UINT64_C(0) + : ~UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b to see if either is NaN, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_sd +FORCE_INLINE __m128d _mm_cmpunord_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_cmpunord_pd(a, b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? UINT64_C(0) + : ~UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for greater-than-or-equal, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comige_sd +FORCE_INLINE int _mm_comige_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vgetq_lane_u64(vcgeq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 >= *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for greater-than, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comigt_sd +FORCE_INLINE int _mm_comigt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vgetq_lane_u64(vcgtq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 > *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for less-than-or-equal, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comile_sd +FORCE_INLINE int _mm_comile_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vgetq_lane_u64(vcleq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 <= *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for less-than, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comilt_sd +FORCE_INLINE int _mm_comilt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vgetq_lane_u64(vcltq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 < *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for equality, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comieq_sd +FORCE_INLINE int _mm_comieq_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vgetq_lane_u64(vceqq_f64(a, b), 0) & 0x1; +#else + uint32x4_t a_not_nan = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(a)); + uint32x4_t b_not_nan = + vceqq_u32(vreinterpretq_u32_m128d(b), vreinterpretq_u32_m128d(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_eq_b = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); + uint64x2_t and_results = vandq_u64(vreinterpretq_u64_u32(a_and_b_not_nan), + vreinterpretq_u64_u32(a_eq_b)); + return vgetq_lane_u64(and_results, 0) & 0x1; +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for not-equal, and return the boolean result (0 or 1). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comineq_sd +FORCE_INLINE int _mm_comineq_sd(__m128d a, __m128d b) +{ + return !_mm_comieq_sd(a, b); +} + +// Convert packed signed 32-bit integers in a to packed double-precision +// (64-bit) floating-point elements, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi32_pd +FORCE_INLINE __m128d _mm_cvtepi32_pd(__m128i a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vcvtq_f64_s64(vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a))))); +#else + double a0 = (double) vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); + double a1 = (double) vgetq_lane_s32(vreinterpretq_s32_m128i(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Convert packed signed 32-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi32_ps +FORCE_INLINE __m128 _mm_cvtepi32_ps(__m128i a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32(vreinterpretq_s32_m128i(a))); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpd_epi32 +FORCE_INLINE __m128i _mm_cvtpd_epi32(__m128d a) +{ +// vrnd32xq_f64 not supported on clang +#if defined(__ARM_FEATURE_FRINT) && !defined(__clang__) + float64x2_t rounded = vrnd32xq_f64(vreinterpretq_f64_m128d(a)); + int64x2_t integers = vcvtq_s64_f64(rounded); + return vreinterpretq_m128i_s32( + vcombine_s32(vmovn_s64(integers), vdup_n_s32(0))); +#else + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double d0 = ((double *) &rnd)[0]; + double d1 = ((double *) &rnd)[1]; + return _mm_set_epi32(0, 0, (int32_t) d1, (int32_t) d0); +#endif +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpd_pi32 +FORCE_INLINE __m64 _mm_cvtpd_pi32(__m128d a) +{ + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double d0 = ((double *) &rnd)[0]; + double d1 = ((double *) &rnd)[1]; + int32_t ALIGN_STRUCT(16) data[2] = {(int32_t) d0, (int32_t) d1}; + return vreinterpret_m64_s32(vld1_s32(data)); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed single-precision (32-bit) floating-point elements, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpd_ps +FORCE_INLINE __m128 _mm_cvtpd_ps(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + float32x2_t tmp = vcvt_f32_f64(vreinterpretq_f64_m128d(a)); + return vreinterpretq_m128_f32(vcombine_f32(tmp, vdup_n_f32(0))); +#else + float a0 = (float) ((double *) &a)[0]; + float a1 = (float) ((double *) &a)[1]; + return _mm_set_ps(0, 0, a1, a0); +#endif +} + +// Convert packed signed 32-bit integers in a to packed double-precision +// (64-bit) floating-point elements, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi32_pd +FORCE_INLINE __m128d _mm_cvtpi32_pd(__m64 a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vcvtq_f64_s64(vmovl_s32(vreinterpret_s32_m64(a)))); +#else + double a0 = (double) vget_lane_s32(vreinterpret_s32_m64(a), 0); + double a1 = (double) vget_lane_s32(vreinterpret_s32_m64(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_epi32 +// *NOTE*. The default rounding mode on SSE is 'round to even', which ARMv7-A +// does not support! It is supported on ARMv8-A however. +FORCE_INLINE __m128i _mm_cvtps_epi32(__m128 a) +{ +#if defined(__ARM_FEATURE_FRINT) + return vreinterpretq_m128i_s32(vcvtq_s32_f32(vrnd32xq_f32(a))); +#elif (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + switch (_MM_GET_ROUNDING_MODE()) { + case _MM_ROUND_NEAREST: + return vreinterpretq_m128i_s32(vcvtnq_s32_f32(a)); + case _MM_ROUND_DOWN: + return vreinterpretq_m128i_s32(vcvtmq_s32_f32(a)); + case _MM_ROUND_UP: + return vreinterpretq_m128i_s32(vcvtpq_s32_f32(a)); + default: // _MM_ROUND_TOWARD_ZERO + return vreinterpretq_m128i_s32(vcvtq_s32_f32(a)); + } +#else + float *f = (float *) &a; + switch (_MM_GET_ROUNDING_MODE()) { + case _MM_ROUND_NEAREST: { + uint32x4_t signmask = vdupq_n_u32(0x80000000); + float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), + vdupq_n_f32(0.5f)); /* +/- 0.5 */ + int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( + vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ + int32x4_t r_trunc = vcvtq_s32_f32( + vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ + int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( + vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ + int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), + vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ + float32x4_t delta = vsubq_f32( + vreinterpretq_f32_m128(a), + vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ + uint32x4_t is_delta_half = + vceqq_f32(delta, half); /* delta == +/- 0.5 */ + return vreinterpretq_m128i_s32( + vbslq_s32(is_delta_half, r_even, r_normal)); + } + case _MM_ROUND_DOWN: + return _mm_set_epi32(floorf(f[3]), floorf(f[2]), floorf(f[1]), + floorf(f[0])); + case _MM_ROUND_UP: + return _mm_set_epi32(ceilf(f[3]), ceilf(f[2]), ceilf(f[1]), + ceilf(f[0])); + default: // _MM_ROUND_TOWARD_ZERO + return _mm_set_epi32((int32_t) f[3], (int32_t) f[2], (int32_t) f[1], + (int32_t) f[0]); + } +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed double-precision (64-bit) floating-point elements, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pd +FORCE_INLINE __m128d _mm_cvtps_pd(__m128 a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vcvt_f64_f32(vget_low_f32(vreinterpretq_f32_m128(a)))); +#else + double a0 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + double a1 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Copy the lower double-precision (64-bit) floating-point element of a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_f64 +FORCE_INLINE double _mm_cvtsd_f64(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return (double) vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0); +#else + return ((double *) &a)[0]; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_si32 +FORCE_INLINE int32_t _mm_cvtsd_si32(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return (int32_t) vgetq_lane_f64(vrndiq_f64(vreinterpretq_f64_m128d(a)), 0); +#else + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double ret = ((double *) &rnd)[0]; + return (int32_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_si64 +FORCE_INLINE int64_t _mm_cvtsd_si64(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return (int64_t) vgetq_lane_f64(vrndiq_f64(vreinterpretq_f64_m128d(a)), 0); +#else + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double ret = ((double *) &rnd)[0]; + return (int64_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_si64x +#define _mm_cvtsd_si64x _mm_cvtsd_si64 + +// Convert the lower double-precision (64-bit) floating-point element in b to a +// single-precision (32-bit) floating-point element, store the result in the +// lower element of dst, and copy the upper 3 packed elements from a to the +// upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_ss +FORCE_INLINE __m128 _mm_cvtsd_ss(__m128 a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32(vsetq_lane_f32( + vget_lane_f32(vcvt_f32_f64(vreinterpretq_f64_m128d(b)), 0), + vreinterpretq_f32_m128(a), 0)); +#else + return vreinterpretq_m128_f32(vsetq_lane_f32((float) ((double *) &b)[0], + vreinterpretq_f32_m128(a), 0)); +#endif +} + +// Copy the lower 32-bit integer in a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si32 +FORCE_INLINE int _mm_cvtsi128_si32(__m128i a) +{ + return vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); +} + +// Copy the lower 64-bit integer in a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si64 +FORCE_INLINE int64_t _mm_cvtsi128_si64(__m128i a) +{ + return vgetq_lane_s64(vreinterpretq_s64_m128i(a), 0); +} + +// Copy the lower 64-bit integer in a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si64x +#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) + +// Convert the signed 32-bit integer b to a double-precision (64-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi32_sd +FORCE_INLINE __m128d _mm_cvtsi32_sd(__m128d a, int32_t b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vsetq_lane_f64((double) b, vreinterpretq_f64_m128d(a), 0)); +#else + double bf = (double) b; + return vreinterpretq_m128d_s64( + vsetq_lane_s64(*(int64_t *) &bf, vreinterpretq_s64_m128d(a), 0)); +#endif +} + +// Copy the lower 64-bit integer in a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si64x +#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) + +// Copy 32-bit integer a to the lower elements of dst, and zero the upper +// elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi32_si128 +FORCE_INLINE __m128i _mm_cvtsi32_si128(int a) +{ + return vreinterpretq_m128i_s32(vsetq_lane_s32(a, vdupq_n_s32(0), 0)); +} + +// Convert the signed 64-bit integer b to a double-precision (64-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64_sd +FORCE_INLINE __m128d _mm_cvtsi64_sd(__m128d a, int64_t b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vsetq_lane_f64((double) b, vreinterpretq_f64_m128d(a), 0)); +#else + double bf = (double) b; + return vreinterpretq_m128d_s64( + vsetq_lane_s64(*(int64_t *) &bf, vreinterpretq_s64_m128d(a), 0)); +#endif +} + +// Copy 64-bit integer a to the lower element of dst, and zero the upper +// element. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64_si128 +FORCE_INLINE __m128i _mm_cvtsi64_si128(int64_t a) +{ + return vreinterpretq_m128i_s64(vsetq_lane_s64(a, vdupq_n_s64(0), 0)); +} + +// Copy 64-bit integer a to the lower element of dst, and zero the upper +// element. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64x_si128 +#define _mm_cvtsi64x_si128(a) _mm_cvtsi64_si128(a) + +// Convert the signed 64-bit integer b to a double-precision (64-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64x_sd +#define _mm_cvtsi64x_sd(a, b) _mm_cvtsi64_sd(a, b) + +// Convert the lower single-precision (32-bit) floating-point element in b to a +// double-precision (64-bit) floating-point element, store the result in the +// lower element of dst, and copy the upper element from a to the upper element +// of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_sd +FORCE_INLINE __m128d _mm_cvtss_sd(__m128d a, __m128 b) +{ + double d = (double) vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vsetq_lane_f64(d, vreinterpretq_f64_m128d(a), 0)); +#else + return vreinterpretq_m128d_s64( + vsetq_lane_s64(*(int64_t *) &d, vreinterpretq_s64_m128d(a), 0)); +#endif +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttpd_epi32 +FORCE_INLINE __m128i _mm_cvttpd_epi32(__m128d a) +{ + double a0 = ((double *) &a)[0]; + double a1 = ((double *) &a)[1]; + return _mm_set_epi32(0, 0, (int32_t) a1, (int32_t) a0); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttpd_pi32 +FORCE_INLINE __m64 _mm_cvttpd_pi32(__m128d a) +{ + double a0 = ((double *) &a)[0]; + double a1 = ((double *) &a)[1]; + int32_t ALIGN_STRUCT(16) data[2] = {(int32_t) a0, (int32_t) a1}; + return vreinterpret_m64_s32(vld1_s32(data)); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttps_epi32 +FORCE_INLINE __m128i _mm_cvttps_epi32(__m128 a) +{ + return vreinterpretq_m128i_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a))); +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttsd_si32 +FORCE_INLINE int32_t _mm_cvttsd_si32(__m128d a) +{ + double ret = *((double *) &a); + return (int32_t) ret; +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttsd_si64 +FORCE_INLINE int64_t _mm_cvttsd_si64(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vgetq_lane_s64(vcvtq_s64_f64(vreinterpretq_f64_m128d(a)), 0); +#else + double ret = *((double *) &a); + return (int64_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttsd_si64x +#define _mm_cvttsd_si64x(a) _mm_cvttsd_si64(a) + +// Divide packed double-precision (64-bit) floating-point elements in a by +// packed elements in b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_pd +FORCE_INLINE __m128d _mm_div_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vdivq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] / db[0]; + c[1] = da[1] / db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Divide the lower double-precision (64-bit) floating-point element in a by the +// lower double-precision (64-bit) floating-point element in b, store the result +// in the lower element of dst, and copy the upper element from a to the upper +// element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_sd +FORCE_INLINE __m128d _mm_div_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + float64x2_t tmp = + vdivq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)); + return vreinterpretq_m128d_f64( + vsetq_lane_f64(vgetq_lane_f64(vreinterpretq_f64_m128d(a), 1), tmp, 1)); +#else + return _mm_move_sd(a, _mm_div_pd(a, b)); +#endif +} + +// Extract a 16-bit integer from a, selected with imm8, and store the result in +// the lower element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi16 +// FORCE_INLINE int _mm_extract_epi16(__m128i a, __constrange(0,8) int imm) +#define _mm_extract_epi16(a, imm) \ + vgetq_lane_u16(vreinterpretq_u16_m128i(a), (imm)) + +// Copy a to dst, and insert the 16-bit integer i into dst at the location +// specified by imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi16 +// FORCE_INLINE __m128i _mm_insert_epi16(__m128i a, int b, +// __constrange(0,8) int imm) +#define _mm_insert_epi16(a, b, imm) \ + vreinterpretq_m128i_s16( \ + vsetq_lane_s16((b), vreinterpretq_s16_m128i(a), (imm))) + +// Load 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from memory into dst. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_pd +FORCE_INLINE __m128d _mm_load_pd(const double *p) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vld1q_f64(p)); +#else + const float *fp = (const float *) p; + float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], fp[2], fp[3]}; + return vreinterpretq_m128d_f32(vld1q_f32(data)); +#endif +} + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_pd1 +#define _mm_load_pd1 _mm_load1_pd + +// Load a double-precision (64-bit) floating-point element from memory into the +// lower of dst, and zero the upper element. mem_addr does not need to be +// aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_sd +FORCE_INLINE __m128d _mm_load_sd(const double *p) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vsetq_lane_f64(*p, vdupq_n_f64(0), 0)); +#else + const float *fp = (const float *) p; + float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], 0, 0}; + return vreinterpretq_m128d_f32(vld1q_f32(data)); +#endif +} + +// Load 128-bits of integer data from memory into dst. mem_addr must be aligned +// on a 16-byte boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_si128 +FORCE_INLINE __m128i _mm_load_si128(const __m128i *p) +{ + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); +} + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load1_pd +FORCE_INLINE __m128d _mm_load1_pd(const double *p) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vld1q_dup_f64(p)); +#else + return vreinterpretq_m128d_s64(vdupq_n_s64(*(const int64_t *) p)); +#endif +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// upper element of dst, and copy the lower element from a to dst. mem_addr does +// not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadh_pd +FORCE_INLINE __m128d _mm_loadh_pd(__m128d a, const double *p) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vcombine_f64(vget_low_f64(vreinterpretq_f64_m128d(a)), vld1_f64(p))); +#else + return vreinterpretq_m128d_f32(vcombine_f32( + vget_low_f32(vreinterpretq_f32_m128d(a)), vld1_f32((const float *) p))); +#endif +} + +// Load 64-bit integer from memory into the first element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadl_epi64 +FORCE_INLINE __m128i _mm_loadl_epi64(__m128i const *p) +{ + /* Load the lower 64 bits of the value pointed to by p into the + * lower 64 bits of the result, zeroing the upper 64 bits of the result. + */ + return vreinterpretq_m128i_s32( + vcombine_s32(vld1_s32((int32_t const *) p), vcreate_s32(0))); +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// lower element of dst, and copy the upper element from a to dst. mem_addr does +// not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadl_pd +FORCE_INLINE __m128d _mm_loadl_pd(__m128d a, const double *p) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vcombine_f64(vld1_f64(p), vget_high_f64(vreinterpretq_f64_m128d(a)))); +#else + return vreinterpretq_m128d_f32( + vcombine_f32(vld1_f32((const float *) p), + vget_high_f32(vreinterpretq_f32_m128d(a)))); +#endif +} + +// Load 2 double-precision (64-bit) floating-point elements from memory into dst +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadr_pd +FORCE_INLINE __m128d _mm_loadr_pd(const double *p) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + float64x2_t v = vld1q_f64(p); + return vreinterpretq_m128d_f64(vextq_f64(v, v, 1)); +#else + int64x2_t v = vld1q_s64((const int64_t *) p); + return vreinterpretq_m128d_s64(vextq_s64(v, v, 1)); +#endif +} + +// Loads two double-precision from unaligned memory, floating-point values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_pd +FORCE_INLINE __m128d _mm_loadu_pd(const double *p) +{ + return _mm_load_pd(p); +} + +// Load 128-bits of integer data from memory into dst. mem_addr does not need to +// be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si128 +FORCE_INLINE __m128i _mm_loadu_si128(const __m128i *p) +{ + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); +} + +// Load unaligned 32-bit integer from memory into the first element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si32 +FORCE_INLINE __m128i _mm_loadu_si32(const void *p) +{ + return vreinterpretq_m128i_s32( + vsetq_lane_s32(*(const int32_t *) p, vdupq_n_s32(0), 0)); +} + +// Multiply packed signed 16-bit integers in a and b, producing intermediate +// signed 32-bit integers. Horizontally add adjacent pairs of intermediate +// 32-bit integers, and pack the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_madd_epi16 +FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) +{ + int32x4_t low = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), + vget_low_s16(vreinterpretq_s16_m128i(b))); +#if defined(__aarch64__) || defined(_M_ARM64) + int32x4_t high = + vmull_high_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b)); + + return vreinterpretq_m128i_s32(vpaddq_s32(low, high)); +#else + int32x4_t high = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), + vget_high_s16(vreinterpretq_s16_m128i(b))); + + int32x2_t low_sum = vpadd_s32(vget_low_s32(low), vget_high_s32(low)); + int32x2_t high_sum = vpadd_s32(vget_low_s32(high), vget_high_s32(high)); + + return vreinterpretq_m128i_s32(vcombine_s32(low_sum, high_sum)); +#endif +} + +// Conditionally store 8-bit integer elements from a into memory using mask +// (elements are not stored when the highest bit is not set in the corresponding +// element) and a non-temporal memory hint. mem_addr does not need to be aligned +// on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maskmoveu_si128 +FORCE_INLINE void _mm_maskmoveu_si128(__m128i a, __m128i mask, char *mem_addr) +{ + int8x16_t shr_mask = vshrq_n_s8(vreinterpretq_s8_m128i(mask), 7); + __m128 b = _mm_load_ps((const float *) mem_addr); + int8x16_t masked = + vbslq_s8(vreinterpretq_u8_s8(shr_mask), vreinterpretq_s8_m128i(a), + vreinterpretq_s8_m128(b)); + vst1q_s8((int8_t *) mem_addr, masked); +} + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epi16 +FORCE_INLINE __m128i _mm_max_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vmaxq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu8 +FORCE_INLINE __m128i _mm_max_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vmaxq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b, +// and store packed maximum values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_pd +FORCE_INLINE __m128d _mm_max_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) +#if SSE2NEON_PRECISE_MINMAX + float64x2_t _a = vreinterpretq_f64_m128d(a); + float64x2_t _b = vreinterpretq_f64_m128d(b); + return vreinterpretq_m128d_f64(vbslq_f64(vcgtq_f64(_a, _b), _a, _b)); +#else + return vreinterpretq_m128d_f64( + vmaxq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#endif +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) > (*(double *) &b0) ? a0 : b0; + d[1] = (*(double *) &a1) > (*(double *) &b1) ? a1 : b1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b, store the maximum value in the lower element of dst, and copy the upper +// element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_sd +FORCE_INLINE __m128d _mm_max_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_max_pd(a, b)); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2] = {da[0] > db[0] ? da[0] : db[0], da[1]}; + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) c)); +#endif +} + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epi16 +FORCE_INLINE __m128i _mm_min_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vminq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epu8 +FORCE_INLINE __m128i _mm_min_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vminq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b, +// and store packed minimum values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_pd +FORCE_INLINE __m128d _mm_min_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) +#if SSE2NEON_PRECISE_MINMAX + float64x2_t _a = vreinterpretq_f64_m128d(a); + float64x2_t _b = vreinterpretq_f64_m128d(b); + return vreinterpretq_m128d_f64(vbslq_f64(vcltq_f64(_a, _b), _a, _b)); +#else + return vreinterpretq_m128d_f64( + vminq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#endif +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) < (*(double *) &b0) ? a0 : b0; + d[1] = (*(double *) &a1) < (*(double *) &b1) ? a1 : b1; + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b, store the minimum value in the lower element of dst, and copy the upper +// element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_sd +FORCE_INLINE __m128d _mm_min_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_min_pd(a, b)); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2] = {da[0] < db[0] ? da[0] : db[0], da[1]}; + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) c)); +#endif +} + +// Copy the lower 64-bit integer in a to the lower element of dst, and zero the +// upper element. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_move_epi64 +FORCE_INLINE __m128i _mm_move_epi64(__m128i a) +{ + return vreinterpretq_m128i_s64( + vsetq_lane_s64(0, vreinterpretq_s64_m128i(a), 1)); +} + +// Move the lower double-precision (64-bit) floating-point element from b to the +// lower element of dst, and copy the upper element from a to the upper element +// of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_move_sd +FORCE_INLINE __m128d _mm_move_sd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_f32( + vcombine_f32(vget_low_f32(vreinterpretq_f32_m128d(b)), + vget_high_f32(vreinterpretq_f32_m128d(a)))); +} + +// Create mask from the most significant bit of each 8-bit element in a, and +// store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_epi8 +FORCE_INLINE int _mm_movemask_epi8(__m128i a) +{ + // Use increasingly wide shifts+adds to collect the sign bits + // together. + // Since the widening shifts would be rather confusing to follow in little + // endian, everything will be illustrated in big endian order instead. This + // has a different result - the bits would actually be reversed on a big + // endian machine. + + // Starting input (only half the elements are shown): + // 89 ff 1d c0 00 10 99 33 + uint8x16_t input = vreinterpretq_u8_m128i(a); + + // Shift out everything but the sign bits with an unsigned shift right. + // + // Bytes of the vector:: + // 89 ff 1d c0 00 10 99 33 + // \ \ \ \ \ \ \ \ high_bits = (uint16x4_t)(input >> 7) + // | | | | | | | | + // 01 01 00 01 00 00 01 00 + // + // Bits of first important lane(s): + // 10001001 (89) + // \______ + // | + // 00000001 (01) + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(input, 7)); + + // Merge the even lanes together with a 16-bit unsigned shift right + add. + // 'xx' represents garbage data which will be ignored in the final result. + // In the important bytes, the add functions like a binary OR. + // + // 01 01 00 01 00 00 01 00 + // \_ | \_ | \_ | \_ | paired16 = (uint32x4_t)(input + (input >> 7)) + // \| \| \| \| + // xx 03 xx 01 xx 00 xx 02 + // + // 00000001 00000001 (01 01) + // \_______ | + // \| + // xxxxxxxx xxxxxx11 (xx 03) + uint32x4_t paired16 = + vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + + // Repeat with a wider 32-bit shift + add. + // xx 03 xx 01 xx 00 xx 02 + // \____ | \____ | paired32 = (uint64x1_t)(paired16 + (paired16 >> + // 14)) + // \| \| + // xx xx xx 0d xx xx xx 02 + // + // 00000011 00000001 (03 01) + // \\_____ || + // '----.\|| + // xxxxxxxx xxxx1101 (xx 0d) + uint64x2_t paired32 = + vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + + // Last, an even wider 64-bit shift + add to get our result in the low 8 bit + // lanes. xx xx xx 0d xx xx xx 02 + // \_________ | paired64 = (uint8x8_t)(paired32 + (paired32 >> + // 28)) + // \| + // xx xx xx xx xx xx xx d2 + // + // 00001101 00000010 (0d 02) + // \ \___ | | + // '---. \| | + // xxxxxxxx 11010010 (xx d2) + uint8x16_t paired64 = + vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + + // Extract the low 8 bits from each 64-bit lane with 2 8-bit extracts. + // xx xx xx xx xx xx xx d2 + // || return paired64[0] + // d2 + // Note: Little endian would return the correct value 4b (01001011) instead. + return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); +} + +// Set each bit of mask dst based on the most significant bit of the +// corresponding packed double-precision (64-bit) floating-point element in a. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_pd +FORCE_INLINE int _mm_movemask_pd(__m128d a) +{ + uint64x2_t input = vreinterpretq_u64_m128d(a); + uint64x2_t high_bits = vshrq_n_u64(input, 63); + return (int) (vgetq_lane_u64(high_bits, 0) | + (vgetq_lane_u64(high_bits, 1) << 1)); +} + +// Copy the lower 64-bit integer in a to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movepi64_pi64 +FORCE_INLINE __m64 _mm_movepi64_pi64(__m128i a) +{ + return vreinterpret_m64_s64(vget_low_s64(vreinterpretq_s64_m128i(a))); +} + +// Copy the 64-bit integer a to the lower element of dst, and zero the upper +// element. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movpi64_epi64 +FORCE_INLINE __m128i _mm_movpi64_epi64(__m64 a) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vreinterpret_s64_m64(a), vdup_n_s64(0))); +} + +// Multiply the low unsigned 32-bit integers from each packed 64-bit element in +// a and b, and store the unsigned 64-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_epu32 +FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) +{ + // vmull_u32 upcasts instead of masking, so we downcast. + uint32x2_t a_lo = vmovn_u64(vreinterpretq_u64_m128i(a)); + uint32x2_t b_lo = vmovn_u64(vreinterpretq_u64_m128i(b)); + return vreinterpretq_m128i_u64(vmull_u32(a_lo, b_lo)); +} + +// Multiply packed double-precision (64-bit) floating-point elements in a and b, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_pd +FORCE_INLINE __m128d _mm_mul_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vmulq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] * db[0]; + c[1] = da[1] * db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Multiply the lower double-precision (64-bit) floating-point element in a and +// b, store the result in the lower element of dst, and copy the upper element +// from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_mul_sd +FORCE_INLINE __m128d _mm_mul_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_mul_pd(a, b)); +} + +// Multiply the low unsigned 32-bit integers from a and b, and store the +// unsigned 64-bit result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_su32 +FORCE_INLINE __m64 _mm_mul_su32(__m64 a, __m64 b) +{ + return vreinterpret_m64_u64(vget_low_u64( + vmull_u32(vreinterpret_u32_m64(a), vreinterpret_u32_m64(b)))); +} + +// Multiply the packed signed 16-bit integers in a and b, producing intermediate +// 32-bit integers, and store the high 16 bits of the intermediate integers in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhi_epi16 +FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) +{ + /* FIXME: issue with large values because of result saturation */ + // int16x8_t ret = vqdmulhq_s16(vreinterpretq_s16_m128i(a), + // vreinterpretq_s16_m128i(b)); /* =2*a*b */ return + // vreinterpretq_m128i_s16(vshrq_n_s16(ret, 1)); + int16x4_t a3210 = vget_low_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b3210 = vget_low_s16(vreinterpretq_s16_m128i(b)); + int32x4_t ab3210 = vmull_s16(a3210, b3210); /* 3333222211110000 */ + int16x4_t a7654 = vget_high_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b7654 = vget_high_s16(vreinterpretq_s16_m128i(b)); + int32x4_t ab7654 = vmull_s16(a7654, b7654); /* 7777666655554444 */ + uint16x8x2_t r = + vuzpq_u16(vreinterpretq_u16_s32(ab3210), vreinterpretq_u16_s32(ab7654)); + return vreinterpretq_m128i_u16(r.val[1]); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhi_epu16 +FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) +{ + uint16x4_t a3210 = vget_low_u16(vreinterpretq_u16_m128i(a)); + uint16x4_t b3210 = vget_low_u16(vreinterpretq_u16_m128i(b)); + uint32x4_t ab3210 = vmull_u16(a3210, b3210); +#if defined(__aarch64__) || defined(_M_ARM64) + uint32x4_t ab7654 = + vmull_high_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b)); + uint16x8_t r = vuzp2q_u16(vreinterpretq_u16_u32(ab3210), + vreinterpretq_u16_u32(ab7654)); + return vreinterpretq_m128i_u16(r); +#else + uint16x4_t a7654 = vget_high_u16(vreinterpretq_u16_m128i(a)); + uint16x4_t b7654 = vget_high_u16(vreinterpretq_u16_m128i(b)); + uint32x4_t ab7654 = vmull_u16(a7654, b7654); + uint16x8x2_t r = + vuzpq_u16(vreinterpretq_u16_u32(ab3210), vreinterpretq_u16_u32(ab7654)); + return vreinterpretq_m128i_u16(r.val[1]); +#endif +} + +// Multiply the packed 16-bit integers in a and b, producing intermediate 32-bit +// integers, and store the low 16 bits of the intermediate integers in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mullo_epi16 +FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vmulq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compute the bitwise OR of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_or_pd +FORCE_INLINE __m128d _mm_or_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + vorrq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Compute the bitwise OR of 128 bits (representing integer data) in a and b, +// and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_or_si128 +FORCE_INLINE __m128i _mm_or_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vorrq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Convert packed signed 16-bit integers from a and b to packed 8-bit integers +// using signed saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi16 +FORCE_INLINE __m128i _mm_packs_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vcombine_s8(vqmovn_s16(vreinterpretq_s16_m128i(a)), + vqmovn_s16(vreinterpretq_s16_m128i(b)))); +} + +// Convert packed signed 32-bit integers from a and b to packed 16-bit integers +// using signed saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi32 +FORCE_INLINE __m128i _mm_packs_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vcombine_s16(vqmovn_s32(vreinterpretq_s32_m128i(a)), + vqmovn_s32(vreinterpretq_s32_m128i(b)))); +} + +// Convert packed signed 16-bit integers from a and b to packed 8-bit integers +// using unsigned saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi16 +FORCE_INLINE __m128i _mm_packus_epi16(const __m128i a, const __m128i b) +{ + return vreinterpretq_m128i_u8( + vcombine_u8(vqmovun_s16(vreinterpretq_s16_m128i(a)), + vqmovun_s16(vreinterpretq_s16_m128i(b)))); +} + +// Pause the processor. This is typically used in spin-wait loops and depending +// on the x86 processor typical values are in the 40-100 cycle range. The +// 'yield' instruction isn't a good fit because it's effectively a nop on most +// Arm cores. Experience with several databases has shown has shown an 'isb' is +// a reasonable approximation. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_pause +FORCE_INLINE void _mm_pause(void) +{ +#if defined(_MSC_VER) + __isb(_ARM64_BARRIER_SY); +#else + __asm__ __volatile__("isb\n"); +#endif +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce two +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of 64-bit elements in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8 +FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) +{ + uint16x8_t t = vpaddlq_u8(vabdq_u8((uint8x16_t) a, (uint8x16_t) b)); + return vreinterpretq_m128i_u64(vpaddlq_u32(vpaddlq_u16(t))); +} + +// Set packed 16-bit integers in dst with the supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi16 +FORCE_INLINE __m128i _mm_set_epi16(short i7, + short i6, + short i5, + short i4, + short i3, + short i2, + short i1, + short i0) +{ + int16_t ALIGN_STRUCT(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7}; + return vreinterpretq_m128i_s16(vld1q_s16(data)); +} + +// Set packed 32-bit integers in dst with the supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi32 +FORCE_INLINE __m128i _mm_set_epi32(int i3, int i2, int i1, int i0) +{ + int32_t ALIGN_STRUCT(16) data[4] = {i0, i1, i2, i3}; + return vreinterpretq_m128i_s32(vld1q_s32(data)); +} + +// Set packed 64-bit integers in dst with the supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi64 +FORCE_INLINE __m128i _mm_set_epi64(__m64 i1, __m64 i2) +{ + return _mm_set_epi64x(vget_lane_s64(i1, 0), vget_lane_s64(i2, 0)); +} + +// Set packed 64-bit integers in dst with the supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi64x +FORCE_INLINE __m128i _mm_set_epi64x(int64_t i1, int64_t i2) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vcreate_s64(i2), vcreate_s64(i1))); +} + +// Set packed 8-bit integers in dst with the supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi8 +FORCE_INLINE __m128i _mm_set_epi8(signed char b15, + signed char b14, + signed char b13, + signed char b12, + signed char b11, + signed char b10, + signed char b9, + signed char b8, + signed char b7, + signed char b6, + signed char b5, + signed char b4, + signed char b3, + signed char b2, + signed char b1, + signed char b0) +{ + int8_t ALIGN_STRUCT(16) + data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, + (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, + (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, + (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; + return (__m128i) vld1q_s8(data); +} + +// Set packed double-precision (64-bit) floating-point elements in dst with the +// supplied values. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_pd +FORCE_INLINE __m128d _mm_set_pd(double e1, double e0) +{ + double ALIGN_STRUCT(16) data[2] = {e0, e1}; +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vld1q_f64((float64_t *) data)); +#else + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) data)); +#endif +} + +// Broadcast double-precision (64-bit) floating-point value a to all elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_pd1 +#define _mm_set_pd1 _mm_set1_pd + +// Copy double-precision (64-bit) floating-point element a to the lower element +// of dst, and zero the upper element. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_sd +FORCE_INLINE __m128d _mm_set_sd(double a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vsetq_lane_f64(a, vdupq_n_f64(0), 0)); +#else + return _mm_set_pd(0, a); +#endif +} + +// Broadcast 16-bit integer a to all elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi16 +FORCE_INLINE __m128i _mm_set1_epi16(short w) +{ + return vreinterpretq_m128i_s16(vdupq_n_s16(w)); +} + +// Broadcast 32-bit integer a to all elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi32 +FORCE_INLINE __m128i _mm_set1_epi32(int _i) +{ + return vreinterpretq_m128i_s32(vdupq_n_s32(_i)); +} + +// Broadcast 64-bit integer a to all elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi64 +FORCE_INLINE __m128i _mm_set1_epi64(__m64 _i) +{ + return vreinterpretq_m128i_s64(vdupq_lane_s64(_i, 0)); +} + +// Broadcast 64-bit integer a to all elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi64x +FORCE_INLINE __m128i _mm_set1_epi64x(int64_t _i) +{ + return vreinterpretq_m128i_s64(vdupq_n_s64(_i)); +} + +// Broadcast 8-bit integer a to all elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi8 +FORCE_INLINE __m128i _mm_set1_epi8(signed char w) +{ + return vreinterpretq_m128i_s8(vdupq_n_s8(w)); +} + +// Broadcast double-precision (64-bit) floating-point value a to all elements of +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_pd +FORCE_INLINE __m128d _mm_set1_pd(double d) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vdupq_n_f64(d)); +#else + return vreinterpretq_m128d_s64(vdupq_n_s64(*(int64_t *) &d)); +#endif +} + +// Set packed 16-bit integers in dst with the supplied values in reverse order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi16 +FORCE_INLINE __m128i _mm_setr_epi16(short w0, + short w1, + short w2, + short w3, + short w4, + short w5, + short w6, + short w7) +{ + int16_t ALIGN_STRUCT(16) data[8] = {w0, w1, w2, w3, w4, w5, w6, w7}; + return vreinterpretq_m128i_s16(vld1q_s16((int16_t *) data)); +} + +// Set packed 32-bit integers in dst with the supplied values in reverse order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi32 +FORCE_INLINE __m128i _mm_setr_epi32(int i3, int i2, int i1, int i0) +{ + int32_t ALIGN_STRUCT(16) data[4] = {i3, i2, i1, i0}; + return vreinterpretq_m128i_s32(vld1q_s32(data)); +} + +// Set packed 64-bit integers in dst with the supplied values in reverse order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi64 +FORCE_INLINE __m128i _mm_setr_epi64(__m64 e1, __m64 e0) +{ + return vreinterpretq_m128i_s64(vcombine_s64(e1, e0)); +} + +// Set packed 8-bit integers in dst with the supplied values in reverse order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi8 +FORCE_INLINE __m128i _mm_setr_epi8(signed char b0, + signed char b1, + signed char b2, + signed char b3, + signed char b4, + signed char b5, + signed char b6, + signed char b7, + signed char b8, + signed char b9, + signed char b10, + signed char b11, + signed char b12, + signed char b13, + signed char b14, + signed char b15) +{ + int8_t ALIGN_STRUCT(16) + data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, + (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, + (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, + (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; + return (__m128i) vld1q_s8(data); +} + +// Set packed double-precision (64-bit) floating-point elements in dst with the +// supplied values in reverse order. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_pd +FORCE_INLINE __m128d _mm_setr_pd(double e1, double e0) +{ + return _mm_set_pd(e0, e1); +} + +// Return vector of type __m128d with all elements set to zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setzero_pd +FORCE_INLINE __m128d _mm_setzero_pd(void) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vdupq_n_f64(0)); +#else + return vreinterpretq_m128d_f32(vdupq_n_f32(0)); +#endif +} + +// Return vector of type __m128i with all elements set to zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setzero_si128 +FORCE_INLINE __m128i _mm_setzero_si128(void) +{ + return vreinterpretq_m128i_s32(vdupq_n_s32(0)); +} + +// Shuffle 32-bit integers in a using the control in imm8, and store the results +// in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi32 +// FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i a, +// __constrange(0,255) int imm) +#if defined(_sse2neon_shuffle) +#define _mm_shuffle_epi32(a, imm) \ + __extension__({ \ + int32x4_t _input = vreinterpretq_s32_m128i(a); \ + int32x4_t _shuf = \ + vshuffleq_s32(_input, _input, (imm) & (0x3), ((imm) >> 2) & 0x3, \ + ((imm) >> 4) & 0x3, ((imm) >> 6) & 0x3); \ + vreinterpretq_m128i_s32(_shuf); \ + }) +#else // generic +#define _mm_shuffle_epi32(a, imm) \ + _sse2neon_define1( \ + __m128i, a, __m128i ret; switch (imm) { \ + case _MM_SHUFFLE(1, 0, 3, 2): \ + ret = _mm_shuffle_epi_1032(_a); \ + break; \ + case _MM_SHUFFLE(2, 3, 0, 1): \ + ret = _mm_shuffle_epi_2301(_a); \ + break; \ + case _MM_SHUFFLE(0, 3, 2, 1): \ + ret = _mm_shuffle_epi_0321(_a); \ + break; \ + case _MM_SHUFFLE(2, 1, 0, 3): \ + ret = _mm_shuffle_epi_2103(_a); \ + break; \ + case _MM_SHUFFLE(1, 0, 1, 0): \ + ret = _mm_shuffle_epi_1010(_a); \ + break; \ + case _MM_SHUFFLE(1, 0, 0, 1): \ + ret = _mm_shuffle_epi_1001(_a); \ + break; \ + case _MM_SHUFFLE(0, 1, 0, 1): \ + ret = _mm_shuffle_epi_0101(_a); \ + break; \ + case _MM_SHUFFLE(2, 2, 1, 1): \ + ret = _mm_shuffle_epi_2211(_a); \ + break; \ + case _MM_SHUFFLE(0, 1, 2, 2): \ + ret = _mm_shuffle_epi_0122(_a); \ + break; \ + case _MM_SHUFFLE(3, 3, 3, 2): \ + ret = _mm_shuffle_epi_3332(_a); \ + break; \ + case _MM_SHUFFLE(0, 0, 0, 0): \ + ret = _mm_shuffle_epi32_splat(_a, 0); \ + break; \ + case _MM_SHUFFLE(1, 1, 1, 1): \ + ret = _mm_shuffle_epi32_splat(_a, 1); \ + break; \ + case _MM_SHUFFLE(2, 2, 2, 2): \ + ret = _mm_shuffle_epi32_splat(_a, 2); \ + break; \ + case _MM_SHUFFLE(3, 3, 3, 3): \ + ret = _mm_shuffle_epi32_splat(_a, 3); \ + break; \ + default: \ + ret = _mm_shuffle_epi32_default(_a, (imm)); \ + break; \ + } _sse2neon_return(ret);) +#endif + +// Shuffle double-precision (64-bit) floating-point elements using the control +// in imm8, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_pd +#ifdef _sse2neon_shuffle +#define _mm_shuffle_pd(a, b, imm8) \ + vreinterpretq_m128d_s64( \ + vshuffleq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b), \ + imm8 & 0x1, ((imm8 & 0x2) >> 1) + 2)) +#else +#define _mm_shuffle_pd(a, b, imm8) \ + _mm_castsi128_pd(_mm_set_epi64x( \ + vgetq_lane_s64(vreinterpretq_s64_m128d(b), (imm8 & 0x2) >> 1), \ + vgetq_lane_s64(vreinterpretq_s64_m128d(a), imm8 & 0x1))) +#endif + +// FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i a, +// __constrange(0,255) int imm) +#if defined(_sse2neon_shuffle) +#define _mm_shufflehi_epi16(a, imm) \ + __extension__({ \ + int16x8_t _input = vreinterpretq_s16_m128i(a); \ + int16x8_t _shuf = \ + vshuffleq_s16(_input, _input, 0, 1, 2, 3, ((imm) & (0x3)) + 4, \ + (((imm) >> 2) & 0x3) + 4, (((imm) >> 4) & 0x3) + 4, \ + (((imm) >> 6) & 0x3) + 4); \ + vreinterpretq_m128i_s16(_shuf); \ + }) +#else // generic +#define _mm_shufflehi_epi16(a, imm) _mm_shufflehi_epi16_function((a), (imm)) +#endif + +// FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i a, +// __constrange(0,255) int imm) +#if defined(_sse2neon_shuffle) +#define _mm_shufflelo_epi16(a, imm) \ + __extension__({ \ + int16x8_t _input = vreinterpretq_s16_m128i(a); \ + int16x8_t _shuf = vshuffleq_s16( \ + _input, _input, ((imm) & (0x3)), (((imm) >> 2) & 0x3), \ + (((imm) >> 4) & 0x3), (((imm) >> 6) & 0x3), 4, 5, 6, 7); \ + vreinterpretq_m128i_s16(_shuf); \ + }) +#else // generic +#define _mm_shufflelo_epi16(a, imm) _mm_shufflelo_epi16_function((a), (imm)) +#endif + +// Shift packed 16-bit integers in a left by count while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sll_epi16 +FORCE_INLINE __m128i _mm_sll_epi16(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~15)) + return _mm_setzero_si128(); + + int16x8_t vc = vdupq_n_s16((int16_t) c); + return vreinterpretq_m128i_s16(vshlq_s16(vreinterpretq_s16_m128i(a), vc)); +} + +// Shift packed 32-bit integers in a left by count while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sll_epi32 +FORCE_INLINE __m128i _mm_sll_epi32(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~31)) + return _mm_setzero_si128(); + + int32x4_t vc = vdupq_n_s32((int32_t) c); + return vreinterpretq_m128i_s32(vshlq_s32(vreinterpretq_s32_m128i(a), vc)); +} + +// Shift packed 64-bit integers in a left by count while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sll_epi64 +FORCE_INLINE __m128i _mm_sll_epi64(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~63)) + return _mm_setzero_si128(); + + int64x2_t vc = vdupq_n_s64((int64_t) c); + return vreinterpretq_m128i_s64(vshlq_s64(vreinterpretq_s64_m128i(a), vc)); +} + +// Shift packed 16-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_epi16 +FORCE_INLINE __m128i _mm_slli_epi16(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~15)) + return _mm_setzero_si128(); + return vreinterpretq_m128i_s16( + vshlq_s16(vreinterpretq_s16_m128i(a), vdupq_n_s16(imm))); +} + +// Shift packed 32-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_epi32 +FORCE_INLINE __m128i _mm_slli_epi32(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~31)) + return _mm_setzero_si128(); + return vreinterpretq_m128i_s32( + vshlq_s32(vreinterpretq_s32_m128i(a), vdupq_n_s32(imm))); +} + +// Shift packed 64-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_epi64 +FORCE_INLINE __m128i _mm_slli_epi64(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~63)) + return _mm_setzero_si128(); + return vreinterpretq_m128i_s64( + vshlq_s64(vreinterpretq_s64_m128i(a), vdupq_n_s64(imm))); +} + +// Shift a left by imm8 bytes while shifting in zeros, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_si128 +#define _mm_slli_si128(a, imm) \ + _sse2neon_define1( \ + __m128i, a, int8x16_t ret; \ + if (_sse2neon_unlikely(imm == 0)) ret = vreinterpretq_s8_m128i(_a); \ + else if (_sse2neon_unlikely((imm) & ~15)) ret = vdupq_n_s8(0); \ + else ret = vextq_s8(vdupq_n_s8(0), vreinterpretq_s8_m128i(_a), \ + ((imm <= 0 || imm > 15) ? 0 : (16 - imm))); \ + _sse2neon_return(vreinterpretq_m128i_s8(ret));) + +// Compute the square root of packed double-precision (64-bit) floating-point +// elements in a, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_pd +FORCE_INLINE __m128d _mm_sqrt_pd(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vsqrtq_f64(vreinterpretq_f64_m128d(a))); +#else + double a0 = sqrt(((double *) &a)[0]); + double a1 = sqrt(((double *) &a)[1]); + return _mm_set_pd(a1, a0); +#endif +} + +// Compute the square root of the lower double-precision (64-bit) floating-point +// element in b, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_sd +FORCE_INLINE __m128d _mm_sqrt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return _mm_move_sd(a, _mm_sqrt_pd(b)); +#else + return _mm_set_pd(((double *) &a)[1], sqrt(((double *) &b)[0])); +#endif +} + +// Shift packed 16-bit integers in a right by count while shifting in sign bits, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sra_epi16 +FORCE_INLINE __m128i _mm_sra_epi16(__m128i a, __m128i count) +{ + int64_t c = vgetq_lane_s64(count, 0); + if (_sse2neon_unlikely(c & ~15)) + return _mm_cmplt_epi16(a, _mm_setzero_si128()); + return vreinterpretq_m128i_s16( + vshlq_s16((int16x8_t) a, vdupq_n_s16((int) -c))); +} + +// Shift packed 32-bit integers in a right by count while shifting in sign bits, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sra_epi32 +FORCE_INLINE __m128i _mm_sra_epi32(__m128i a, __m128i count) +{ + int64_t c = vgetq_lane_s64(count, 0); + if (_sse2neon_unlikely(c & ~31)) + return _mm_cmplt_epi32(a, _mm_setzero_si128()); + return vreinterpretq_m128i_s32( + vshlq_s32((int32x4_t) a, vdupq_n_s32((int) -c))); +} + +// Shift packed 16-bit integers in a right by imm8 while shifting in sign +// bits, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srai_epi16 +FORCE_INLINE __m128i _mm_srai_epi16(__m128i a, int imm) +{ + const int count = (imm & ~15) ? 15 : imm; + return (__m128i) vshlq_s16((int16x8_t) a, vdupq_n_s16(-count)); +} + +// Shift packed 32-bit integers in a right by imm8 while shifting in sign bits, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srai_epi32 +// FORCE_INLINE __m128i _mm_srai_epi32(__m128i a, __constrange(0,255) int imm) +#define _mm_srai_epi32(a, imm) \ + _sse2neon_define0( \ + __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) == 0)) { \ + ret = _a; \ + } else if (_sse2neon_likely(0 < (imm) && (imm) < 32)) { \ + ret = vreinterpretq_m128i_s32( \ + vshlq_s32(vreinterpretq_s32_m128i(_a), vdupq_n_s32(-(imm)))); \ + } else { \ + ret = vreinterpretq_m128i_s32( \ + vshrq_n_s32(vreinterpretq_s32_m128i(_a), 31)); \ + } _sse2neon_return(ret);) + +// Shift packed 16-bit integers in a right by count while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srl_epi16 +FORCE_INLINE __m128i _mm_srl_epi16(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~15)) + return _mm_setzero_si128(); + + int16x8_t vc = vdupq_n_s16(-(int16_t) c); + return vreinterpretq_m128i_u16(vshlq_u16(vreinterpretq_u16_m128i(a), vc)); +} + +// Shift packed 32-bit integers in a right by count while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srl_epi32 +FORCE_INLINE __m128i _mm_srl_epi32(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~31)) + return _mm_setzero_si128(); + + int32x4_t vc = vdupq_n_s32(-(int32_t) c); + return vreinterpretq_m128i_u32(vshlq_u32(vreinterpretq_u32_m128i(a), vc)); +} + +// Shift packed 64-bit integers in a right by count while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srl_epi64 +FORCE_INLINE __m128i _mm_srl_epi64(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~63)) + return _mm_setzero_si128(); + + int64x2_t vc = vdupq_n_s64(-(int64_t) c); + return vreinterpretq_m128i_u64(vshlq_u64(vreinterpretq_u64_m128i(a), vc)); +} + +// Shift packed 16-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_epi16 +#define _mm_srli_epi16(a, imm) \ + _sse2neon_define0( \ + __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) & ~15)) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_u16( \ + vshlq_u16(vreinterpretq_u16_m128i(_a), vdupq_n_s16(-(imm)))); \ + } _sse2neon_return(ret);) + +// Shift packed 32-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_epi32 +// FORCE_INLINE __m128i _mm_srli_epi32(__m128i a, __constrange(0,255) int imm) +#define _mm_srli_epi32(a, imm) \ + _sse2neon_define0( \ + __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) & ~31)) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_u32( \ + vshlq_u32(vreinterpretq_u32_m128i(_a), vdupq_n_s32(-(imm)))); \ + } _sse2neon_return(ret);) + +// Shift packed 64-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_epi64 +#define _mm_srli_epi64(a, imm) \ + _sse2neon_define0( \ + __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) & ~63)) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_u64( \ + vshlq_u64(vreinterpretq_u64_m128i(_a), vdupq_n_s64(-(imm)))); \ + } _sse2neon_return(ret);) + +// Shift a right by imm8 bytes while shifting in zeros, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_si128 +#define _mm_srli_si128(a, imm) \ + _sse2neon_define1( \ + __m128i, a, int8x16_t ret; \ + if (_sse2neon_unlikely((imm) & ~15)) ret = vdupq_n_s8(0); \ + else ret = vextq_s8(vreinterpretq_s8_m128i(_a), vdupq_n_s8(0), \ + (imm > 15 ? 0 : imm)); \ + _sse2neon_return(vreinterpretq_m128i_s8(ret));) + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory. mem_addr must be aligned on a 16-byte boundary +// or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_pd +FORCE_INLINE void _mm_store_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + vst1q_f64((float64_t *) mem_addr, vreinterpretq_f64_m128d(a)); +#else + vst1q_f32((float32_t *) mem_addr, vreinterpretq_f32_m128d(a)); +#endif +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// 2 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_pd1 +FORCE_INLINE void _mm_store_pd1(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + float64x1_t a_low = vget_low_f64(vreinterpretq_f64_m128d(a)); + vst1q_f64((float64_t *) mem_addr, + vreinterpretq_f64_m128d(vcombine_f64(a_low, a_low))); +#else + float32x2_t a_low = vget_low_f32(vreinterpretq_f32_m128d(a)); + vst1q_f32((float32_t *) mem_addr, + vreinterpretq_f32_m128d(vcombine_f32(a_low, a_low))); +#endif +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// memory. mem_addr does not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_store_sd +FORCE_INLINE void _mm_store_sd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + vst1_f64((float64_t *) mem_addr, vget_low_f64(vreinterpretq_f64_m128d(a))); +#else + vst1_u64((uint64_t *) mem_addr, vget_low_u64(vreinterpretq_u64_m128d(a))); +#endif +} + +// Store 128-bits of integer data from a into memory. mem_addr must be aligned +// on a 16-byte boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_si128 +FORCE_INLINE void _mm_store_si128(__m128i *p, __m128i a) +{ + vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// 2 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#expand=9,526,5601&text=_mm_store1_pd +#define _mm_store1_pd _mm_store_pd1 + +// Store the upper double-precision (64-bit) floating-point element from a into +// memory. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeh_pd +FORCE_INLINE void _mm_storeh_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + vst1_f64((float64_t *) mem_addr, vget_high_f64(vreinterpretq_f64_m128d(a))); +#else + vst1_f32((float32_t *) mem_addr, vget_high_f32(vreinterpretq_f32_m128d(a))); +#endif +} + +// Store 64-bit integer from the first element of a into memory. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storel_epi64 +FORCE_INLINE void _mm_storel_epi64(__m128i *a, __m128i b) +{ + vst1_u64((uint64_t *) a, vget_low_u64(vreinterpretq_u64_m128i(b))); +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// memory. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storel_pd +FORCE_INLINE void _mm_storel_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + vst1_f64((float64_t *) mem_addr, vget_low_f64(vreinterpretq_f64_m128d(a))); +#else + vst1_f32((float32_t *) mem_addr, vget_low_f32(vreinterpretq_f32_m128d(a))); +#endif +} + +// Store 2 double-precision (64-bit) floating-point elements from a into memory +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storer_pd +FORCE_INLINE void _mm_storer_pd(double *mem_addr, __m128d a) +{ + float32x4_t f = vreinterpretq_f32_m128d(a); + _mm_store_pd(mem_addr, vreinterpretq_m128d_f32(vextq_f32(f, f, 2))); +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory. mem_addr does not need to be aligned on any +// particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_pd +FORCE_INLINE void _mm_storeu_pd(double *mem_addr, __m128d a) +{ + _mm_store_pd(mem_addr, a); +} + +// Store 128-bits of integer data from a into memory. mem_addr does not need to +// be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si128 +FORCE_INLINE void _mm_storeu_si128(__m128i *p, __m128i a) +{ + vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); +} + +// Store 32-bit integer from the first element of a into memory. mem_addr does +// not need to be aligned on any particular boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si32 +FORCE_INLINE void _mm_storeu_si32(void *p, __m128i a) +{ + vst1q_lane_s32((int32_t *) p, vreinterpretq_s32_m128i(a), 0); +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory using a non-temporal memory hint. mem_addr must +// be aligned on a 16-byte boundary or a general-protection exception may be +// generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_pd +FORCE_INLINE void _mm_stream_pd(double *p, __m128d a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, (__m128d *) p); +#elif defined(__aarch64__) || defined(_M_ARM64) + vst1q_f64(p, vreinterpretq_f64_m128d(a)); +#else + vst1q_s64((int64_t *) p, vreinterpretq_s64_m128d(a)); +#endif +} + +// Store 128-bits of integer data from a into memory using a non-temporal memory +// hint. mem_addr must be aligned on a 16-byte boundary or a general-protection +// exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_si128 +FORCE_INLINE void _mm_stream_si128(__m128i *p, __m128i a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, p); +#else + vst1q_s64((int64_t *) p, vreinterpretq_s64_m128i(a)); +#endif +} + +// Store 32-bit integer a into memory using a non-temporal hint to minimize +// cache pollution. If the cache line containing address mem_addr is already in +// the cache, the cache will be updated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_si32 +FORCE_INLINE void _mm_stream_si32(int *p, int a) +{ + vst1q_lane_s32((int32_t *) p, vdupq_n_s32(a), 0); +} + +// Store 64-bit integer a into memory using a non-temporal hint to minimize +// cache pollution. If the cache line containing address mem_addr is already in +// the cache, the cache will be updated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_si64 +FORCE_INLINE void _mm_stream_si64(__int64 *p, __int64 a) +{ + vst1_s64((int64_t *) p, vdup_n_s64((int64_t) a)); +} + +// Subtract packed 16-bit integers in b from packed 16-bit integers in a, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi16 +FORCE_INLINE __m128i _mm_sub_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Subtract packed 32-bit integers in b from packed 32-bit integers in a, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi32 +FORCE_INLINE __m128i _mm_sub_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vsubq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Subtract packed 64-bit integers in b from packed 64-bit integers in a, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi64 +FORCE_INLINE __m128i _mm_sub_epi64(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s64( + vsubq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +} + +// Subtract packed 8-bit integers in b from packed 8-bit integers in a, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi8 +FORCE_INLINE __m128i _mm_sub_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Subtract packed double-precision (64-bit) floating-point elements in b from +// packed double-precision (64-bit) floating-point elements in a, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_sub_pd +FORCE_INLINE __m128d _mm_sub_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vsubq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] - db[0]; + c[1] = da[1] - db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Subtract the lower double-precision (64-bit) floating-point element in b from +// the lower double-precision (64-bit) floating-point element in a, store the +// result in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_sd +FORCE_INLINE __m128d _mm_sub_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_sub_pd(a, b)); +} + +// Subtract 64-bit integer b from 64-bit integer a, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_si64 +FORCE_INLINE __m64 _mm_sub_si64(__m64 a, __m64 b) +{ + return vreinterpret_m64_s64( + vsub_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); +} + +// Subtract packed signed 16-bit integers in b from packed 16-bit integers in a +// using saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epi16 +FORCE_INLINE __m128i _mm_subs_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vqsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Subtract packed signed 8-bit integers in b from packed 8-bit integers in a +// using saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epi8 +FORCE_INLINE __m128i _mm_subs_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vqsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Subtract packed unsigned 16-bit integers in b from packed unsigned 16-bit +// integers in a using saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epu16 +FORCE_INLINE __m128i _mm_subs_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vqsubq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Subtract packed unsigned 8-bit integers in b from packed unsigned 8-bit +// integers in a using saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epu8 +FORCE_INLINE __m128i _mm_subs_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vqsubq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +#define _mm_ucomieq_sd _mm_comieq_sd +#define _mm_ucomige_sd _mm_comige_sd +#define _mm_ucomigt_sd _mm_comigt_sd +#define _mm_ucomile_sd _mm_comile_sd +#define _mm_ucomilt_sd _mm_comilt_sd +#define _mm_ucomineq_sd _mm_comineq_sd + +// Return vector of type __m128d with undefined elements. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_undefined_pd +FORCE_INLINE __m128d _mm_undefined_pd(void) +{ +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + __m128d a; +#if defined(_MSC_VER) + a = _mm_setzero_pd(); +#endif + return a; +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +// Unpack and interleave 16-bit integers from the high half of a and b, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi16 +FORCE_INLINE __m128i _mm_unpackhi_epi16(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s16( + vzip2q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +#else + int16x4_t a1 = vget_high_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b1 = vget_high_s16(vreinterpretq_s16_m128i(b)); + int16x4x2_t result = vzip_s16(a1, b1); + return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave 32-bit integers from the high half of a and b, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi32 +FORCE_INLINE __m128i _mm_unpackhi_epi32(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s32( + vzip2q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +#else + int32x2_t a1 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t b1 = vget_high_s32(vreinterpretq_s32_m128i(b)); + int32x2x2_t result = vzip_s32(a1, b1); + return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave 64-bit integers from the high half of a and b, and +// store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi64 +FORCE_INLINE __m128i _mm_unpackhi_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s64( + vzip2q_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +#else + int64x1_t a_h = vget_high_s64(vreinterpretq_s64_m128i(a)); + int64x1_t b_h = vget_high_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vcombine_s64(a_h, b_h)); +#endif +} + +// Unpack and interleave 8-bit integers from the high half of a and b, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi8 +FORCE_INLINE __m128i _mm_unpackhi_epi8(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s8( + vzip2q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +#else + int8x8_t a1 = + vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(a))); + int8x8_t b1 = + vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(b))); + int8x8x2_t result = vzip_s8(a1, b1); + return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave double-precision (64-bit) floating-point elements from +// the high half of a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_pd +FORCE_INLINE __m128d _mm_unpackhi_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vzip2q_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + return vreinterpretq_m128d_s64( + vcombine_s64(vget_high_s64(vreinterpretq_s64_m128d(a)), + vget_high_s64(vreinterpretq_s64_m128d(b)))); +#endif +} + +// Unpack and interleave 16-bit integers from the low half of a and b, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi16 +FORCE_INLINE __m128i _mm_unpacklo_epi16(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s16( + vzip1q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +#else + int16x4_t a1 = vget_low_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b1 = vget_low_s16(vreinterpretq_s16_m128i(b)); + int16x4x2_t result = vzip_s16(a1, b1); + return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave 32-bit integers from the low half of a and b, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi32 +FORCE_INLINE __m128i _mm_unpacklo_epi32(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s32( + vzip1q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +#else + int32x2_t a1 = vget_low_s32(vreinterpretq_s32_m128i(a)); + int32x2_t b1 = vget_low_s32(vreinterpretq_s32_m128i(b)); + int32x2x2_t result = vzip_s32(a1, b1); + return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave 64-bit integers from the low half of a and b, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi64 +FORCE_INLINE __m128i _mm_unpacklo_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s64( + vzip1q_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +#else + int64x1_t a_l = vget_low_s64(vreinterpretq_s64_m128i(a)); + int64x1_t b_l = vget_low_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vcombine_s64(a_l, b_l)); +#endif +} + +// Unpack and interleave 8-bit integers from the low half of a and b, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi8 +FORCE_INLINE __m128i _mm_unpacklo_epi8(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s8( + vzip1q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +#else + int8x8_t a1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(a))); + int8x8_t b1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(b))); + int8x8x2_t result = vzip_s8(a1, b1); + return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave double-precision (64-bit) floating-point elements from +// the low half of a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_pd +FORCE_INLINE __m128d _mm_unpacklo_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vzip1q_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + return vreinterpretq_m128d_s64( + vcombine_s64(vget_low_s64(vreinterpretq_s64_m128d(a)), + vget_low_s64(vreinterpretq_s64_m128d(b)))); +#endif +} + +// Compute the bitwise XOR of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_xor_pd +FORCE_INLINE __m128d _mm_xor_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + veorq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Compute the bitwise XOR of 128 bits (representing integer data) in a and b, +// and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_xor_si128 +FORCE_INLINE __m128i _mm_xor_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + veorq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +/* SSE3 */ + +// Alternatively add and subtract packed double-precision (64-bit) +// floating-point elements in a to/from packed elements in b, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_addsub_pd +FORCE_INLINE __m128d _mm_addsub_pd(__m128d a, __m128d b) +{ + _sse2neon_const __m128d mask = _mm_set_pd(1.0f, -1.0f); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vfmaq_f64(vreinterpretq_f64_m128d(a), + vreinterpretq_f64_m128d(b), + vreinterpretq_f64_m128d(mask))); +#else + return _mm_add_pd(_mm_mul_pd(b, mask), a); +#endif +} + +// Alternatively add and subtract packed single-precision (32-bit) +// floating-point elements in a to/from packed elements in b, and store the +// results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=addsub_ps +FORCE_INLINE __m128 _mm_addsub_ps(__m128 a, __m128 b) +{ + _sse2neon_const __m128 mask = _mm_setr_ps(-1.0f, 1.0f, -1.0f, 1.0f); +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_FMA) /* VFPv4+ */ + return vreinterpretq_m128_f32(vfmaq_f32(vreinterpretq_f32_m128(a), + vreinterpretq_f32_m128(mask), + vreinterpretq_f32_m128(b))); +#else + return _mm_add_ps(_mm_mul_ps(b, mask), a); +#endif +} + +// Horizontally add adjacent pairs of double-precision (64-bit) floating-point +// elements in a and b, and pack the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_pd +FORCE_INLINE __m128d _mm_hadd_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vpaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[] = {da[0] + da[1], db[0] + db[1]}; + return vreinterpretq_m128d_u64(vld1q_u64((uint64_t *) c)); +#endif +} + +// Horizontally add adjacent pairs of single-precision (32-bit) floating-point +// elements in a and b, and pack the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_ps +FORCE_INLINE __m128 _mm_hadd_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vpaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32( + vcombine_f32(vpadd_f32(a10, a32), vpadd_f32(b10, b32))); +#endif +} + +// Horizontally subtract adjacent pairs of double-precision (64-bit) +// floating-point elements in a and b, and pack the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_pd +FORCE_INLINE __m128d _mm_hsub_pd(__m128d _a, __m128d _b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + float64x2_t a = vreinterpretq_f64_m128d(_a); + float64x2_t b = vreinterpretq_f64_m128d(_b); + return vreinterpretq_m128d_f64( + vsubq_f64(vuzp1q_f64(a, b), vuzp2q_f64(a, b))); +#else + double *da = (double *) &_a; + double *db = (double *) &_b; + double c[] = {da[0] - da[1], db[0] - db[1]}; + return vreinterpretq_m128d_u64(vld1q_u64((uint64_t *) c)); +#endif +} + +// Horizontally subtract adjacent pairs of single-precision (32-bit) +// floating-point elements in a and b, and pack the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_ps +FORCE_INLINE __m128 _mm_hsub_ps(__m128 _a, __m128 _b) +{ + float32x4_t a = vreinterpretq_f32_m128(_a); + float32x4_t b = vreinterpretq_f32_m128(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vsubq_f32(vuzp1q_f32(a, b), vuzp2q_f32(a, b))); +#else + float32x4x2_t c = vuzpq_f32(a, b); + return vreinterpretq_m128_f32(vsubq_f32(c.val[0], c.val[1])); +#endif +} + +// Load 128-bits of integer data from unaligned memory into dst. This intrinsic +// may perform better than _mm_loadu_si128 when the data crosses a cache line +// boundary. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_lddqu_si128 +#define _mm_lddqu_si128 _mm_loadu_si128 + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loaddup_pd +#define _mm_loaddup_pd _mm_load1_pd + +// Duplicate the low double-precision (64-bit) floating-point element from a, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movedup_pd +FORCE_INLINE __m128d _mm_movedup_pd(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64( + vdupq_laneq_f64(vreinterpretq_f64_m128d(a), 0)); +#else + return vreinterpretq_m128d_u64( + vdupq_n_u64(vgetq_lane_u64(vreinterpretq_u64_m128d(a), 0))); +#endif +} + +// Duplicate odd-indexed single-precision (32-bit) floating-point elements +// from a, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movehdup_ps +FORCE_INLINE __m128 _mm_movehdup_ps(__m128 a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vtrn2q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a))); +#elif defined(_sse2neon_shuffle) + return vreinterpretq_m128_f32(vshuffleq_s32( + vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 1, 1, 3, 3)); +#else + float32_t a1 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); + float32_t a3 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 3); + float ALIGN_STRUCT(16) data[4] = {a1, a1, a3, a3}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +#endif +} + +// Duplicate even-indexed single-precision (32-bit) floating-point elements +// from a, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_moveldup_ps +FORCE_INLINE __m128 _mm_moveldup_ps(__m128 a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128_f32( + vtrn1q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a))); +#elif defined(_sse2neon_shuffle) + return vreinterpretq_m128_f32(vshuffleq_s32( + vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 0, 0, 2, 2)); +#else + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32_t a2 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 2); + float ALIGN_STRUCT(16) data[4] = {a0, a0, a2, a2}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +#endif +} + +/* SSSE3 */ + +// Compute the absolute value of packed signed 16-bit integers in a, and store +// the unsigned results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_epi16 +FORCE_INLINE __m128i _mm_abs_epi16(__m128i a) +{ + return vreinterpretq_m128i_s16(vabsq_s16(vreinterpretq_s16_m128i(a))); +} + +// Compute the absolute value of packed signed 32-bit integers in a, and store +// the unsigned results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_epi32 +FORCE_INLINE __m128i _mm_abs_epi32(__m128i a) +{ + return vreinterpretq_m128i_s32(vabsq_s32(vreinterpretq_s32_m128i(a))); +} + +// Compute the absolute value of packed signed 8-bit integers in a, and store +// the unsigned results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_epi8 +FORCE_INLINE __m128i _mm_abs_epi8(__m128i a) +{ + return vreinterpretq_m128i_s8(vabsq_s8(vreinterpretq_s8_m128i(a))); +} + +// Compute the absolute value of packed signed 16-bit integers in a, and store +// the unsigned results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_pi16 +FORCE_INLINE __m64 _mm_abs_pi16(__m64 a) +{ + return vreinterpret_m64_s16(vabs_s16(vreinterpret_s16_m64(a))); +} + +// Compute the absolute value of packed signed 32-bit integers in a, and store +// the unsigned results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_pi32 +FORCE_INLINE __m64 _mm_abs_pi32(__m64 a) +{ + return vreinterpret_m64_s32(vabs_s32(vreinterpret_s32_m64(a))); +} + +// Compute the absolute value of packed signed 8-bit integers in a, and store +// the unsigned results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_pi8 +FORCE_INLINE __m64 _mm_abs_pi8(__m64 a) +{ + return vreinterpret_m64_s8(vabs_s8(vreinterpret_s8_m64(a))); +} + +// Concatenate 16-byte blocks in a and b into a 32-byte temporary result, shift +// the result right by imm8 bytes, and store the low 16 bytes in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_alignr_epi8 +#if defined(__GNUC__) && !defined(__clang__) +#define _mm_alignr_epi8(a, b, imm) \ + __extension__({ \ + uint8x16_t _a = vreinterpretq_u8_m128i(a); \ + uint8x16_t _b = vreinterpretq_u8_m128i(b); \ + __m128i ret; \ + if (_sse2neon_unlikely((imm) & ~31)) \ + ret = vreinterpretq_m128i_u8(vdupq_n_u8(0)); \ + else if (imm >= 16) \ + ret = _mm_srli_si128(a, imm >= 16 ? imm - 16 : 0); \ + else \ + ret = \ + vreinterpretq_m128i_u8(vextq_u8(_b, _a, imm < 16 ? imm : 0)); \ + ret; \ + }) + +#else +#define _mm_alignr_epi8(a, b, imm) \ + _sse2neon_define2( \ + __m128i, a, b, uint8x16_t __a = vreinterpretq_u8_m128i(_a); \ + uint8x16_t __b = vreinterpretq_u8_m128i(_b); __m128i ret; \ + if (_sse2neon_unlikely((imm) & ~31)) ret = \ + vreinterpretq_m128i_u8(vdupq_n_u8(0)); \ + else if (imm >= 16) ret = \ + _mm_srli_si128(_a, imm >= 16 ? imm - 16 : 0); \ + else ret = \ + vreinterpretq_m128i_u8(vextq_u8(__b, __a, imm < 16 ? imm : 0)); \ + _sse2neon_return(ret);) + +#endif + +// Concatenate 8-byte blocks in a and b into a 16-byte temporary result, shift +// the result right by imm8 bytes, and store the low 8 bytes in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_alignr_pi8 +#define _mm_alignr_pi8(a, b, imm) \ + _sse2neon_define2( \ + __m64, a, b, __m64 ret; if (_sse2neon_unlikely((imm) >= 16)) { \ + ret = vreinterpret_m64_s8(vdup_n_s8(0)); \ + } else { \ + uint8x8_t tmp_low; \ + uint8x8_t tmp_high; \ + if ((imm) >= 8) { \ + const int idx = (imm) -8; \ + tmp_low = vreinterpret_u8_m64(_a); \ + tmp_high = vdup_n_u8(0); \ + ret = vreinterpret_m64_u8(vext_u8(tmp_low, tmp_high, idx)); \ + } else { \ + const int idx = (imm); \ + tmp_low = vreinterpret_u8_m64(_b); \ + tmp_high = vreinterpret_u8_m64(_a); \ + ret = vreinterpret_m64_u8(vext_u8(tmp_low, tmp_high, idx)); \ + } \ + } _sse2neon_return(ret);) + +// Horizontally add adjacent pairs of 16-bit integers in a and b, and pack the +// signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_epi16 +FORCE_INLINE __m128i _mm_hadd_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s16(vpaddq_s16(a, b)); +#else + return vreinterpretq_m128i_s16( + vcombine_s16(vpadd_s16(vget_low_s16(a), vget_high_s16(a)), + vpadd_s16(vget_low_s16(b), vget_high_s16(b)))); +#endif +} + +// Horizontally add adjacent pairs of 32-bit integers in a and b, and pack the +// signed 32-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_epi32 +FORCE_INLINE __m128i _mm_hadd_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s32(vpaddq_s32(a, b)); +#else + return vreinterpretq_m128i_s32( + vcombine_s32(vpadd_s32(vget_low_s32(a), vget_high_s32(a)), + vpadd_s32(vget_low_s32(b), vget_high_s32(b)))); +#endif +} + +// Horizontally add adjacent pairs of 16-bit integers in a and b, and pack the +// signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_pi16 +FORCE_INLINE __m64 _mm_hadd_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vpadd_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Horizontally add adjacent pairs of 32-bit integers in a and b, and pack the +// signed 32-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_pi32 +FORCE_INLINE __m64 _mm_hadd_pi32(__m64 a, __m64 b) +{ + return vreinterpret_m64_s32( + vpadd_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b))); +} + +// Horizontally add adjacent pairs of signed 16-bit integers in a and b using +// saturation, and pack the signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadds_epi16 +FORCE_INLINE __m128i _mm_hadds_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + return vreinterpretq_s64_s16( + vqaddq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + // Interleave using vshrn/vmovn + // [a0|a2|a4|a6|b0|b2|b4|b6] + // [a1|a3|a5|a7|b1|b3|b5|b7] + int16x8_t ab0246 = vcombine_s16(vmovn_s32(a), vmovn_s32(b)); + int16x8_t ab1357 = vcombine_s16(vshrn_n_s32(a, 16), vshrn_n_s32(b, 16)); + // Saturated add + return vreinterpretq_m128i_s16(vqaddq_s16(ab0246, ab1357)); +#endif +} + +// Horizontally add adjacent pairs of signed 16-bit integers in a and b using +// saturation, and pack the signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadds_pi16 +FORCE_INLINE __m64 _mm_hadds_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpret_s64_s16(vqadd_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); +#else + int16x4x2_t res = vuzp_s16(a, b); + return vreinterpret_s64_s16(vqadd_s16(res.val[0], res.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 16-bit integers in a and b, and pack +// the signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_epi16 +FORCE_INLINE __m128i _mm_hsub_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s16( + vsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int16x8x2_t c = vuzpq_s16(a, b); + return vreinterpretq_m128i_s16(vsubq_s16(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 32-bit integers in a and b, and pack +// the signed 32-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_epi32 +FORCE_INLINE __m128i _mm_hsub_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s32( + vsubq_s32(vuzp1q_s32(a, b), vuzp2q_s32(a, b))); +#else + int32x4x2_t c = vuzpq_s32(a, b); + return vreinterpretq_m128i_s32(vsubq_s32(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 16-bit integers in a and b, and pack +// the signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_pi16 +FORCE_INLINE __m64 _mm_hsub_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpret_m64_s16(vsub_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); +#else + int16x4x2_t c = vuzp_s16(a, b); + return vreinterpret_m64_s16(vsub_s16(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 32-bit integers in a and b, and pack +// the signed 32-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_hsub_pi32 +FORCE_INLINE __m64 _mm_hsub_pi32(__m64 _a, __m64 _b) +{ + int32x2_t a = vreinterpret_s32_m64(_a); + int32x2_t b = vreinterpret_s32_m64(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpret_m64_s32(vsub_s32(vuzp1_s32(a, b), vuzp2_s32(a, b))); +#else + int32x2x2_t c = vuzp_s32(a, b); + return vreinterpret_m64_s32(vsub_s32(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of signed 16-bit integers in a and b +// using saturation, and pack the signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsubs_epi16 +FORCE_INLINE __m128i _mm_hsubs_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s16( + vqsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int16x8x2_t c = vuzpq_s16(a, b); + return vreinterpretq_m128i_s16(vqsubq_s16(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of signed 16-bit integers in a and b +// using saturation, and pack the signed 16-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsubs_pi16 +FORCE_INLINE __m64 _mm_hsubs_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpret_m64_s16(vqsub_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); +#else + int16x4x2_t c = vuzp_s16(a, b); + return vreinterpret_m64_s16(vqsub_s16(c.val[0], c.val[1])); +#endif +} + +// Vertically multiply each unsigned 8-bit integer from a with the corresponding +// signed 8-bit integer from b, producing intermediate signed 16-bit integers. +// Horizontally add adjacent pairs of intermediate signed 16-bit integers, +// and pack the saturated results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16 +FORCE_INLINE __m128i _mm_maddubs_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + uint8x16_t a = vreinterpretq_u8_m128i(_a); + int8x16_t b = vreinterpretq_s8_m128i(_b); + int16x8_t tl = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(a))), + vmovl_s8(vget_low_s8(b))); + int16x8_t th = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(a))), + vmovl_s8(vget_high_s8(b))); + return vreinterpretq_m128i_s16( + vqaddq_s16(vuzp1q_s16(tl, th), vuzp2q_s16(tl, th))); +#else + // This would be much simpler if x86 would choose to zero extend OR sign + // extend, not both. This could probably be optimized better. + uint16x8_t a = vreinterpretq_u16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + + // Zero extend a + int16x8_t a_odd = vreinterpretq_s16_u16(vshrq_n_u16(a, 8)); + int16x8_t a_even = vreinterpretq_s16_u16(vbicq_u16(a, vdupq_n_u16(0xff00))); + + // Sign extend by shifting left then shifting right. + int16x8_t b_even = vshrq_n_s16(vshlq_n_s16(b, 8), 8); + int16x8_t b_odd = vshrq_n_s16(b, 8); + + // multiply + int16x8_t prod1 = vmulq_s16(a_even, b_even); + int16x8_t prod2 = vmulq_s16(a_odd, b_odd); + + // saturated add + return vreinterpretq_m128i_s16(vqaddq_s16(prod1, prod2)); +#endif +} + +// Vertically multiply each unsigned 8-bit integer from a with the corresponding +// signed 8-bit integer from b, producing intermediate signed 16-bit integers. +// Horizontally add adjacent pairs of intermediate signed 16-bit integers, and +// pack the saturated results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_pi16 +FORCE_INLINE __m64 _mm_maddubs_pi16(__m64 _a, __m64 _b) +{ + uint16x4_t a = vreinterpret_u16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); + + // Zero extend a + int16x4_t a_odd = vreinterpret_s16_u16(vshr_n_u16(a, 8)); + int16x4_t a_even = vreinterpret_s16_u16(vand_u16(a, vdup_n_u16(0xff))); + + // Sign extend by shifting left then shifting right. + int16x4_t b_even = vshr_n_s16(vshl_n_s16(b, 8), 8); + int16x4_t b_odd = vshr_n_s16(b, 8); + + // multiply + int16x4_t prod1 = vmul_s16(a_even, b_even); + int16x4_t prod2 = vmul_s16(a_odd, b_odd); + + // saturated add + return vreinterpret_m64_s16(vqadd_s16(prod1, prod2)); +} + +// Multiply packed signed 16-bit integers in a and b, producing intermediate +// signed 32-bit integers. Shift right by 15 bits while rounding up, and store +// the packed 16-bit integers in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16 +FORCE_INLINE __m128i _mm_mulhrs_epi16(__m128i a, __m128i b) +{ + // Has issues due to saturation + // return vreinterpretq_m128i_s16(vqrdmulhq_s16(a, b)); + + // Multiply + int32x4_t mul_lo = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), + vget_low_s16(vreinterpretq_s16_m128i(b))); + int32x4_t mul_hi = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), + vget_high_s16(vreinterpretq_s16_m128i(b))); + + // Rounding narrowing shift right + // narrow = (int16_t)((mul + 16384) >> 15); + int16x4_t narrow_lo = vrshrn_n_s32(mul_lo, 15); + int16x4_t narrow_hi = vrshrn_n_s32(mul_hi, 15); + + // Join together + return vreinterpretq_m128i_s16(vcombine_s16(narrow_lo, narrow_hi)); +} + +// Multiply packed signed 16-bit integers in a and b, producing intermediate +// signed 32-bit integers. Truncate each intermediate integer to the 18 most +// significant bits, round by adding 1, and store bits [16:1] to dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_pi16 +FORCE_INLINE __m64 _mm_mulhrs_pi16(__m64 a, __m64 b) +{ + int32x4_t mul_extend = + vmull_s16((vreinterpret_s16_m64(a)), (vreinterpret_s16_m64(b))); + + // Rounding narrowing shift right + return vreinterpret_m64_s16(vrshrn_n_s32(mul_extend, 15)); +} + +// Shuffle packed 8-bit integers in a according to shuffle control mask in the +// corresponding 8-bit element of b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8 +FORCE_INLINE __m128i _mm_shuffle_epi8(__m128i a, __m128i b) +{ + int8x16_t tbl = vreinterpretq_s8_m128i(a); // input a + uint8x16_t idx = vreinterpretq_u8_m128i(b); // input b + uint8x16_t idx_masked = + vandq_u8(idx, vdupq_n_u8(0x8F)); // avoid using meaningless bits +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_s8(vqtbl1q_s8(tbl, idx_masked)); +#elif defined(__GNUC__) + int8x16_t ret; + // %e and %f represent the even and odd D registers + // respectively. + __asm__ __volatile__( + "vtbl.8 %e[ret], {%e[tbl], %f[tbl]}, %e[idx]\n" + "vtbl.8 %f[ret], {%e[tbl], %f[tbl]}, %f[idx]\n" + : [ret] "=&w"(ret) + : [tbl] "w"(tbl), [idx] "w"(idx_masked)); + return vreinterpretq_m128i_s8(ret); +#else + // use this line if testing on aarch64 + int8x8x2_t a_split = {vget_low_s8(tbl), vget_high_s8(tbl)}; + return vreinterpretq_m128i_s8( + vcombine_s8(vtbl2_s8(a_split, vget_low_u8(idx_masked)), + vtbl2_s8(a_split, vget_high_u8(idx_masked)))); +#endif +} + +// Shuffle packed 8-bit integers in a according to shuffle control mask in the +// corresponding 8-bit element of b, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_pi8 +FORCE_INLINE __m64 _mm_shuffle_pi8(__m64 a, __m64 b) +{ + const int8x8_t controlMask = + vand_s8(vreinterpret_s8_m64(b), vdup_n_s8((int8_t) (0x1 << 7 | 0x07))); + int8x8_t res = vtbl1_s8(vreinterpret_s8_m64(a), controlMask); + return vreinterpret_m64_s8(res); +} + +// Negate packed 16-bit integers in a when the corresponding signed +// 16-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_epi16 +FORCE_INLINE __m128i _mm_sign_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFF : 0 + uint16x8_t ltMask = vreinterpretq_u16_s16(vshrq_n_s16(b, 15)); + // (b == 0) ? 0xFFFF : 0 +#if defined(__aarch64__) || defined(_M_ARM64) + int16x8_t zeroMask = vreinterpretq_s16_u16(vceqzq_s16(b)); +#else + int16x8_t zeroMask = vreinterpretq_s16_u16(vceqq_s16(b, vdupq_n_s16(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s16(a) equals to negative + // 'a') based on ltMask + int16x8_t masked = vbslq_s16(ltMask, vnegq_s16(a), a); + // res = masked & (~zeroMask) + int16x8_t res = vbicq_s16(masked, zeroMask); + return vreinterpretq_m128i_s16(res); +} + +// Negate packed 32-bit integers in a when the corresponding signed +// 32-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_epi32 +FORCE_INLINE __m128i _mm_sign_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFFFFFF : 0 + uint32x4_t ltMask = vreinterpretq_u32_s32(vshrq_n_s32(b, 31)); + + // (b == 0) ? 0xFFFFFFFF : 0 +#if defined(__aarch64__) || defined(_M_ARM64) + int32x4_t zeroMask = vreinterpretq_s32_u32(vceqzq_s32(b)); +#else + int32x4_t zeroMask = vreinterpretq_s32_u32(vceqq_s32(b, vdupq_n_s32(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s32(a) equals to negative + // 'a') based on ltMask + int32x4_t masked = vbslq_s32(ltMask, vnegq_s32(a), a); + // res = masked & (~zeroMask) + int32x4_t res = vbicq_s32(masked, zeroMask); + return vreinterpretq_m128i_s32(res); +} + +// Negate packed 8-bit integers in a when the corresponding signed +// 8-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_epi8 +FORCE_INLINE __m128i _mm_sign_epi8(__m128i _a, __m128i _b) +{ + int8x16_t a = vreinterpretq_s8_m128i(_a); + int8x16_t b = vreinterpretq_s8_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFF : 0 + uint8x16_t ltMask = vreinterpretq_u8_s8(vshrq_n_s8(b, 7)); + + // (b == 0) ? 0xFF : 0 +#if defined(__aarch64__) || defined(_M_ARM64) + int8x16_t zeroMask = vreinterpretq_s8_u8(vceqzq_s8(b)); +#else + int8x16_t zeroMask = vreinterpretq_s8_u8(vceqq_s8(b, vdupq_n_s8(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s8(a) return negative 'a') + // based on ltMask + int8x16_t masked = vbslq_s8(ltMask, vnegq_s8(a), a); + // res = masked & (~zeroMask) + int8x16_t res = vbicq_s8(masked, zeroMask); + + return vreinterpretq_m128i_s8(res); +} + +// Negate packed 16-bit integers in a when the corresponding signed 16-bit +// integer in b is negative, and store the results in dst. Element in dst are +// zeroed out when the corresponding element in b is zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_pi16 +FORCE_INLINE __m64 _mm_sign_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFF : 0 + uint16x4_t ltMask = vreinterpret_u16_s16(vshr_n_s16(b, 15)); + + // (b == 0) ? 0xFFFF : 0 +#if defined(__aarch64__) || defined(_M_ARM64) + int16x4_t zeroMask = vreinterpret_s16_u16(vceqz_s16(b)); +#else + int16x4_t zeroMask = vreinterpret_s16_u16(vceq_s16(b, vdup_n_s16(0))); +#endif + + // bitwise select either a or negative 'a' (vneg_s16(a) return negative 'a') + // based on ltMask + int16x4_t masked = vbsl_s16(ltMask, vneg_s16(a), a); + // res = masked & (~zeroMask) + int16x4_t res = vbic_s16(masked, zeroMask); + + return vreinterpret_m64_s16(res); +} + +// Negate packed 32-bit integers in a when the corresponding signed 32-bit +// integer in b is negative, and store the results in dst. Element in dst are +// zeroed out when the corresponding element in b is zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_pi32 +FORCE_INLINE __m64 _mm_sign_pi32(__m64 _a, __m64 _b) +{ + int32x2_t a = vreinterpret_s32_m64(_a); + int32x2_t b = vreinterpret_s32_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFFFFFF : 0 + uint32x2_t ltMask = vreinterpret_u32_s32(vshr_n_s32(b, 31)); + + // (b == 0) ? 0xFFFFFFFF : 0 +#if defined(__aarch64__) || defined(_M_ARM64) + int32x2_t zeroMask = vreinterpret_s32_u32(vceqz_s32(b)); +#else + int32x2_t zeroMask = vreinterpret_s32_u32(vceq_s32(b, vdup_n_s32(0))); +#endif + + // bitwise select either a or negative 'a' (vneg_s32(a) return negative 'a') + // based on ltMask + int32x2_t masked = vbsl_s32(ltMask, vneg_s32(a), a); + // res = masked & (~zeroMask) + int32x2_t res = vbic_s32(masked, zeroMask); + + return vreinterpret_m64_s32(res); +} + +// Negate packed 8-bit integers in a when the corresponding signed 8-bit integer +// in b is negative, and store the results in dst. Element in dst are zeroed out +// when the corresponding element in b is zero. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_pi8 +FORCE_INLINE __m64 _mm_sign_pi8(__m64 _a, __m64 _b) +{ + int8x8_t a = vreinterpret_s8_m64(_a); + int8x8_t b = vreinterpret_s8_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFF : 0 + uint8x8_t ltMask = vreinterpret_u8_s8(vshr_n_s8(b, 7)); + + // (b == 0) ? 0xFF : 0 +#if defined(__aarch64__) || defined(_M_ARM64) + int8x8_t zeroMask = vreinterpret_s8_u8(vceqz_s8(b)); +#else + int8x8_t zeroMask = vreinterpret_s8_u8(vceq_s8(b, vdup_n_s8(0))); +#endif + + // bitwise select either a or negative 'a' (vneg_s8(a) return negative 'a') + // based on ltMask + int8x8_t masked = vbsl_s8(ltMask, vneg_s8(a), a); + // res = masked & (~zeroMask) + int8x8_t res = vbic_s8(masked, zeroMask); + + return vreinterpret_m64_s8(res); +} + +/* SSE4.1 */ + +// Blend packed 16-bit integers from a and b using control mask imm8, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blend_epi16 +// FORCE_INLINE __m128i _mm_blend_epi16(__m128i a, __m128i b, +// __constrange(0,255) int imm) +#define _mm_blend_epi16(a, b, imm) \ + _sse2neon_define2( \ + __m128i, a, b, \ + const uint16_t _mask[8] = \ + _sse2neon_init(((imm) & (1 << 0)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 1)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 2)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 3)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 4)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 5)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 6)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 7)) ? (uint16_t) -1 : 0x0); \ + uint16x8_t _mask_vec = vld1q_u16(_mask); \ + uint16x8_t __a = vreinterpretq_u16_m128i(_a); \ + uint16x8_t __b = vreinterpretq_u16_m128i(_b); _sse2neon_return( \ + vreinterpretq_m128i_u16(vbslq_u16(_mask_vec, __b, __a)));) + +// Blend packed double-precision (64-bit) floating-point elements from a and b +// using control mask imm8, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blend_pd +#define _mm_blend_pd(a, b, imm) \ + _sse2neon_define2( \ + __m128d, a, b, \ + const uint64_t _mask[2] = \ + _sse2neon_init(((imm) & (1 << 0)) ? ~UINT64_C(0) : UINT64_C(0), \ + ((imm) & (1 << 1)) ? ~UINT64_C(0) : UINT64_C(0)); \ + uint64x2_t _mask_vec = vld1q_u64(_mask); \ + uint64x2_t __a = vreinterpretq_u64_m128d(_a); \ + uint64x2_t __b = vreinterpretq_u64_m128d(_b); _sse2neon_return( \ + vreinterpretq_m128d_u64(vbslq_u64(_mask_vec, __b, __a)));) + +// Blend packed single-precision (32-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blend_ps +FORCE_INLINE __m128 _mm_blend_ps(__m128 _a, __m128 _b, const char imm8) +{ + const uint32_t ALIGN_STRUCT(16) + data[4] = {((imm8) & (1 << 0)) ? UINT32_MAX : 0, + ((imm8) & (1 << 1)) ? UINT32_MAX : 0, + ((imm8) & (1 << 2)) ? UINT32_MAX : 0, + ((imm8) & (1 << 3)) ? UINT32_MAX : 0}; + uint32x4_t mask = vld1q_u32(data); + float32x4_t a = vreinterpretq_f32_m128(_a); + float32x4_t b = vreinterpretq_f32_m128(_b); + return vreinterpretq_m128_f32(vbslq_f32(mask, b, a)); +} + +// Blend packed 8-bit integers from a and b using mask, and store the results in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_epi8 +FORCE_INLINE __m128i _mm_blendv_epi8(__m128i _a, __m128i _b, __m128i _mask) +{ + // Use a signed shift right to create a mask with the sign bit + uint8x16_t mask = + vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_m128i(_mask), 7)); + uint8x16_t a = vreinterpretq_u8_m128i(_a); + uint8x16_t b = vreinterpretq_u8_m128i(_b); + return vreinterpretq_m128i_u8(vbslq_u8(mask, b, a)); +} + +// Blend packed double-precision (64-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_pd +FORCE_INLINE __m128d _mm_blendv_pd(__m128d _a, __m128d _b, __m128d _mask) +{ + uint64x2_t mask = + vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_m128d(_mask), 63)); +#if defined(__aarch64__) || defined(_M_ARM64) + float64x2_t a = vreinterpretq_f64_m128d(_a); + float64x2_t b = vreinterpretq_f64_m128d(_b); + return vreinterpretq_m128d_f64(vbslq_f64(mask, b, a)); +#else + uint64x2_t a = vreinterpretq_u64_m128d(_a); + uint64x2_t b = vreinterpretq_u64_m128d(_b); + return vreinterpretq_m128d_u64(vbslq_u64(mask, b, a)); +#endif +} + +// Blend packed single-precision (32-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_ps +FORCE_INLINE __m128 _mm_blendv_ps(__m128 _a, __m128 _b, __m128 _mask) +{ + // Use a signed shift right to create a mask with the sign bit + uint32x4_t mask = + vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_m128(_mask), 31)); + float32x4_t a = vreinterpretq_f32_m128(_a); + float32x4_t b = vreinterpretq_f32_m128(_b); + return vreinterpretq_m128_f32(vbslq_f32(mask, b, a)); +} + +// Round the packed double-precision (64-bit) floating-point elements in a up +// to an integer value, and store the results as packed double-precision +// floating-point elements in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_pd +FORCE_INLINE __m128d _mm_ceil_pd(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vrndpq_f64(vreinterpretq_f64_m128d(a))); +#else + double *f = (double *) &a; + return _mm_set_pd(ceil(f[1]), ceil(f[0])); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a up to +// an integer value, and store the results as packed single-precision +// floating-point elements in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_ps +FORCE_INLINE __m128 _mm_ceil_ps(__m128 a) +{ +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + return vreinterpretq_m128_f32(vrndpq_f32(vreinterpretq_f32_m128(a))); +#else + float *f = (float *) &a; + return _mm_set_ps(ceilf(f[3]), ceilf(f[2]), ceilf(f[1]), ceilf(f[0])); +#endif +} + +// Round the lower double-precision (64-bit) floating-point element in b up to +// an integer value, store the result as a double-precision floating-point +// element in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_sd +FORCE_INLINE __m128d _mm_ceil_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_ceil_pd(b)); +} + +// Round the lower single-precision (32-bit) floating-point element in b up to +// an integer value, store the result as a single-precision floating-point +// element in the lower element of dst, and copy the upper 3 packed elements +// from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_ss +FORCE_INLINE __m128 _mm_ceil_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_ceil_ps(b)); +} + +// Compare packed 64-bit integers in a and b for equality, and store the results +// in dst +FORCE_INLINE __m128i _mm_cmpeq_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_u64( + vceqq_u64(vreinterpretq_u64_m128i(a), vreinterpretq_u64_m128i(b))); +#else + // ARMv7 lacks vceqq_u64 + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128i_u32(vandq_u32(cmp, swapped)); +#endif +} + +// Sign extend packed 16-bit integers in a to packed 32-bit integers, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi16_epi32 +FORCE_INLINE __m128i _mm_cvtepi16_epi32(__m128i a) +{ + return vreinterpretq_m128i_s32( + vmovl_s16(vget_low_s16(vreinterpretq_s16_m128i(a)))); +} + +// Sign extend packed 16-bit integers in a to packed 64-bit integers, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi16_epi64 +FORCE_INLINE __m128i _mm_cvtepi16_epi64(__m128i a) +{ + int16x8_t s16x8 = vreinterpretq_s16_m128i(a); /* xxxx xxxx xxxx 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ + int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_s64(s64x2); +} + +// Sign extend packed 32-bit integers in a to packed 64-bit integers, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi32_epi64 +FORCE_INLINE __m128i _mm_cvtepi32_epi64(__m128i a) +{ + return vreinterpretq_m128i_s64( + vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a)))); +} + +// Sign extend packed 8-bit integers in a to packed 16-bit integers, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi8_epi16 +FORCE_INLINE __m128i _mm_cvtepi8_epi16(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + return vreinterpretq_m128i_s16(s16x8); +} + +// Sign extend packed 8-bit integers in a to packed 32-bit integers, and store +// the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi8_epi32 +FORCE_INLINE __m128i _mm_cvtepi8_epi32(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000D 000C 000B 000A */ + return vreinterpretq_m128i_s32(s32x4); +} + +// Sign extend packed 8-bit integers in the low 8 bytes of a to packed 64-bit +// integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi8_epi64 +FORCE_INLINE __m128i _mm_cvtepi8_epi64(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx xxBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0x0x 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ + int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_s64(s64x2); +} + +// Zero extend packed unsigned 16-bit integers in a to packed 32-bit integers, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu16_epi32 +FORCE_INLINE __m128i _mm_cvtepu16_epi32(__m128i a) +{ + return vreinterpretq_m128i_u32( + vmovl_u16(vget_low_u16(vreinterpretq_u16_m128i(a)))); +} + +// Zero extend packed unsigned 16-bit integers in a to packed 64-bit integers, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu16_epi64 +FORCE_INLINE __m128i _mm_cvtepu16_epi64(__m128i a) +{ + uint16x8_t u16x8 = vreinterpretq_u16_m128i(a); /* xxxx xxxx xxxx 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ + uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_u64(u64x2); +} + +// Zero extend packed unsigned 32-bit integers in a to packed 64-bit integers, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu32_epi64 +FORCE_INLINE __m128i _mm_cvtepu32_epi64(__m128i a) +{ + return vreinterpretq_m128i_u64( + vmovl_u32(vget_low_u32(vreinterpretq_u32_m128i(a)))); +} + +// Zero extend packed unsigned 8-bit integers in a to packed 16-bit integers, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu8_epi16 +FORCE_INLINE __m128i _mm_cvtepu8_epi16(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx HGFE DCBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0H0G 0F0E 0D0C 0B0A */ + return vreinterpretq_m128i_u16(u16x8); +} + +// Zero extend packed unsigned 8-bit integers in a to packed 32-bit integers, +// and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu8_epi32 +FORCE_INLINE __m128i _mm_cvtepu8_epi32(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx DCBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000D 000C 000B 000A */ + return vreinterpretq_m128i_u32(u32x4); +} + +// Zero extend packed unsigned 8-bit integers in the low 8 bytes of a to packed +// 64-bit integers, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu8_epi64 +FORCE_INLINE __m128i _mm_cvtepu8_epi64(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx xxBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0x0x 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ + uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_u64(u64x2); +} + +// Conditionally multiply the packed double-precision (64-bit) floating-point +// elements in a and b using the high 4 bits in imm8, sum the four products, and +// conditionally store the sum in dst using the low 4 bits of imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_dp_pd +FORCE_INLINE __m128d _mm_dp_pd(__m128d a, __m128d b, const int imm) +{ + // Generate mask value from constant immediate bit value + const int64_t bit0Mask = imm & 0x01 ? UINT64_MAX : 0; + const int64_t bit1Mask = imm & 0x02 ? UINT64_MAX : 0; +#if !SSE2NEON_PRECISE_DP + const int64_t bit4Mask = imm & 0x10 ? UINT64_MAX : 0; + const int64_t bit5Mask = imm & 0x20 ? UINT64_MAX : 0; +#endif + // Conditional multiplication +#if !SSE2NEON_PRECISE_DP + __m128d mul = _mm_mul_pd(a, b); + const __m128d mulMask = + _mm_castsi128_pd(_mm_set_epi64x(bit5Mask, bit4Mask)); + __m128d tmp = _mm_and_pd(mul, mulMask); +#else +#if defined(__aarch64__) || defined(_M_ARM64) + double d0 = (imm & 0x10) ? vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0) * + vgetq_lane_f64(vreinterpretq_f64_m128d(b), 0) + : 0; + double d1 = (imm & 0x20) ? vgetq_lane_f64(vreinterpretq_f64_m128d(a), 1) * + vgetq_lane_f64(vreinterpretq_f64_m128d(b), 1) + : 0; +#else + double d0 = (imm & 0x10) ? ((double *) &a)[0] * ((double *) &b)[0] : 0; + double d1 = (imm & 0x20) ? ((double *) &a)[1] * ((double *) &b)[1] : 0; +#endif + __m128d tmp = _mm_set_pd(d1, d0); +#endif + // Sum the products +#if defined(__aarch64__) || defined(_M_ARM64) + double sum = vpaddd_f64(vreinterpretq_f64_m128d(tmp)); +#else + double sum = *((double *) &tmp) + *(((double *) &tmp) + 1); +#endif + // Conditionally store the sum + const __m128d sumMask = + _mm_castsi128_pd(_mm_set_epi64x(bit1Mask, bit0Mask)); + __m128d res = _mm_and_pd(_mm_set_pd1(sum), sumMask); + return res; +} + +// Conditionally multiply the packed single-precision (32-bit) floating-point +// elements in a and b using the high 4 bits in imm8, sum the four products, +// and conditionally store the sum in dst using the low 4 bits of imm. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_dp_ps +FORCE_INLINE __m128 _mm_dp_ps(__m128 a, __m128 b, const int imm) +{ + float32x4_t elementwise_prod = _mm_mul_ps(a, b); + +#if defined(__aarch64__) || defined(_M_ARM64) + /* shortcuts */ + if (imm == 0xFF) { + return _mm_set1_ps(vaddvq_f32(elementwise_prod)); + } + + if ((imm & 0x0F) == 0x0F) { + if (!(imm & (1 << 4))) + elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 0); + if (!(imm & (1 << 5))) + elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 1); + if (!(imm & (1 << 6))) + elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 2); + if (!(imm & (1 << 7))) + elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 3); + + return _mm_set1_ps(vaddvq_f32(elementwise_prod)); + } +#endif + + float s = 0.0f; + + if (imm & (1 << 4)) + s += vgetq_lane_f32(elementwise_prod, 0); + if (imm & (1 << 5)) + s += vgetq_lane_f32(elementwise_prod, 1); + if (imm & (1 << 6)) + s += vgetq_lane_f32(elementwise_prod, 2); + if (imm & (1 << 7)) + s += vgetq_lane_f32(elementwise_prod, 3); + + const float32_t res[4] = { + (imm & 0x1) ? s : 0.0f, + (imm & 0x2) ? s : 0.0f, + (imm & 0x4) ? s : 0.0f, + (imm & 0x8) ? s : 0.0f, + }; + return vreinterpretq_m128_f32(vld1q_f32(res)); +} + +// Extract a 32-bit integer from a, selected with imm8, and store the result in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi32 +// FORCE_INLINE int _mm_extract_epi32(__m128i a, __constrange(0,4) int imm) +#define _mm_extract_epi32(a, imm) \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)) + +// Extract a 64-bit integer from a, selected with imm8, and store the result in +// dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi64 +// FORCE_INLINE __int64 _mm_extract_epi64(__m128i a, __constrange(0,2) int imm) +#define _mm_extract_epi64(a, imm) \ + vgetq_lane_s64(vreinterpretq_s64_m128i(a), (imm)) + +// Extract an 8-bit integer from a, selected with imm8, and store the result in +// the lower element of dst. FORCE_INLINE int _mm_extract_epi8(__m128i a, +// __constrange(0,16) int imm) +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi8 +#define _mm_extract_epi8(a, imm) vgetq_lane_u8(vreinterpretq_u8_m128i(a), (imm)) + +// Extracts the selected single-precision (32-bit) floating-point from a. +// FORCE_INLINE int _mm_extract_ps(__m128 a, __constrange(0,4) int imm) +#define _mm_extract_ps(a, imm) vgetq_lane_s32(vreinterpretq_s32_m128(a), (imm)) + +// Round the packed double-precision (64-bit) floating-point elements in a down +// to an integer value, and store the results as packed double-precision +// floating-point elements in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_pd +FORCE_INLINE __m128d _mm_floor_pd(__m128d a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128d_f64(vrndmq_f64(vreinterpretq_f64_m128d(a))); +#else + double *f = (double *) &a; + return _mm_set_pd(floor(f[1]), floor(f[0])); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a down +// to an integer value, and store the results as packed single-precision +// floating-point elements in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_ps +FORCE_INLINE __m128 _mm_floor_ps(__m128 a) +{ +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + return vreinterpretq_m128_f32(vrndmq_f32(vreinterpretq_f32_m128(a))); +#else + float *f = (float *) &a; + return _mm_set_ps(floorf(f[3]), floorf(f[2]), floorf(f[1]), floorf(f[0])); +#endif +} + +// Round the lower double-precision (64-bit) floating-point element in b down to +// an integer value, store the result as a double-precision floating-point +// element in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_sd +FORCE_INLINE __m128d _mm_floor_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_floor_pd(b)); +} + +// Round the lower single-precision (32-bit) floating-point element in b down to +// an integer value, store the result as a single-precision floating-point +// element in the lower element of dst, and copy the upper 3 packed elements +// from a to the upper elements of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_ss +FORCE_INLINE __m128 _mm_floor_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_floor_ps(b)); +} + +// Copy a to dst, and insert the 32-bit integer i into dst at the location +// specified by imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi32 +// FORCE_INLINE __m128i _mm_insert_epi32(__m128i a, int b, +// __constrange(0,4) int imm) +#define _mm_insert_epi32(a, b, imm) \ + vreinterpretq_m128i_s32( \ + vsetq_lane_s32((b), vreinterpretq_s32_m128i(a), (imm))) + +// Copy a to dst, and insert the 64-bit integer i into dst at the location +// specified by imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi64 +// FORCE_INLINE __m128i _mm_insert_epi64(__m128i a, __int64 b, +// __constrange(0,2) int imm) +#define _mm_insert_epi64(a, b, imm) \ + vreinterpretq_m128i_s64( \ + vsetq_lane_s64((b), vreinterpretq_s64_m128i(a), (imm))) + +// Copy a to dst, and insert the lower 8-bit integer from i into dst at the +// location specified by imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi8 +// FORCE_INLINE __m128i _mm_insert_epi8(__m128i a, int b, +// __constrange(0,16) int imm) +#define _mm_insert_epi8(a, b, imm) \ + vreinterpretq_m128i_s8(vsetq_lane_s8((b), vreinterpretq_s8_m128i(a), (imm))) + +// Copy a to tmp, then insert a single-precision (32-bit) floating-point +// element from b into tmp using the control in imm8. Store tmp to dst using +// the mask in imm8 (elements are zeroed out when the corresponding bit is set). +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=insert_ps +#define _mm_insert_ps(a, b, imm8) \ + _sse2neon_define2( \ + __m128, a, b, \ + float32x4_t tmp1 = \ + vsetq_lane_f32(vgetq_lane_f32(_b, (imm8 >> 6) & 0x3), \ + vreinterpretq_f32_m128(_a), 0); \ + float32x4_t tmp2 = \ + vsetq_lane_f32(vgetq_lane_f32(tmp1, 0), \ + vreinterpretq_f32_m128(_a), ((imm8 >> 4) & 0x3)); \ + const uint32_t data[4] = \ + _sse2neon_init(((imm8) & (1 << 0)) ? UINT32_MAX : 0, \ + ((imm8) & (1 << 1)) ? UINT32_MAX : 0, \ + ((imm8) & (1 << 2)) ? UINT32_MAX : 0, \ + ((imm8) & (1 << 3)) ? UINT32_MAX : 0); \ + uint32x4_t mask = vld1q_u32(data); \ + float32x4_t all_zeros = vdupq_n_f32(0); \ + \ + _sse2neon_return(vreinterpretq_m128_f32( \ + vbslq_f32(mask, all_zeros, vreinterpretq_f32_m128(tmp2))));) + +// Compare packed signed 32-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epi32 +FORCE_INLINE __m128i _mm_max_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vmaxq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epi8 +FORCE_INLINE __m128i _mm_max_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vmaxq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed unsigned 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu16 +FORCE_INLINE __m128i _mm_max_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vmaxq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Compare packed unsigned 32-bit integers in a and b, and store packed maximum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu32 +FORCE_INLINE __m128i _mm_max_epu32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vmaxq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); +} + +// Compare packed signed 32-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epi32 +FORCE_INLINE __m128i _mm_min_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vminq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epi8 +FORCE_INLINE __m128i _mm_min_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vminq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed unsigned 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epu16 +FORCE_INLINE __m128i _mm_min_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vminq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Compare packed unsigned 32-bit integers in a and b, and store packed minimum +// values in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu32 +FORCE_INLINE __m128i _mm_min_epu32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vminq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); +} + +// Horizontally compute the minimum amongst the packed unsigned 16-bit integers +// in a, store the minimum and index in dst, and zero the remaining bits in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_minpos_epu16 +FORCE_INLINE __m128i _mm_minpos_epu16(__m128i a) +{ + __m128i dst; + uint16_t min, idx = 0; +#if defined(__aarch64__) || defined(_M_ARM64) + // Find the minimum value + min = vminvq_u16(vreinterpretq_u16_m128i(a)); + + // Get the index of the minimum value + static const uint16_t idxv[] = {0, 1, 2, 3, 4, 5, 6, 7}; + uint16x8_t minv = vdupq_n_u16(min); + uint16x8_t cmeq = vceqq_u16(minv, vreinterpretq_u16_m128i(a)); + idx = vminvq_u16(vornq_u16(vld1q_u16(idxv), cmeq)); +#else + // Find the minimum value + __m64 tmp; + tmp = vreinterpret_m64_u16( + vmin_u16(vget_low_u16(vreinterpretq_u16_m128i(a)), + vget_high_u16(vreinterpretq_u16_m128i(a)))); + tmp = vreinterpret_m64_u16( + vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); + tmp = vreinterpret_m64_u16( + vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); + min = vget_lane_u16(vreinterpret_u16_m64(tmp), 0); + // Get the index of the minimum value + int i; + for (i = 0; i < 8; i++) { + if (min == vgetq_lane_u16(vreinterpretq_u16_m128i(a), 0)) { + idx = (uint16_t) i; + break; + } + a = _mm_srli_si128(a, 2); + } +#endif + // Generate result + dst = _mm_setzero_si128(); + dst = vreinterpretq_m128i_u16( + vsetq_lane_u16(min, vreinterpretq_u16_m128i(dst), 0)); + dst = vreinterpretq_m128i_u16( + vsetq_lane_u16(idx, vreinterpretq_u16_m128i(dst), 1)); + return dst; +} + +// Compute the sum of absolute differences (SADs) of quadruplets of unsigned +// 8-bit integers in a compared to those in b, and store the 16-bit results in +// dst. Eight SADs are performed using one quadruplet from b and eight +// quadruplets from a. One quadruplet is selected from b starting at on the +// offset specified in imm8. Eight quadruplets are formed from sequential 8-bit +// integers selected from a starting at the offset specified in imm8. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8 +FORCE_INLINE __m128i _mm_mpsadbw_epu8(__m128i a, __m128i b, const int imm) +{ + uint8x16_t _a, _b; + + switch (imm & 0x4) { + case 0: + // do nothing + _a = vreinterpretq_u8_m128i(a); + break; + case 4: + _a = vreinterpretq_u8_u32(vextq_u32(vreinterpretq_u32_m128i(a), + vreinterpretq_u32_m128i(a), 1)); + break; + default: +#if defined(__GNUC__) || defined(__clang__) + __builtin_unreachable(); +#elif defined(_MSC_VER) + __assume(0); +#endif + break; + } + + switch (imm & 0x3) { + case 0: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 0))); + break; + case 1: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 1))); + break; + case 2: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 2))); + break; + case 3: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 3))); + break; + default: +#if defined(__GNUC__) || defined(__clang__) + __builtin_unreachable(); +#elif defined(_MSC_VER) + __assume(0); +#endif + break; + } + + int16x8_t c04, c15, c26, c37; + uint8x8_t low_b = vget_low_u8(_b); + c04 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a), low_b)); + uint8x16_t _a_1 = vextq_u8(_a, _a, 1); + c15 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a_1), low_b)); + uint8x16_t _a_2 = vextq_u8(_a, _a, 2); + c26 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a_2), low_b)); + uint8x16_t _a_3 = vextq_u8(_a, _a, 3); + c37 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a_3), low_b)); +#if defined(__aarch64__) || defined(_M_ARM64) + // |0|4|2|6| + c04 = vpaddq_s16(c04, c26); + // |1|5|3|7| + c15 = vpaddq_s16(c15, c37); + + int32x4_t trn1_c = + vtrn1q_s32(vreinterpretq_s32_s16(c04), vreinterpretq_s32_s16(c15)); + int32x4_t trn2_c = + vtrn2q_s32(vreinterpretq_s32_s16(c04), vreinterpretq_s32_s16(c15)); + return vreinterpretq_m128i_s16(vpaddq_s16(vreinterpretq_s16_s32(trn1_c), + vreinterpretq_s16_s32(trn2_c))); +#else + int16x4_t c01, c23, c45, c67; + c01 = vpadd_s16(vget_low_s16(c04), vget_low_s16(c15)); + c23 = vpadd_s16(vget_low_s16(c26), vget_low_s16(c37)); + c45 = vpadd_s16(vget_high_s16(c04), vget_high_s16(c15)); + c67 = vpadd_s16(vget_high_s16(c26), vget_high_s16(c37)); + + return vreinterpretq_m128i_s16( + vcombine_s16(vpadd_s16(c01, c23), vpadd_s16(c45, c67))); +#endif +} + +// Multiply the low signed 32-bit integers from each packed 64-bit element in +// a and b, and store the signed 64-bit results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_epi32 +FORCE_INLINE __m128i _mm_mul_epi32(__m128i a, __m128i b) +{ + // vmull_s32 upcasts instead of masking, so we downcast. + int32x2_t a_lo = vmovn_s64(vreinterpretq_s64_m128i(a)); + int32x2_t b_lo = vmovn_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vmull_s32(a_lo, b_lo)); +} + +// Multiply the packed 32-bit integers in a and b, producing intermediate 64-bit +// integers, and store the low 32 bits of the intermediate integers in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mullo_epi32 +FORCE_INLINE __m128i _mm_mullo_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vmulq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Convert packed signed 32-bit integers from a and b to packed 16-bit integers +// using unsigned saturation, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi32 +FORCE_INLINE __m128i _mm_packus_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcombine_u16(vqmovun_s32(vreinterpretq_s32_m128i(a)), + vqmovun_s32(vreinterpretq_s32_m128i(b)))); +} + +// Round the packed double-precision (64-bit) floating-point elements in a using +// the rounding parameter, and store the results as packed double-precision +// floating-point elements in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_round_pd +FORCE_INLINE __m128d _mm_round_pd(__m128d a, int rounding) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + switch (rounding) { + case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): + return vreinterpretq_m128d_f64(vrndnq_f64(vreinterpretq_f64_m128d(a))); + case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): + return _mm_floor_pd(a); + case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): + return _mm_ceil_pd(a); + case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): + return vreinterpretq_m128d_f64(vrndq_f64(vreinterpretq_f64_m128d(a))); + default: //_MM_FROUND_CUR_DIRECTION + return vreinterpretq_m128d_f64(vrndiq_f64(vreinterpretq_f64_m128d(a))); + } +#else + double *v_double = (double *) &a; + + if (rounding == (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_NEAREST)) { + double res[2], tmp; + for (int i = 0; i < 2; i++) { + tmp = (v_double[i] < 0) ? -v_double[i] : v_double[i]; + double roundDown = floor(tmp); // Round down value + double roundUp = ceil(tmp); // Round up value + double diffDown = tmp - roundDown; + double diffUp = roundUp - tmp; + if (diffDown < diffUp) { + /* If it's closer to the round down value, then use it */ + res[i] = roundDown; + } else if (diffDown > diffUp) { + /* If it's closer to the round up value, then use it */ + res[i] = roundUp; + } else { + /* If it's equidistant between round up and round down value, + * pick the one which is an even number */ + double half = roundDown / 2; + if (half != floor(half)) { + /* If the round down value is odd, return the round up value + */ + res[i] = roundUp; + } else { + /* If the round up value is odd, return the round down value + */ + res[i] = roundDown; + } + } + res[i] = (v_double[i] < 0) ? -res[i] : res[i]; + } + return _mm_set_pd(res[1], res[0]); + } else if (rounding == (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_DOWN)) { + return _mm_floor_pd(a); + } else if (rounding == (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_UP)) { + return _mm_ceil_pd(a); + } + return _mm_set_pd(v_double[1] > 0 ? floor(v_double[1]) : ceil(v_double[1]), + v_double[0] > 0 ? floor(v_double[0]) : ceil(v_double[0])); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a using +// the rounding parameter, and store the results as packed single-precision +// floating-point elements in dst. +// software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_ps +FORCE_INLINE __m128 _mm_round_ps(__m128 a, int rounding) +{ +#if (defined(__aarch64__) || defined(_M_ARM64)) || \ + defined(__ARM_FEATURE_DIRECTED_ROUNDING) + switch (rounding) { + case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndnq_f32(vreinterpretq_f32_m128(a))); + case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): + return _mm_floor_ps(a); + case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): + return _mm_ceil_ps(a); + case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndq_f32(vreinterpretq_f32_m128(a))); + default: //_MM_FROUND_CUR_DIRECTION + return vreinterpretq_m128_f32(vrndiq_f32(vreinterpretq_f32_m128(a))); + } +#else + float *v_float = (float *) &a; + + if (rounding == (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_NEAREST)) { + uint32x4_t signmask = vdupq_n_u32(0x80000000); + float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), + vdupq_n_f32(0.5f)); /* +/- 0.5 */ + int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( + vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ + int32x4_t r_trunc = vcvtq_s32_f32( + vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ + int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( + vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ + int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), + vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ + float32x4_t delta = vsubq_f32( + vreinterpretq_f32_m128(a), + vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ + uint32x4_t is_delta_half = + vceqq_f32(delta, half); /* delta == +/- 0.5 */ + return vreinterpretq_m128_f32( + vcvtq_f32_s32(vbslq_s32(is_delta_half, r_even, r_normal))); + } else if (rounding == (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_DOWN)) { + return _mm_floor_ps(a); + } else if (rounding == (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_UP)) { + return _mm_ceil_ps(a); + } + return _mm_set_ps(v_float[3] > 0 ? floorf(v_float[3]) : ceilf(v_float[3]), + v_float[2] > 0 ? floorf(v_float[2]) : ceilf(v_float[2]), + v_float[1] > 0 ? floorf(v_float[1]) : ceilf(v_float[1]), + v_float[0] > 0 ? floorf(v_float[0]) : ceilf(v_float[0])); +#endif +} + +// Round the lower double-precision (64-bit) floating-point element in b using +// the rounding parameter, store the result as a double-precision floating-point +// element in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_round_sd +FORCE_INLINE __m128d _mm_round_sd(__m128d a, __m128d b, int rounding) +{ + return _mm_move_sd(a, _mm_round_pd(b, rounding)); +} + +// Round the lower single-precision (32-bit) floating-point element in b using +// the rounding parameter, store the result as a single-precision floating-point +// element in the lower element of dst, and copy the upper 3 packed elements +// from a to the upper elements of dst. Rounding is done according to the +// rounding[3:0] parameter, which can be one of: +// (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC) // round to nearest, and +// suppress exceptions +// (_MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC) // round down, and +// suppress exceptions +// (_MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC) // round up, and suppress +// exceptions +// (_MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC) // truncate, and suppress +// exceptions _MM_FROUND_CUR_DIRECTION // use MXCSR.RC; see +// _MM_SET_ROUNDING_MODE +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_round_ss +FORCE_INLINE __m128 _mm_round_ss(__m128 a, __m128 b, int rounding) +{ + return _mm_move_ss(a, _mm_round_ps(b, rounding)); +} + +// Load 128-bits of integer data from memory into dst using a non-temporal +// memory hint. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_load_si128 +FORCE_INLINE __m128i _mm_stream_load_si128(__m128i *p) +{ +#if __has_builtin(__builtin_nontemporal_store) + return __builtin_nontemporal_load(p); +#else + return vreinterpretq_m128i_s64(vld1q_s64((int64_t *) p)); +#endif +} + +// Compute the bitwise NOT of a and then AND with a 128-bit vector containing +// all 1's, and return 1 if the result is zero, otherwise return 0. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_test_all_ones +FORCE_INLINE int _mm_test_all_ones(__m128i a) +{ + return (uint64_t) (vgetq_lane_s64(a, 0) & vgetq_lane_s64(a, 1)) == + ~(uint64_t) 0; +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and +// mask, and return 1 if the result is zero, otherwise return 0. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_test_all_zeros +FORCE_INLINE int _mm_test_all_zeros(__m128i a, __m128i mask) +{ + int64x2_t a_and_mask = + vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(mask)); + return !(vgetq_lane_s64(a_and_mask, 0) | vgetq_lane_s64(a_and_mask, 1)); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and +// mask, and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute +// the bitwise NOT of a and then AND with mask, and set CF to 1 if the result is +// zero, otherwise set CF to 0. Return 1 if both the ZF and CF values are zero, +// otherwise return 0. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_test_mix_ones_zero +// Note: Argument names may be wrong in the Intel intrinsics guide. +FORCE_INLINE int _mm_test_mix_ones_zeros(__m128i a, __m128i mask) +{ + uint64x2_t v = vreinterpretq_u64_m128i(a); + uint64x2_t m = vreinterpretq_u64_m128i(mask); + + // find ones (set-bits) and zeros (clear-bits) under clip mask + uint64x2_t ones = vandq_u64(m, v); + uint64x2_t zeros = vbicq_u64(m, v); + + // If both 128-bit variables are populated (non-zero) then return 1. + // For comparision purposes, first compact each var down to 32-bits. + uint32x2_t reduced = vpmax_u32(vqmovn_u64(ones), vqmovn_u64(zeros)); + + // if folding minimum is non-zero then both vars must be non-zero + return (vget_lane_u32(vpmin_u32(reduced, reduced), 0) != 0); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return the CF value. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testc_si128 +FORCE_INLINE int _mm_testc_si128(__m128i a, __m128i b) +{ + int64x2_t s64 = + vbicq_s64(vreinterpretq_s64_m128i(b), vreinterpretq_s64_m128i(a)); + return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return 1 if both the ZF and CF values are zero, +// otherwise return 0. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testnzc_si128 +#define _mm_testnzc_si128(a, b) _mm_test_mix_ones_zeros(a, b) + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return the ZF value. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testz_si128 +FORCE_INLINE int _mm_testz_si128(__m128i a, __m128i b) +{ + int64x2_t s64 = + vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b)); + return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); +} + +/* SSE4.2 */ + +static const uint16_t ALIGN_STRUCT(16) _sse2neon_cmpestr_mask16b[8] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, +}; +static const uint8_t ALIGN_STRUCT(16) _sse2neon_cmpestr_mask8b[16] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, +}; + +/* specify the source data format */ +#define _SIDD_UBYTE_OPS 0x00 /* unsigned 8-bit characters */ +#define _SIDD_UWORD_OPS 0x01 /* unsigned 16-bit characters */ +#define _SIDD_SBYTE_OPS 0x02 /* signed 8-bit characters */ +#define _SIDD_SWORD_OPS 0x03 /* signed 16-bit characters */ + +/* specify the comparison operation */ +#define _SIDD_CMP_EQUAL_ANY 0x00 /* compare equal any: strchr */ +#define _SIDD_CMP_RANGES 0x04 /* compare ranges */ +#define _SIDD_CMP_EQUAL_EACH 0x08 /* compare equal each: strcmp */ +#define _SIDD_CMP_EQUAL_ORDERED 0x0C /* compare equal ordered */ + +/* specify the polarity */ +#define _SIDD_POSITIVE_POLARITY 0x00 +#define _SIDD_MASKED_POSITIVE_POLARITY 0x20 +#define _SIDD_NEGATIVE_POLARITY 0x10 /* negate results */ +#define _SIDD_MASKED_NEGATIVE_POLARITY \ + 0x30 /* negate results only before end of string */ + +/* specify the output selection in _mm_cmpXstri */ +#define _SIDD_LEAST_SIGNIFICANT 0x00 +#define _SIDD_MOST_SIGNIFICANT 0x40 + +/* specify the output selection in _mm_cmpXstrm */ +#define _SIDD_BIT_MASK 0x00 +#define _SIDD_UNIT_MASK 0x40 + +/* Pattern Matching for C macros. + * https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms + */ + +/* catenate */ +#define SSE2NEON_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ +#define SSE2NEON_CAT(a, b) SSE2NEON_PRIMITIVE_CAT(a, b) + +#define SSE2NEON_IIF(c) SSE2NEON_PRIMITIVE_CAT(SSE2NEON_IIF_, c) +/* run the 2nd parameter */ +#define SSE2NEON_IIF_0(t, ...) __VA_ARGS__ +/* run the 1st parameter */ +#define SSE2NEON_IIF_1(t, ...) t + +#define SSE2NEON_COMPL(b) SSE2NEON_PRIMITIVE_CAT(SSE2NEON_COMPL_, b) +#define SSE2NEON_COMPL_0 1 +#define SSE2NEON_COMPL_1 0 + +#define SSE2NEON_DEC(x) SSE2NEON_PRIMITIVE_CAT(SSE2NEON_DEC_, x) +#define SSE2NEON_DEC_1 0 +#define SSE2NEON_DEC_2 1 +#define SSE2NEON_DEC_3 2 +#define SSE2NEON_DEC_4 3 +#define SSE2NEON_DEC_5 4 +#define SSE2NEON_DEC_6 5 +#define SSE2NEON_DEC_7 6 +#define SSE2NEON_DEC_8 7 +#define SSE2NEON_DEC_9 8 +#define SSE2NEON_DEC_10 9 +#define SSE2NEON_DEC_11 10 +#define SSE2NEON_DEC_12 11 +#define SSE2NEON_DEC_13 12 +#define SSE2NEON_DEC_14 13 +#define SSE2NEON_DEC_15 14 +#define SSE2NEON_DEC_16 15 + +/* detection */ +#define SSE2NEON_CHECK_N(x, n, ...) n +#define SSE2NEON_CHECK(...) SSE2NEON_CHECK_N(__VA_ARGS__, 0, ) +#define SSE2NEON_PROBE(x) x, 1, + +#define SSE2NEON_NOT(x) SSE2NEON_CHECK(SSE2NEON_PRIMITIVE_CAT(SSE2NEON_NOT_, x)) +#define SSE2NEON_NOT_0 SSE2NEON_PROBE(~) + +#define SSE2NEON_BOOL(x) SSE2NEON_COMPL(SSE2NEON_NOT(x)) +#define SSE2NEON_IF(c) SSE2NEON_IIF(SSE2NEON_BOOL(c)) + +#define SSE2NEON_EAT(...) +#define SSE2NEON_EXPAND(...) __VA_ARGS__ +#define SSE2NEON_WHEN(c) SSE2NEON_IF(c)(SSE2NEON_EXPAND, SSE2NEON_EAT) + +/* recursion */ +/* deferred expression */ +#define SSE2NEON_EMPTY() +#define SSE2NEON_DEFER(id) id SSE2NEON_EMPTY() +#define SSE2NEON_OBSTRUCT(...) __VA_ARGS__ SSE2NEON_DEFER(SSE2NEON_EMPTY)() +#define SSE2NEON_EXPAND(...) __VA_ARGS__ + +#define SSE2NEON_EVAL(...) \ + SSE2NEON_EVAL1(SSE2NEON_EVAL1(SSE2NEON_EVAL1(__VA_ARGS__))) +#define SSE2NEON_EVAL1(...) \ + SSE2NEON_EVAL2(SSE2NEON_EVAL2(SSE2NEON_EVAL2(__VA_ARGS__))) +#define SSE2NEON_EVAL2(...) \ + SSE2NEON_EVAL3(SSE2NEON_EVAL3(SSE2NEON_EVAL3(__VA_ARGS__))) +#define SSE2NEON_EVAL3(...) __VA_ARGS__ + +#define SSE2NEON_REPEAT(count, macro, ...) \ + SSE2NEON_WHEN(count) \ + (SSE2NEON_OBSTRUCT(SSE2NEON_REPEAT_INDIRECT)()( \ + SSE2NEON_DEC(count), macro, \ + __VA_ARGS__) SSE2NEON_OBSTRUCT(macro)(SSE2NEON_DEC(count), \ + __VA_ARGS__)) +#define SSE2NEON_REPEAT_INDIRECT() SSE2NEON_REPEAT + +#define SSE2NEON_SIZE_OF_byte 8 +#define SSE2NEON_NUMBER_OF_LANES_byte 16 +#define SSE2NEON_SIZE_OF_word 16 +#define SSE2NEON_NUMBER_OF_LANES_word 8 + +#define SSE2NEON_COMPARE_EQUAL_THEN_FILL_LANE(i, type) \ + mtx[i] = vreinterpretq_m128i_##type(vceqq_##type( \ + vdupq_n_##type(vgetq_lane_##type(vreinterpretq_##type##_m128i(b), i)), \ + vreinterpretq_##type##_m128i(a))); + +#define SSE2NEON_FILL_LANE(i, type) \ + vec_b[i] = \ + vdupq_n_##type(vgetq_lane_##type(vreinterpretq_##type##_m128i(b), i)); + +#define PCMPSTR_RANGES(a, b, mtx, data_type_prefix, type_prefix, size, \ + number_of_lanes, byte_or_word) \ + do { \ + SSE2NEON_CAT( \ + data_type_prefix, \ + SSE2NEON_CAT(size, \ + SSE2NEON_CAT(x, SSE2NEON_CAT(number_of_lanes, _t)))) \ + vec_b[number_of_lanes]; \ + __m128i mask = SSE2NEON_IIF(byte_or_word)( \ + vreinterpretq_m128i_u16(vdupq_n_u16(0xff)), \ + vreinterpretq_m128i_u32(vdupq_n_u32(0xffff))); \ + SSE2NEON_EVAL(SSE2NEON_REPEAT(number_of_lanes, SSE2NEON_FILL_LANE, \ + SSE2NEON_CAT(type_prefix, size))) \ + for (int i = 0; i < number_of_lanes; i++) { \ + mtx[i] = SSE2NEON_CAT(vreinterpretq_m128i_u, \ + size)(SSE2NEON_CAT(vbslq_u, size)( \ + SSE2NEON_CAT(vreinterpretq_u, \ + SSE2NEON_CAT(size, _m128i))(mask), \ + SSE2NEON_CAT(vcgeq_, SSE2NEON_CAT(type_prefix, size))( \ + vec_b[i], \ + SSE2NEON_CAT( \ + vreinterpretq_, \ + SSE2NEON_CAT(type_prefix, \ + SSE2NEON_CAT(size, _m128i(a))))), \ + SSE2NEON_CAT(vcleq_, SSE2NEON_CAT(type_prefix, size))( \ + vec_b[i], \ + SSE2NEON_CAT( \ + vreinterpretq_, \ + SSE2NEON_CAT(type_prefix, \ + SSE2NEON_CAT(size, _m128i(a))))))); \ + } \ + } while (0) + +#define PCMPSTR_EQ(a, b, mtx, size, number_of_lanes) \ + do { \ + SSE2NEON_EVAL(SSE2NEON_REPEAT(number_of_lanes, \ + SSE2NEON_COMPARE_EQUAL_THEN_FILL_LANE, \ + SSE2NEON_CAT(u, size))) \ + } while (0) + +#define SSE2NEON_CMP_EQUAL_ANY_IMPL(type) \ + static int _sse2neon_cmp_##type##_equal_any(__m128i a, int la, __m128i b, \ + int lb) \ + { \ + __m128i mtx[16]; \ + PCMPSTR_EQ(a, b, mtx, SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ + SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type)); \ + return SSE2NEON_CAT( \ + _sse2neon_aggregate_equal_any_, \ + SSE2NEON_CAT( \ + SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ + SSE2NEON_CAT(x, SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, \ + type))))(la, lb, mtx); \ + } + +#define SSE2NEON_CMP_RANGES_IMPL(type, data_type, us, byte_or_word) \ + static int _sse2neon_cmp_##us##type##_ranges(__m128i a, int la, __m128i b, \ + int lb) \ + { \ + __m128i mtx[16]; \ + PCMPSTR_RANGES( \ + a, b, mtx, data_type, us, SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ + SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type), byte_or_word); \ + return SSE2NEON_CAT( \ + _sse2neon_aggregate_ranges_, \ + SSE2NEON_CAT( \ + SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ + SSE2NEON_CAT(x, SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, \ + type))))(la, lb, mtx); \ + } + +#define SSE2NEON_CMP_EQUAL_ORDERED_IMPL(type) \ + static int _sse2neon_cmp_##type##_equal_ordered(__m128i a, int la, \ + __m128i b, int lb) \ + { \ + __m128i mtx[16]; \ + PCMPSTR_EQ(a, b, mtx, SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ + SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type)); \ + return SSE2NEON_CAT( \ + _sse2neon_aggregate_equal_ordered_, \ + SSE2NEON_CAT( \ + SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ + SSE2NEON_CAT(x, \ + SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type))))( \ + SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type), la, lb, mtx); \ + } + +static int _sse2neon_aggregate_equal_any_8x16(int la, int lb, __m128i mtx[16]) +{ + int res = 0; + int m = (1 << la) - 1; + uint8x8_t vec_mask = vld1_u8(_sse2neon_cmpestr_mask8b); + uint8x8_t t_lo = vtst_u8(vdup_n_u8(m & 0xff), vec_mask); + uint8x8_t t_hi = vtst_u8(vdup_n_u8(m >> 8), vec_mask); + uint8x16_t vec = vcombine_u8(t_lo, t_hi); + for (int j = 0; j < lb; j++) { + mtx[j] = vreinterpretq_m128i_u8( + vandq_u8(vec, vreinterpretq_u8_m128i(mtx[j]))); + mtx[j] = vreinterpretq_m128i_u8( + vshrq_n_u8(vreinterpretq_u8_m128i(mtx[j]), 7)); + int tmp = _sse2neon_vaddvq_u8(vreinterpretq_u8_m128i(mtx[j])) ? 1 : 0; + res |= (tmp << j); + } + return res; +} + +static int _sse2neon_aggregate_equal_any_16x8(int la, int lb, __m128i mtx[16]) +{ + int res = 0; + int m = (1 << la) - 1; + uint16x8_t vec = + vtstq_u16(vdupq_n_u16(m), vld1q_u16(_sse2neon_cmpestr_mask16b)); + for (int j = 0; j < lb; j++) { + mtx[j] = vreinterpretq_m128i_u16( + vandq_u16(vec, vreinterpretq_u16_m128i(mtx[j]))); + mtx[j] = vreinterpretq_m128i_u16( + vshrq_n_u16(vreinterpretq_u16_m128i(mtx[j]), 15)); + int tmp = _sse2neon_vaddvq_u16(vreinterpretq_u16_m128i(mtx[j])) ? 1 : 0; + res |= (tmp << j); + } + return res; +} + +/* clang-format off */ +#define SSE2NEON_GENERATE_CMP_EQUAL_ANY(prefix) \ + prefix##IMPL(byte) \ + prefix##IMPL(word) +/* clang-format on */ + +SSE2NEON_GENERATE_CMP_EQUAL_ANY(SSE2NEON_CMP_EQUAL_ANY_) + +static int _sse2neon_aggregate_ranges_16x8(int la, int lb, __m128i mtx[16]) +{ + int res = 0; + int m = (1 << la) - 1; + uint16x8_t vec = + vtstq_u16(vdupq_n_u16(m), vld1q_u16(_sse2neon_cmpestr_mask16b)); + for (int j = 0; j < lb; j++) { + mtx[j] = vreinterpretq_m128i_u16( + vandq_u16(vec, vreinterpretq_u16_m128i(mtx[j]))); + mtx[j] = vreinterpretq_m128i_u16( + vshrq_n_u16(vreinterpretq_u16_m128i(mtx[j]), 15)); + __m128i tmp = vreinterpretq_m128i_u32( + vshrq_n_u32(vreinterpretq_u32_m128i(mtx[j]), 16)); + uint32x4_t vec_res = vandq_u32(vreinterpretq_u32_m128i(mtx[j]), + vreinterpretq_u32_m128i(tmp)); +#if defined(__aarch64__) || defined(_M_ARM64) + int t = vaddvq_u32(vec_res) ? 1 : 0; +#else + uint64x2_t sumh = vpaddlq_u32(vec_res); + int t = vgetq_lane_u64(sumh, 0) + vgetq_lane_u64(sumh, 1); +#endif + res |= (t << j); + } + return res; +} + +static int _sse2neon_aggregate_ranges_8x16(int la, int lb, __m128i mtx[16]) +{ + int res = 0; + int m = (1 << la) - 1; + uint8x8_t vec_mask = vld1_u8(_sse2neon_cmpestr_mask8b); + uint8x8_t t_lo = vtst_u8(vdup_n_u8(m & 0xff), vec_mask); + uint8x8_t t_hi = vtst_u8(vdup_n_u8(m >> 8), vec_mask); + uint8x16_t vec = vcombine_u8(t_lo, t_hi); + for (int j = 0; j < lb; j++) { + mtx[j] = vreinterpretq_m128i_u8( + vandq_u8(vec, vreinterpretq_u8_m128i(mtx[j]))); + mtx[j] = vreinterpretq_m128i_u8( + vshrq_n_u8(vreinterpretq_u8_m128i(mtx[j]), 7)); + __m128i tmp = vreinterpretq_m128i_u16( + vshrq_n_u16(vreinterpretq_u16_m128i(mtx[j]), 8)); + uint16x8_t vec_res = vandq_u16(vreinterpretq_u16_m128i(mtx[j]), + vreinterpretq_u16_m128i(tmp)); + int t = _sse2neon_vaddvq_u16(vec_res) ? 1 : 0; + res |= (t << j); + } + return res; +} + +#define SSE2NEON_CMP_RANGES_IS_BYTE 1 +#define SSE2NEON_CMP_RANGES_IS_WORD 0 + +/* clang-format off */ +#define SSE2NEON_GENERATE_CMP_RANGES(prefix) \ + prefix##IMPL(byte, uint, u, prefix##IS_BYTE) \ + prefix##IMPL(byte, int, s, prefix##IS_BYTE) \ + prefix##IMPL(word, uint, u, prefix##IS_WORD) \ + prefix##IMPL(word, int, s, prefix##IS_WORD) +/* clang-format on */ + +SSE2NEON_GENERATE_CMP_RANGES(SSE2NEON_CMP_RANGES_) + +#undef SSE2NEON_CMP_RANGES_IS_BYTE +#undef SSE2NEON_CMP_RANGES_IS_WORD + +static int _sse2neon_cmp_byte_equal_each(__m128i a, int la, __m128i b, int lb) +{ + uint8x16_t mtx = + vceqq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b)); + int m0 = (la < lb) ? 0 : ((1 << la) - (1 << lb)); + int m1 = 0x10000 - (1 << la); + int tb = 0x10000 - (1 << lb); + uint8x8_t vec_mask, vec0_lo, vec0_hi, vec1_lo, vec1_hi; + uint8x8_t tmp_lo, tmp_hi, res_lo, res_hi; + vec_mask = vld1_u8(_sse2neon_cmpestr_mask8b); + vec0_lo = vtst_u8(vdup_n_u8(m0), vec_mask); + vec0_hi = vtst_u8(vdup_n_u8(m0 >> 8), vec_mask); + vec1_lo = vtst_u8(vdup_n_u8(m1), vec_mask); + vec1_hi = vtst_u8(vdup_n_u8(m1 >> 8), vec_mask); + tmp_lo = vtst_u8(vdup_n_u8(tb), vec_mask); + tmp_hi = vtst_u8(vdup_n_u8(tb >> 8), vec_mask); + + res_lo = vbsl_u8(vec0_lo, vdup_n_u8(0), vget_low_u8(mtx)); + res_hi = vbsl_u8(vec0_hi, vdup_n_u8(0), vget_high_u8(mtx)); + res_lo = vbsl_u8(vec1_lo, tmp_lo, res_lo); + res_hi = vbsl_u8(vec1_hi, tmp_hi, res_hi); + res_lo = vand_u8(res_lo, vec_mask); + res_hi = vand_u8(res_hi, vec_mask); + + int res = _sse2neon_vaddv_u8(res_lo) + (_sse2neon_vaddv_u8(res_hi) << 8); + return res; +} + +static int _sse2neon_cmp_word_equal_each(__m128i a, int la, __m128i b, int lb) +{ + uint16x8_t mtx = + vceqq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b)); + int m0 = (la < lb) ? 0 : ((1 << la) - (1 << lb)); + int m1 = 0x100 - (1 << la); + int tb = 0x100 - (1 << lb); + uint16x8_t vec_mask = vld1q_u16(_sse2neon_cmpestr_mask16b); + uint16x8_t vec0 = vtstq_u16(vdupq_n_u16(m0), vec_mask); + uint16x8_t vec1 = vtstq_u16(vdupq_n_u16(m1), vec_mask); + uint16x8_t tmp = vtstq_u16(vdupq_n_u16(tb), vec_mask); + mtx = vbslq_u16(vec0, vdupq_n_u16(0), mtx); + mtx = vbslq_u16(vec1, tmp, mtx); + mtx = vandq_u16(mtx, vec_mask); + return _sse2neon_vaddvq_u16(mtx); +} + +#define SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UBYTE 1 +#define SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UWORD 0 + +#define SSE2NEON_AGGREGATE_EQUAL_ORDER_IMPL(size, number_of_lanes, data_type) \ + static int _sse2neon_aggregate_equal_ordered_##size##x##number_of_lanes( \ + int bound, int la, int lb, __m128i mtx[16]) \ + { \ + int res = 0; \ + int m1 = SSE2NEON_IIF(data_type)(0x10000, 0x100) - (1 << la); \ + uint##size##x8_t vec_mask = SSE2NEON_IIF(data_type)( \ + vld1_u##size(_sse2neon_cmpestr_mask##size##b), \ + vld1q_u##size(_sse2neon_cmpestr_mask##size##b)); \ + uint##size##x##number_of_lanes##_t vec1 = SSE2NEON_IIF(data_type)( \ + vcombine_u##size(vtst_u##size(vdup_n_u##size(m1), vec_mask), \ + vtst_u##size(vdup_n_u##size(m1 >> 8), vec_mask)), \ + vtstq_u##size(vdupq_n_u##size(m1), vec_mask)); \ + uint##size##x##number_of_lanes##_t vec_minusone = vdupq_n_u##size(-1); \ + uint##size##x##number_of_lanes##_t vec_zero = vdupq_n_u##size(0); \ + for (int j = 0; j < lb; j++) { \ + mtx[j] = vreinterpretq_m128i_u##size(vbslq_u##size( \ + vec1, vec_minusone, vreinterpretq_u##size##_m128i(mtx[j]))); \ + } \ + for (int j = lb; j < bound; j++) { \ + mtx[j] = vreinterpretq_m128i_u##size( \ + vbslq_u##size(vec1, vec_minusone, vec_zero)); \ + } \ + unsigned SSE2NEON_IIF(data_type)(char, short) *ptr = \ + (unsigned SSE2NEON_IIF(data_type)(char, short) *) mtx; \ + for (int i = 0; i < bound; i++) { \ + int val = 1; \ + for (int j = 0, k = i; j < bound - i && k < bound; j++, k++) \ + val &= ptr[k * bound + j]; \ + res += val << i; \ + } \ + return res; \ + } + +/* clang-format off */ +#define SSE2NEON_GENERATE_AGGREGATE_EQUAL_ORDER(prefix) \ + prefix##IMPL(8, 16, prefix##IS_UBYTE) \ + prefix##IMPL(16, 8, prefix##IS_UWORD) +/* clang-format on */ + +SSE2NEON_GENERATE_AGGREGATE_EQUAL_ORDER(SSE2NEON_AGGREGATE_EQUAL_ORDER_) + +#undef SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UBYTE +#undef SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UWORD + +/* clang-format off */ +#define SSE2NEON_GENERATE_CMP_EQUAL_ORDERED(prefix) \ + prefix##IMPL(byte) \ + prefix##IMPL(word) +/* clang-format on */ + +SSE2NEON_GENERATE_CMP_EQUAL_ORDERED(SSE2NEON_CMP_EQUAL_ORDERED_) + +#define SSE2NEON_CMPESTR_LIST \ + _(CMP_UBYTE_EQUAL_ANY, cmp_byte_equal_any) \ + _(CMP_UWORD_EQUAL_ANY, cmp_word_equal_any) \ + _(CMP_SBYTE_EQUAL_ANY, cmp_byte_equal_any) \ + _(CMP_SWORD_EQUAL_ANY, cmp_word_equal_any) \ + _(CMP_UBYTE_RANGES, cmp_ubyte_ranges) \ + _(CMP_UWORD_RANGES, cmp_uword_ranges) \ + _(CMP_SBYTE_RANGES, cmp_sbyte_ranges) \ + _(CMP_SWORD_RANGES, cmp_sword_ranges) \ + _(CMP_UBYTE_EQUAL_EACH, cmp_byte_equal_each) \ + _(CMP_UWORD_EQUAL_EACH, cmp_word_equal_each) \ + _(CMP_SBYTE_EQUAL_EACH, cmp_byte_equal_each) \ + _(CMP_SWORD_EQUAL_EACH, cmp_word_equal_each) \ + _(CMP_UBYTE_EQUAL_ORDERED, cmp_byte_equal_ordered) \ + _(CMP_UWORD_EQUAL_ORDERED, cmp_word_equal_ordered) \ + _(CMP_SBYTE_EQUAL_ORDERED, cmp_byte_equal_ordered) \ + _(CMP_SWORD_EQUAL_ORDERED, cmp_word_equal_ordered) + +enum { +#define _(name, func_suffix) name, + SSE2NEON_CMPESTR_LIST +#undef _ +}; +typedef int (*cmpestr_func_t)(__m128i a, int la, __m128i b, int lb); +static cmpestr_func_t _sse2neon_cmpfunc_table[] = { +#define _(name, func_suffix) _sse2neon_##func_suffix, + SSE2NEON_CMPESTR_LIST +#undef _ +}; + +FORCE_INLINE int _sse2neon_sido_negative(int res, int lb, int imm8, int bound) +{ + switch (imm8 & 0x30) { + case _SIDD_NEGATIVE_POLARITY: + res ^= 0xffffffff; + break; + case _SIDD_MASKED_NEGATIVE_POLARITY: + res ^= (1 << lb) - 1; + break; + default: + break; + } + + return res & ((bound == 8) ? 0xFF : 0xFFFF); +} + +FORCE_INLINE int _sse2neon_clz(unsigned int x) +{ +#ifdef _MSC_VER + unsigned long cnt = 0; + if (_BitScanReverse(&cnt, x)) + return 31 - cnt; + return 32; +#else + return x != 0 ? __builtin_clz(x) : 32; +#endif +} + +FORCE_INLINE int _sse2neon_ctz(unsigned int x) +{ +#ifdef _MSC_VER + unsigned long cnt = 0; + if (_BitScanForward(&cnt, x)) + return cnt; + return 32; +#else + return x != 0 ? __builtin_ctz(x) : 32; +#endif +} + +FORCE_INLINE int _sse2neon_ctzll(unsigned long long x) +{ +#ifdef _MSC_VER + unsigned long cnt; +#if defined(SSE2NEON_HAS_BITSCAN64) + if (_BitScanForward64(&cnt, x)) + return (int) (cnt); +#else + if (_BitScanForward(&cnt, (unsigned long) (x))) + return (int) cnt; + if (_BitScanForward(&cnt, (unsigned long) (x >> 32))) + return (int) (cnt + 32); +#endif /* SSE2NEON_HAS_BITSCAN64 */ + return 64; +#else /* assume GNU compatible compilers */ + return x != 0 ? __builtin_ctzll(x) : 64; +#endif +} + +#define SSE2NEON_MIN(x, y) (x) < (y) ? (x) : (y) + +#define SSE2NEON_CMPSTR_SET_UPPER(var, imm) \ + const int var = (imm & 0x01) ? 8 : 16 + +#define SSE2NEON_CMPESTRX_LEN_PAIR(a, b, la, lb) \ + int tmp1 = la ^ (la >> 31); \ + la = tmp1 - (la >> 31); \ + int tmp2 = lb ^ (lb >> 31); \ + lb = tmp2 - (lb >> 31); \ + la = SSE2NEON_MIN(la, bound); \ + lb = SSE2NEON_MIN(lb, bound) + +// Compare all pairs of character in string a and b, +// then aggregate the result. +// As the only difference of PCMPESTR* and PCMPISTR* is the way to calculate the +// length of string, we use SSE2NEON_CMP{I,E}STRX_GET_LEN to get the length of +// string a and b. +#define SSE2NEON_COMP_AGG(a, b, la, lb, imm8, IE) \ + SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); \ + SSE2NEON_##IE##_LEN_PAIR(a, b, la, lb); \ + int r2 = (_sse2neon_cmpfunc_table[imm8 & 0x0f])(a, la, b, lb); \ + r2 = _sse2neon_sido_negative(r2, lb, imm8, bound) + +#define SSE2NEON_CMPSTR_GENERATE_INDEX(r2, bound, imm8) \ + return (r2 == 0) ? bound \ + : ((imm8 & 0x40) ? (31 - _sse2neon_clz(r2)) \ + : _sse2neon_ctz(r2)) + +#define SSE2NEON_CMPSTR_GENERATE_MASK(dst) \ + __m128i dst = vreinterpretq_m128i_u8(vdupq_n_u8(0)); \ + if (imm8 & 0x40) { \ + if (bound == 8) { \ + uint16x8_t tmp = vtstq_u16(vdupq_n_u16(r2), \ + vld1q_u16(_sse2neon_cmpestr_mask16b)); \ + dst = vreinterpretq_m128i_u16(vbslq_u16( \ + tmp, vdupq_n_u16(-1), vreinterpretq_u16_m128i(dst))); \ + } else { \ + uint8x16_t vec_r2 = \ + vcombine_u8(vdup_n_u8(r2), vdup_n_u8(r2 >> 8)); \ + uint8x16_t tmp = \ + vtstq_u8(vec_r2, vld1q_u8(_sse2neon_cmpestr_mask8b)); \ + dst = vreinterpretq_m128i_u8( \ + vbslq_u8(tmp, vdupq_n_u8(-1), vreinterpretq_u8_m128i(dst))); \ + } \ + } else { \ + if (bound == 16) { \ + dst = vreinterpretq_m128i_u16( \ + vsetq_lane_u16(r2 & 0xffff, vreinterpretq_u16_m128i(dst), 0)); \ + } else { \ + dst = vreinterpretq_m128i_u8( \ + vsetq_lane_u8(r2 & 0xff, vreinterpretq_u8_m128i(dst), 0)); \ + } \ + } \ + return dst + +// Compare packed strings in a and b with lengths la and lb using the control +// in imm8, and returns 1 if b did not contain a null character and the +// resulting mask was zero, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestra +FORCE_INLINE int _mm_cmpestra(__m128i a, + int la, + __m128i b, + int lb, + const int imm8) +{ + int lb_cpy = lb; + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); + return !r2 & (lb_cpy > bound); +} + +// Compare packed strings in a and b with lengths la and lb using the control in +// imm8, and returns 1 if the resulting mask was non-zero, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrc +FORCE_INLINE int _mm_cmpestrc(__m128i a, + int la, + __m128i b, + int lb, + const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); + return r2 != 0; +} + +// Compare packed strings in a and b with lengths la and lb using the control +// in imm8, and store the generated index in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestri +FORCE_INLINE int _mm_cmpestri(__m128i a, + int la, + __m128i b, + int lb, + const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); + SSE2NEON_CMPSTR_GENERATE_INDEX(r2, bound, imm8); +} + +// Compare packed strings in a and b with lengths la and lb using the control +// in imm8, and store the generated mask in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrm +FORCE_INLINE __m128i +_mm_cmpestrm(__m128i a, int la, __m128i b, int lb, const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); + SSE2NEON_CMPSTR_GENERATE_MASK(dst); +} + +// Compare packed strings in a and b with lengths la and lb using the control in +// imm8, and returns bit 0 of the resulting bit mask. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestro +FORCE_INLINE int _mm_cmpestro(__m128i a, + int la, + __m128i b, + int lb, + const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); + return r2 & 1; +} + +// Compare packed strings in a and b with lengths la and lb using the control in +// imm8, and returns 1 if any character in a was null, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrs +FORCE_INLINE int _mm_cmpestrs(__m128i a, + int la, + __m128i b, + int lb, + const int imm8) +{ + (void) a; + (void) b; + (void) lb; + SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); + return la <= (bound - 1); +} + +// Compare packed strings in a and b with lengths la and lb using the control in +// imm8, and returns 1 if any character in b was null, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrz +FORCE_INLINE int _mm_cmpestrz(__m128i a, + int la, + __m128i b, + int lb, + const int imm8) +{ + (void) a; + (void) b; + (void) la; + SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); + return lb <= (bound - 1); +} + +#define SSE2NEON_CMPISTRX_LENGTH(str, len, imm8) \ + do { \ + if (imm8 & 0x01) { \ + uint16x8_t equal_mask_##str = \ + vceqq_u16(vreinterpretq_u16_m128i(str), vdupq_n_u16(0)); \ + uint8x8_t res_##str = vshrn_n_u16(equal_mask_##str, 4); \ + uint64_t matches_##str = \ + vget_lane_u64(vreinterpret_u64_u8(res_##str), 0); \ + len = _sse2neon_ctzll(matches_##str) >> 3; \ + } else { \ + uint16x8_t equal_mask_##str = vreinterpretq_u16_u8( \ + vceqq_u8(vreinterpretq_u8_m128i(str), vdupq_n_u8(0))); \ + uint8x8_t res_##str = vshrn_n_u16(equal_mask_##str, 4); \ + uint64_t matches_##str = \ + vget_lane_u64(vreinterpret_u64_u8(res_##str), 0); \ + len = _sse2neon_ctzll(matches_##str) >> 2; \ + } \ + } while (0) + +#define SSE2NEON_CMPISTRX_LEN_PAIR(a, b, la, lb) \ + int la, lb; \ + do { \ + SSE2NEON_CMPISTRX_LENGTH(a, la, imm8); \ + SSE2NEON_CMPISTRX_LENGTH(b, lb, imm8); \ + } while (0) + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and returns 1 if b did not contain a null character and the resulting +// mask was zero, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistra +FORCE_INLINE int _mm_cmpistra(__m128i a, __m128i b, const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); + return !r2 & (lb >= bound); +} + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and returns 1 if the resulting mask was non-zero, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrc +FORCE_INLINE int _mm_cmpistrc(__m128i a, __m128i b, const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); + return r2 != 0; +} + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and store the generated index in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistri +FORCE_INLINE int _mm_cmpistri(__m128i a, __m128i b, const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); + SSE2NEON_CMPSTR_GENERATE_INDEX(r2, bound, imm8); +} + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and store the generated mask in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrm +FORCE_INLINE __m128i _mm_cmpistrm(__m128i a, __m128i b, const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); + SSE2NEON_CMPSTR_GENERATE_MASK(dst); +} + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and returns bit 0 of the resulting bit mask. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistro +FORCE_INLINE int _mm_cmpistro(__m128i a, __m128i b, const int imm8) +{ + SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); + return r2 & 1; +} + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and returns 1 if any character in a was null, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrs +FORCE_INLINE int _mm_cmpistrs(__m128i a, __m128i b, const int imm8) +{ + (void) b; + SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); + int la; + SSE2NEON_CMPISTRX_LENGTH(a, la, imm8); + return la <= (bound - 1); +} + +// Compare packed strings with implicit lengths in a and b using the control in +// imm8, and returns 1 if any character in b was null, and 0 otherwise. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrz +FORCE_INLINE int _mm_cmpistrz(__m128i a, __m128i b, const int imm8) +{ + (void) a; + SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); + int lb; + SSE2NEON_CMPISTRX_LENGTH(b, lb, imm8); + return lb <= (bound - 1); +} + +// Compares the 2 signed 64-bit integers in a and the 2 signed 64-bit integers +// in b for greater than. +FORCE_INLINE __m128i _mm_cmpgt_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + return vreinterpretq_m128i_u64( + vcgtq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +#else + return vreinterpretq_m128i_s64(vshrq_n_s64( + vqsubq_s64(vreinterpretq_s64_m128i(b), vreinterpretq_s64_m128i(a)), + 63)); +#endif +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 16-bit integer v, and stores the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u16 +FORCE_INLINE uint32_t _mm_crc32_u16(uint32_t crc, uint16_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32ch %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#elif ((__ARM_ARCH == 8) && defined(__ARM_FEATURE_CRC32)) || \ + (defined(_M_ARM64) && !defined(__clang__)) + crc = __crc32ch(crc, v); +#else + crc = _mm_crc32_u8(crc, v & 0xff); + crc = _mm_crc32_u8(crc, (v >> 8) & 0xff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 32-bit integer v, and stores the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u32 +FORCE_INLINE uint32_t _mm_crc32_u32(uint32_t crc, uint32_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cw %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#elif ((__ARM_ARCH == 8) && defined(__ARM_FEATURE_CRC32)) || \ + (defined(_M_ARM64) && !defined(__clang__)) + crc = __crc32cw(crc, v); +#else + crc = _mm_crc32_u16(crc, v & 0xffff); + crc = _mm_crc32_u16(crc, (v >> 16) & 0xffff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 64-bit integer v, and stores the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u64 +FORCE_INLINE uint64_t _mm_crc32_u64(uint64_t crc, uint64_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cx %w[c], %w[c], %x[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#elif (defined(_M_ARM64) && !defined(__clang__)) + crc = __crc32cd((uint32_t) crc, v); +#else + crc = _mm_crc32_u32((uint32_t) (crc), v & 0xffffffff); + crc = _mm_crc32_u32((uint32_t) (crc), (v >> 32) & 0xffffffff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 8-bit integer v, and stores the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u8 +FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t crc, uint8_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cb %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#elif ((__ARM_ARCH == 8) && defined(__ARM_FEATURE_CRC32)) || \ + (defined(_M_ARM64) && !defined(__clang__)) + crc = __crc32cb(crc, v); +#else + crc ^= v; + for (int bit = 0; bit < 8; bit++) { + if (crc & 1) + crc = (crc >> 1) ^ UINT32_C(0x82f63b78); + else + crc = (crc >> 1); + } +#endif + return crc; +} + +/* AES */ + +#if !defined(__ARM_FEATURE_CRYPTO) && (!defined(_M_ARM64) || defined(__clang__)) +/* clang-format off */ +#define SSE2NEON_AES_SBOX(w) \ + { \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), \ + w(0xc5), w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), \ + w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), \ + w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \ + w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), \ + w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), \ + w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), w(0x04), \ + w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), \ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), \ + w(0x75), w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), \ + w(0x5a), w(0xa0), w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), \ + w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), \ + w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \ + w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), \ + w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), \ + w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), \ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), \ + w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), \ + w(0xf3), w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), \ + w(0x97), w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), \ + w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), w(0x81), w(0x4f), \ + w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \ + w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), \ + w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), \ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), \ + w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), \ + w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), \ + w(0x7a), w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), \ + w(0x1c), w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), \ + w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), \ + w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \ + w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), \ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), \ + w(0x94), w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), \ + w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), \ + w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), \ + w(0xb0), w(0x54), w(0xbb), w(0x16) \ + } +#define SSE2NEON_AES_RSBOX(w) \ + { \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), \ + w(0x38), w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), \ + w(0xd7), w(0xfb), w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), \ + w(0x2f), w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), \ + w(0xc4), w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), \ + w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), \ + w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e), w(0x08), \ + w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2), \ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), \ + w(0x25), w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), \ + w(0x98), w(0x16), w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), \ + w(0x65), w(0xb6), w(0x92), w(0x6c), w(0x70), w(0x48), w(0x50), \ + w(0xfd), w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), \ + w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), \ + w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), \ + w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06), \ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), \ + w(0x02), w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), \ + w(0x8a), w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), \ + w(0x67), w(0xdc), w(0xea), w(0x97), w(0xf2), w(0xcf), w(0xce), \ + w(0xf0), w(0xb4), w(0xe6), w(0x73), w(0x96), w(0xac), w(0x74), \ + w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), \ + w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), \ + w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), \ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), \ + w(0x1b), w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), \ + w(0x79), w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), \ + w(0xcd), w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), \ + w(0x88), w(0x07), w(0xc7), w(0x31), w(0xb1), w(0x12), w(0x10), \ + w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f), w(0x60), w(0x51), \ + w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), \ + w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), \ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), \ + w(0xb0), w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), \ + w(0x99), w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), \ + w(0x77), w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), \ + w(0x55), w(0x21), w(0x0c), w(0x7d) \ + } +/* clang-format on */ + +/* X Macro trick. See https://en.wikipedia.org/wiki/X_Macro */ +#define SSE2NEON_AES_H0(x) (x) +static const uint8_t _sse2neon_sbox[256] = SSE2NEON_AES_SBOX(SSE2NEON_AES_H0); +static const uint8_t _sse2neon_rsbox[256] = SSE2NEON_AES_RSBOX(SSE2NEON_AES_H0); +#undef SSE2NEON_AES_H0 + +/* x_time function and matrix multiply function */ +#if !defined(__aarch64__) && !defined(_M_ARM64) +#define SSE2NEON_XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) +#define SSE2NEON_MULTIPLY(x, y) \ + (((y & 1) * x) ^ ((y >> 1 & 1) * SSE2NEON_XT(x)) ^ \ + ((y >> 2 & 1) * SSE2NEON_XT(SSE2NEON_XT(x))) ^ \ + ((y >> 3 & 1) * SSE2NEON_XT(SSE2NEON_XT(SSE2NEON_XT(x)))) ^ \ + ((y >> 4 & 1) * SSE2NEON_XT(SSE2NEON_XT(SSE2NEON_XT(SSE2NEON_XT(x)))))) +#endif + +// In the absence of crypto extensions, implement aesenc using regular NEON +// intrinsics instead. See: +// https://www.workofard.com/2017/01/accelerated-aes-for-the-arm64-linux-kernel/ +// https://www.workofard.com/2017/07/ghash-for-low-end-cores/ and +// for more information. +FORCE_INLINE __m128i _mm_aesenc_si128(__m128i a, __m128i RoundKey) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + static const uint8_t shift_rows[] = { + 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3, + 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb, + }; + static const uint8_t ror32by8[] = { + 0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, + 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc, + }; + + uint8x16_t v; + uint8x16_t w = vreinterpretq_u8_m128i(a); + + /* shift rows */ + w = vqtbl1q_u8(w, vld1q_u8(shift_rows)); + + /* sub bytes */ + // Here, we separate the whole 256-bytes table into 4 64-bytes tables, and + // look up each of the table. After each lookup, we load the next table + // which locates at the next 64-bytes. In the meantime, the index in the + // table would be smaller than it was, so the index parameters of + // `vqtbx4q_u8()` need to be added the same constant as the loaded tables. + v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_sbox), w); + // 'w-0x40' equals to 'vsubq_u8(w, vdupq_n_u8(0x40))' + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x40), w - 0x40); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x80), w - 0x80); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0xc0), w - 0xc0); + + /* mix columns */ + w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); + w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); + w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); + + /* add round key */ + return vreinterpretq_m128i_u8(w) ^ RoundKey; + +#else /* ARMv7-A implementation for a table-based AES */ +#define SSE2NEON_AES_B2W(b0, b1, b2, b3) \ + (((uint32_t) (b3) << 24) | ((uint32_t) (b2) << 16) | \ + ((uint32_t) (b1) << 8) | (uint32_t) (b0)) +// muliplying 'x' by 2 in GF(2^8) +#define SSE2NEON_AES_F2(x) ((x << 1) ^ (((x >> 7) & 1) * 0x011b /* WPOLY */)) +// muliplying 'x' by 3 in GF(2^8) +#define SSE2NEON_AES_F3(x) (SSE2NEON_AES_F2(x) ^ x) +#define SSE2NEON_AES_U0(p) \ + SSE2NEON_AES_B2W(SSE2NEON_AES_F2(p), p, p, SSE2NEON_AES_F3(p)) +#define SSE2NEON_AES_U1(p) \ + SSE2NEON_AES_B2W(SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p, p) +#define SSE2NEON_AES_U2(p) \ + SSE2NEON_AES_B2W(p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p) +#define SSE2NEON_AES_U3(p) \ + SSE2NEON_AES_B2W(p, p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p)) + + // this generates a table containing every possible permutation of + // shift_rows() and sub_bytes() with mix_columns(). + static const uint32_t ALIGN_STRUCT(16) aes_table[4][256] = { + SSE2NEON_AES_SBOX(SSE2NEON_AES_U0), + SSE2NEON_AES_SBOX(SSE2NEON_AES_U1), + SSE2NEON_AES_SBOX(SSE2NEON_AES_U2), + SSE2NEON_AES_SBOX(SSE2NEON_AES_U3), + }; +#undef SSE2NEON_AES_B2W +#undef SSE2NEON_AES_F2 +#undef SSE2NEON_AES_F3 +#undef SSE2NEON_AES_U0 +#undef SSE2NEON_AES_U1 +#undef SSE2NEON_AES_U2 +#undef SSE2NEON_AES_U3 + + uint32_t x0 = _mm_cvtsi128_si32(a); // get a[31:0] + uint32_t x1 = + _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0x55)); // get a[63:32] + uint32_t x2 = + _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xAA)); // get a[95:64] + uint32_t x3 = + _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xFF)); // get a[127:96] + + // finish the modulo addition step in mix_columns() + __m128i out = _mm_set_epi32( + (aes_table[0][x3 & 0xff] ^ aes_table[1][(x0 >> 8) & 0xff] ^ + aes_table[2][(x1 >> 16) & 0xff] ^ aes_table[3][x2 >> 24]), + (aes_table[0][x2 & 0xff] ^ aes_table[1][(x3 >> 8) & 0xff] ^ + aes_table[2][(x0 >> 16) & 0xff] ^ aes_table[3][x1 >> 24]), + (aes_table[0][x1 & 0xff] ^ aes_table[1][(x2 >> 8) & 0xff] ^ + aes_table[2][(x3 >> 16) & 0xff] ^ aes_table[3][x0 >> 24]), + (aes_table[0][x0 & 0xff] ^ aes_table[1][(x1 >> 8) & 0xff] ^ + aes_table[2][(x2 >> 16) & 0xff] ^ aes_table[3][x3 >> 24])); + + return _mm_xor_si128(out, RoundKey); +#endif +} + +// Perform one round of an AES decryption flow on data (state) in a using the +// round key in RoundKey, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128 +FORCE_INLINE __m128i _mm_aesdec_si128(__m128i a, __m128i RoundKey) +{ +#if defined(__aarch64__) + static const uint8_t inv_shift_rows[] = { + 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb, + 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3, + }; + static const uint8_t ror32by8[] = { + 0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, + 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc, + }; + + uint8x16_t v; + uint8x16_t w = vreinterpretq_u8_m128i(a); + + // inverse shift rows + w = vqtbl1q_u8(w, vld1q_u8(inv_shift_rows)); + + // inverse sub bytes + v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_rsbox), w); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x40), w - 0x40); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x80), w - 0x80); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0xc0), w - 0xc0); + + // inverse mix columns + // multiplying 'v' by 4 in GF(2^8) + w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); + w = (w << 1) ^ (uint8x16_t) (((int8x16_t) w >> 7) & 0x1b); + v ^= w; + v ^= (uint8x16_t) vrev32q_u16((uint16x8_t) w); + + w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & + 0x1b); // muliplying 'v' by 2 in GF(2^8) + w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); + w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); + + // add round key + return vreinterpretq_m128i_u8(w) ^ RoundKey; + +#else /* ARMv7-A NEON implementation */ + /* FIXME: optimized for NEON */ + uint8_t i, e, f, g, h, v[4][4]; + uint8_t *_a = (uint8_t *) &a; + for (i = 0; i < 16; ++i) { + v[((i / 4) + (i % 4)) % 4][i % 4] = _sse2neon_rsbox[_a[i]]; + } + + // inverse mix columns + for (i = 0; i < 4; ++i) { + e = v[i][0]; + f = v[i][1]; + g = v[i][2]; + h = v[i][3]; + + v[i][0] = SSE2NEON_MULTIPLY(e, 0x0e) ^ SSE2NEON_MULTIPLY(f, 0x0b) ^ + SSE2NEON_MULTIPLY(g, 0x0d) ^ SSE2NEON_MULTIPLY(h, 0x09); + v[i][1] = SSE2NEON_MULTIPLY(e, 0x09) ^ SSE2NEON_MULTIPLY(f, 0x0e) ^ + SSE2NEON_MULTIPLY(g, 0x0b) ^ SSE2NEON_MULTIPLY(h, 0x0d); + v[i][2] = SSE2NEON_MULTIPLY(e, 0x0d) ^ SSE2NEON_MULTIPLY(f, 0x09) ^ + SSE2NEON_MULTIPLY(g, 0x0e) ^ SSE2NEON_MULTIPLY(h, 0x0b); + v[i][3] = SSE2NEON_MULTIPLY(e, 0x0b) ^ SSE2NEON_MULTIPLY(f, 0x0d) ^ + SSE2NEON_MULTIPLY(g, 0x09) ^ SSE2NEON_MULTIPLY(h, 0x0e); + } + + return vreinterpretq_m128i_u8(vld1q_u8((uint8_t *) v)) ^ RoundKey; +#endif +} + +// Perform the last round of an AES encryption flow on data (state) in a using +// the round key in RoundKey, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128 +FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) +{ +#if defined(__aarch64__) + static const uint8_t shift_rows[] = { + 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3, + 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb, + }; + + uint8x16_t v; + uint8x16_t w = vreinterpretq_u8_m128i(a); + + // shift rows + w = vqtbl1q_u8(w, vld1q_u8(shift_rows)); + + // sub bytes + v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_sbox), w); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x40), w - 0x40); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x80), w - 0x80); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0xc0), w - 0xc0); + + // add round key + return vreinterpretq_m128i_u8(v) ^ RoundKey; + +#else /* ARMv7-A implementation */ + uint8_t v[16] = { + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 0)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 5)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 10)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 15)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 4)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 9)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 14)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 3)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 8)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 13)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 2)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 7)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 12)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 1)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 6)], + _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 11)], + }; + + return vreinterpretq_m128i_u8(vld1q_u8(v)) ^ RoundKey; +#endif +} + +// Perform the last round of an AES decryption flow on data (state) in a using +// the round key in RoundKey, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128 +FORCE_INLINE __m128i _mm_aesdeclast_si128(__m128i a, __m128i RoundKey) +{ +#if defined(__aarch64__) + static const uint8_t inv_shift_rows[] = { + 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb, + 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3, + }; + + uint8x16_t v; + uint8x16_t w = vreinterpretq_u8_m128i(a); + + // inverse shift rows + w = vqtbl1q_u8(w, vld1q_u8(inv_shift_rows)); + + // inverse sub bytes + v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_rsbox), w); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x40), w - 0x40); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x80), w - 0x80); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0xc0), w - 0xc0); + + // add round key + return vreinterpretq_m128i_u8(v) ^ RoundKey; + +#else /* ARMv7-A NEON implementation */ + /* FIXME: optimized for NEON */ + uint8_t v[4][4]; + uint8_t *_a = (uint8_t *) &a; + for (int i = 0; i < 16; ++i) { + v[((i / 4) + (i % 4)) % 4][i % 4] = _sse2neon_rsbox[_a[i]]; + } + + return vreinterpretq_m128i_u8(vld1q_u8((uint8_t *) v)) ^ RoundKey; +#endif +} + +// Perform the InvMixColumns transformation on a and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesimc_si128 +FORCE_INLINE __m128i _mm_aesimc_si128(__m128i a) +{ +#if defined(__aarch64__) + static const uint8_t ror32by8[] = { + 0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, + 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc, + }; + uint8x16_t v = vreinterpretq_u8_m128i(a); + uint8x16_t w; + + // multiplying 'v' by 4 in GF(2^8) + w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); + w = (w << 1) ^ (uint8x16_t) (((int8x16_t) w >> 7) & 0x1b); + v ^= w; + v ^= (uint8x16_t) vrev32q_u16((uint16x8_t) w); + + // multiplying 'v' by 2 in GF(2^8) + w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); + w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); + w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); + return vreinterpretq_m128i_u8(w); + +#else /* ARMv7-A NEON implementation */ + uint8_t i, e, f, g, h, v[4][4]; + vst1q_u8((uint8_t *) v, vreinterpretq_u8_m128i(a)); + for (i = 0; i < 4; ++i) { + e = v[i][0]; + f = v[i][1]; + g = v[i][2]; + h = v[i][3]; + + v[i][0] = SSE2NEON_MULTIPLY(e, 0x0e) ^ SSE2NEON_MULTIPLY(f, 0x0b) ^ + SSE2NEON_MULTIPLY(g, 0x0d) ^ SSE2NEON_MULTIPLY(h, 0x09); + v[i][1] = SSE2NEON_MULTIPLY(e, 0x09) ^ SSE2NEON_MULTIPLY(f, 0x0e) ^ + SSE2NEON_MULTIPLY(g, 0x0b) ^ SSE2NEON_MULTIPLY(h, 0x0d); + v[i][2] = SSE2NEON_MULTIPLY(e, 0x0d) ^ SSE2NEON_MULTIPLY(f, 0x09) ^ + SSE2NEON_MULTIPLY(g, 0x0e) ^ SSE2NEON_MULTIPLY(h, 0x0b); + v[i][3] = SSE2NEON_MULTIPLY(e, 0x0b) ^ SSE2NEON_MULTIPLY(f, 0x0d) ^ + SSE2NEON_MULTIPLY(g, 0x09) ^ SSE2NEON_MULTIPLY(h, 0x0e); + } + + return vreinterpretq_m128i_u8(vld1q_u8((uint8_t *) v)); +#endif +} + +// Assist in expanding the AES cipher key by computing steps towards generating +// a round key for encryption cipher using data from a and an 8-bit round +// constant specified in imm8, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aeskeygenassist_si128 +// +// Emits the Advanced Encryption Standard (AES) instruction aeskeygenassist. +// This instruction generates a round key for AES encryption. See +// https://kazakov.life/2017/11/01/cryptocurrency-mining-on-ios-devices/ +// for details. +FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon) +{ +#if defined(__aarch64__) + uint8x16_t _a = vreinterpretq_u8_m128i(a); + uint8x16_t v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_sbox), _a); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x40), _a - 0x40); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x80), _a - 0x80); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0xc0), _a - 0xc0); + + uint32x4_t v_u32 = vreinterpretq_u32_u8(v); + uint32x4_t ror_v = vorrq_u32(vshrq_n_u32(v_u32, 8), vshlq_n_u32(v_u32, 24)); + uint32x4_t ror_xor_v = veorq_u32(ror_v, vdupq_n_u32(rcon)); + + return vreinterpretq_m128i_u32(vtrn2q_u32(v_u32, ror_xor_v)); + +#else /* ARMv7-A NEON implementation */ + uint32_t X1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0x55)); + uint32_t X3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xFF)); + for (int i = 0; i < 4; ++i) { + ((uint8_t *) &X1)[i] = _sse2neon_sbox[((uint8_t *) &X1)[i]]; + ((uint8_t *) &X3)[i] = _sse2neon_sbox[((uint8_t *) &X3)[i]]; + } + return _mm_set_epi32(((X3 >> 8) | (X3 << 24)) ^ rcon, X3, + ((X1 >> 8) | (X1 << 24)) ^ rcon, X1); +#endif +} +#undef SSE2NEON_AES_SBOX +#undef SSE2NEON_AES_RSBOX + +#if defined(__aarch64__) +#undef SSE2NEON_XT +#undef SSE2NEON_MULTIPLY +#endif + +#else /* __ARM_FEATURE_CRYPTO */ +// Implements equivalent of 'aesenc' by combining AESE (with an empty key) and +// AESMC and then manually applying the real key as an xor operation. This +// unfortunately means an additional xor op; the compiler should be able to +// optimize this away for repeated calls however. See +// https://blog.michaelbrase.com/2018/05/08/emulating-x86-aes-intrinsics-on-armv8-a +// for more details. +FORCE_INLINE __m128i _mm_aesenc_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8(veorq_u8( + vaesmcq_u8(vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), + vreinterpretq_u8_m128i(b))); +} + +// Perform one round of an AES decryption flow on data (state) in a using the +// round key in RoundKey, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128 +FORCE_INLINE __m128i _mm_aesdec_si128(__m128i a, __m128i RoundKey) +{ + return vreinterpretq_m128i_u8(veorq_u8( + vaesimcq_u8(vaesdq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), + vreinterpretq_u8_m128i(RoundKey))); +} + +// Perform the last round of an AES encryption flow on data (state) in a using +// the round key in RoundKey, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128 +FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) +{ + return _mm_xor_si128(vreinterpretq_m128i_u8(vaeseq_u8( + vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), + RoundKey); +} + +// Perform the last round of an AES decryption flow on data (state) in a using +// the round key in RoundKey, and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128 +FORCE_INLINE __m128i _mm_aesdeclast_si128(__m128i a, __m128i RoundKey) +{ + return vreinterpretq_m128i_u8( + veorq_u8(vaesdq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0)), + vreinterpretq_u8_m128i(RoundKey))); +} + +// Perform the InvMixColumns transformation on a and store the result in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesimc_si128 +FORCE_INLINE __m128i _mm_aesimc_si128(__m128i a) +{ + return vreinterpretq_m128i_u8(vaesimcq_u8(vreinterpretq_u8_m128i(a))); +} + +// Assist in expanding the AES cipher key by computing steps towards generating +// a round key for encryption cipher using data from a and an 8-bit round +// constant specified in imm8, and store the result in dst." +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aeskeygenassist_si128 +FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon) +{ + // AESE does ShiftRows and SubBytes on A + uint8x16_t u8 = vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0)); + +#ifndef _MSC_VER + uint8x16_t dest = { + // Undo ShiftRows step from AESE and extract X1 and X3 + u8[0x4], u8[0x1], u8[0xE], u8[0xB], // SubBytes(X1) + u8[0x1], u8[0xE], u8[0xB], u8[0x4], // ROT(SubBytes(X1)) + u8[0xC], u8[0x9], u8[0x6], u8[0x3], // SubBytes(X3) + u8[0x9], u8[0x6], u8[0x3], u8[0xC], // ROT(SubBytes(X3)) + }; + uint32x4_t r = {0, (unsigned) rcon, 0, (unsigned) rcon}; + return vreinterpretq_m128i_u8(dest) ^ vreinterpretq_m128i_u32(r); +#else + // We have to do this hack because MSVC is strictly adhering to the CPP + // standard, in particular C++03 8.5.1 sub-section 15, which states that + // unions must be initialized by their first member type. + + // As per the Windows ARM64 ABI, it is always little endian, so this works + __n128 dest{ + ((uint64_t) u8.n128_u8[0x4] << 0) | ((uint64_t) u8.n128_u8[0x1] << 8) | + ((uint64_t) u8.n128_u8[0xE] << 16) | + ((uint64_t) u8.n128_u8[0xB] << 24) | + ((uint64_t) u8.n128_u8[0x1] << 32) | + ((uint64_t) u8.n128_u8[0xE] << 40) | + ((uint64_t) u8.n128_u8[0xB] << 48) | + ((uint64_t) u8.n128_u8[0x4] << 56), + ((uint64_t) u8.n128_u8[0xC] << 0) | ((uint64_t) u8.n128_u8[0x9] << 8) | + ((uint64_t) u8.n128_u8[0x6] << 16) | + ((uint64_t) u8.n128_u8[0x3] << 24) | + ((uint64_t) u8.n128_u8[0x9] << 32) | + ((uint64_t) u8.n128_u8[0x6] << 40) | + ((uint64_t) u8.n128_u8[0x3] << 48) | + ((uint64_t) u8.n128_u8[0xC] << 56)}; + + dest.n128_u32[1] = dest.n128_u32[1] ^ rcon; + dest.n128_u32[3] = dest.n128_u32[3] ^ rcon; + + return dest; +#endif +} +#endif + +/* Others */ + +// Perform a carry-less multiplication of two 64-bit integers, selected from a +// and b according to imm8, and store the results in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128 +FORCE_INLINE __m128i _mm_clmulepi64_si128(__m128i _a, __m128i _b, const int imm) +{ + uint64x2_t a = vreinterpretq_u64_m128i(_a); + uint64x2_t b = vreinterpretq_u64_m128i(_b); + switch (imm & 0x11) { + case 0x00: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_low_u64(a), vget_low_u64(b))); + case 0x01: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_high_u64(a), vget_low_u64(b))); + case 0x10: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_low_u64(a), vget_high_u64(b))); + case 0x11: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_high_u64(a), vget_high_u64(b))); + default: + abort(); + } +} + +FORCE_INLINE unsigned int _sse2neon_mm_get_denormals_zero_mode(void) +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) || defined(_M_ARM64) + r.value = _sse2neon_get_fpcr(); +#else + __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + return r.field.bit24 ? _MM_DENORMALS_ZERO_ON : _MM_DENORMALS_ZERO_OFF; +} + +// Count the number of bits set to 1 in unsigned 32-bit integer a, and +// return that count in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_popcnt_u32 +FORCE_INLINE int _mm_popcnt_u32(unsigned int a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(a); +#elif defined(_MSC_VER) + return _CountOneBits(a); +#else + return (int) vaddlv_u8(vcnt_u8(vcreate_u8((uint64_t) a))); +#endif +#else + uint32_t count = 0; + uint8x8_t input_val, count8x8_val; + uint16x4_t count16x4_val; + uint32x2_t count32x2_val; + + input_val = vld1_u8((uint8_t *) &a); + count8x8_val = vcnt_u8(input_val); + count16x4_val = vpaddl_u8(count8x8_val); + count32x2_val = vpaddl_u16(count16x4_val); + + vst1_u32(&count, count32x2_val); + return count; +#endif +} + +// Count the number of bits set to 1 in unsigned 64-bit integer a, and +// return that count in dst. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_popcnt_u64 +FORCE_INLINE int64_t _mm_popcnt_u64(uint64_t a) +{ +#if defined(__aarch64__) || defined(_M_ARM64) +#if __has_builtin(__builtin_popcountll) + return __builtin_popcountll(a); +#elif defined(_MSC_VER) + return _CountOneBits64(a); +#else + return (int64_t) vaddlv_u8(vcnt_u8(vcreate_u8(a))); +#endif +#else + uint64_t count = 0; + uint8x8_t input_val, count8x8_val; + uint16x4_t count16x4_val; + uint32x2_t count32x2_val; + uint64x1_t count64x1_val; + + input_val = vld1_u8((uint8_t *) &a); + count8x8_val = vcnt_u8(input_val); + count16x4_val = vpaddl_u8(count8x8_val); + count32x2_val = vpaddl_u16(count16x4_val); + count64x1_val = vpaddl_u32(count32x2_val); + vst1_u64(&count, count64x1_val); + return count; +#endif +} + +FORCE_INLINE void _sse2neon_mm_set_denormals_zero_mode(unsigned int flag) +{ + // AArch32 Advanced SIMD arithmetic always uses the Flush-to-zero setting, + // regardless of the value of the FZ bit. + union { + fpcr_bitfield field; +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) || defined(_M_ARM64) + r.value = _sse2neon_get_fpcr(); +#else + __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + r.field.bit24 = (flag & _MM_DENORMALS_ZERO_MASK) == _MM_DENORMALS_ZERO_ON; + +#if defined(__aarch64__) || defined(_M_ARM64) + _sse2neon_set_fpcr(r.value); +#else + __asm__ __volatile__("vmsr FPSCR, %0" ::"r"(r)); /* write */ +#endif +} + +// Return the current 64-bit value of the processor's time-stamp counter. +// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=rdtsc +FORCE_INLINE uint64_t __rdtsc(void) +{ +#if defined(__aarch64__) || defined(_M_ARM64) + uint64_t val; + + /* According to ARM DDI 0487F.c, from Armv8.0 to Armv8.5 inclusive, the + * system counter is at least 56 bits wide; from Armv8.6, the counter + * must be 64 bits wide. So the system counter could be less than 64 + * bits wide and it is attributed with the flag 'cap_user_time_short' + * is true. + */ +#if defined(_MSC_VER) + val = _ReadStatusReg(ARM64_SYSREG(3, 3, 14, 0, 2)); +#else + __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(val)); +#endif + + return val; +#else + uint32_t pmccntr, pmuseren, pmcntenset; + // Read the user mode Performance Monitoring Unit (PMU) + // User Enable Register (PMUSERENR) access permissions. + __asm__ __volatile__("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren)); + if (pmuseren & 1) { // Allows reading PMUSERENR for user mode code. + __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset)); + if (pmcntenset & 0x80000000UL) { // Is it counting? + __asm__ __volatile__("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); + // The counter is set up to count every 64th cycle + return (uint64_t) (pmccntr) << 6; + } + } + + // Fallback to syscall as we can't enable PMUSERENR in user mode. + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t) (tv.tv_sec) * 1000000 + tv.tv_usec; +#endif +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma pop_macro("ALIGN_STRUCT") +#pragma pop_macro("FORCE_INLINE") +#endif + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC pop_options +#endif + +#endif diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 56633755..32dbc8db 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -5,7 +5,7 @@ plugins { android { namespace 'info.cemu.Cemu' compileSdk 34 - ndkVersion '25.2.9519653' + ndkVersion '26.1.10909125' defaultConfig { applicationId "info.cemu.Cemu" minSdk 30 @@ -47,6 +47,7 @@ android { '-DBUNDLE_SPEEX=ON', '-DENABLE_DISCORD_RPC=OFF', '-DENABLE_NSYSHID_LIBUSB=OFF', + '-DENABLE_HIDAPI=OFF', '-DENABLE_WAYLAND=OFF', ) // abiFilters("x86_64", "arm64-v8a") diff --git a/src/android/app/src/main/cpp/EmulationState.h b/src/android/app/src/main/cpp/EmulationState.h index 8640b900..59dbeb5b 100644 --- a/src/android/app/src/main/cpp/EmulationState.h +++ b/src/android/app/src/main/cpp/EmulationState.h @@ -118,9 +118,7 @@ class EmulationState int wpadCount = 0; for (int i = 0; i < InputManager::kMaxController; i++) { - auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController( - i) - .getEmulatedController(); + auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(i).getEmulatedController(); if (!emulatedController) continue; if (emulatedController->type() != EmulatedController::Type::VPAD) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 599db860..528e5b6b 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -74,11 +74,7 @@ if(WIN32) target_sources(CemuUtil PRIVATE MemMapper/MemMapperWin.cpp) target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoWin.cpp) elseif(UNIX) - if(ANDROID) - target_sources(CemuUtil PRIVATE Fiber/FiberBoost.cpp) - else() - target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp) - endif() + target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp) target_sources(CemuUtil PRIVATE MemMapper/MemMapperUnix.cpp) target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoUnix.cpp) if(NOT APPLE) @@ -95,7 +91,7 @@ set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$ +using _ucontext_t = libucontext_ucontext_t; +constexpr auto& swapcontext = libucontext_swapcontext; +constexpr auto& getcontext = libucontext_getcontext; +constexpr auto& makecontext = libucontext_makecontext; +#else #include +using _ucontext_t = ucontext_t; +#endif #include thread_local Fiber* sCurrentFiber{}; Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData) : m_privateData(privateData) { - ucontext_t* ctx = (ucontext_t*)malloc(sizeof(ucontext_t)); + _ucontext_t* ctx = (_ucontext_t*)malloc(sizeof(_ucontext_t)); const size_t stackSize = 2 * 1024 * 1024; m_stackPtr = malloc(stackSize); @@ -21,7 +30,7 @@ Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* pri Fiber::Fiber(void* privateData) : m_privateData(privateData) { - ucontext_t* ctx = (ucontext_t*)malloc(sizeof(ucontext_t)); + _ucontext_t* ctx = (_ucontext_t*)malloc(sizeof(_ucontext_t)); getcontext(ctx); this->m_implData = (void*)ctx; m_stackPtr = nullptr; @@ -46,7 +55,7 @@ void Fiber::Switch(Fiber& targetFiber) Fiber* leavingFiber = sCurrentFiber; sCurrentFiber = &targetFiber; std::atomic_thread_fence(std::memory_order_seq_cst); - swapcontext((ucontext_t*)(leavingFiber->m_implData), (ucontext_t*)(targetFiber.m_implData)); + swapcontext((_ucontext_t*)(leavingFiber->m_implData), (_ucontext_t*)(targetFiber.m_implData)); std::atomic_thread_fence(std::memory_order_seq_cst); } diff --git a/src/util/crypto/aes128.cpp b/src/util/crypto/aes128.cpp index e11b4b44..345a0dfb 100644 --- a/src/util/crypto/aes128.cpp +++ b/src/util/crypto/aes128.cpp @@ -600,7 +600,6 @@ void AES128_CBC_decrypt_updateIV(uint8* output, uint8* input, uint32 length, con memcpy(iv, newIv, KEYLEN); } -#if defined(ARCH_X86_64) ATTRIBUTE_AESNI inline __m128i AESNI128_ASSIST( __m128i temp1, __m128i temp2) @@ -792,7 +791,6 @@ ATTRIBUTE_AESNI void __aesni__AES128_ECB_encrypt(uint8* input, const uint8* key, feedback = _mm_aesenclast_si128(feedback, ((__m128i*)expandedKey)[10]); _mm_storeu_si128(&((__m128i*)output)[0], feedback); } -#endif void(*AES128_ECB_encrypt)(uint8* input, const uint8* key, uint8* output); void (*AES128_CBC_decrypt)(uint8* output, uint8* input, uint32 length, const uint8* key, const uint8* iv) = nullptr; @@ -837,7 +835,6 @@ void AES128_init() lookupTable_multiply[i] = (vE << 0) | (v9 << 8) | (vD << 16) | (vB << 24); } // check if AES-NI is available - #if defined(ARCH_X86_64) if (g_CPUFeatures.x86.aesni) { // AES-NI implementation @@ -850,8 +847,4 @@ void AES128_init() AES128_CBC_decrypt = __soft__AES128_CBC_decrypt; AES128_ECB_encrypt = __soft__AES128_ECB_encrypt; } - #else - AES128_CBC_decrypt = __soft__AES128_CBC_decrypt; - AES128_ECB_encrypt = __soft__AES128_ECB_encrypt; - #endif } diff --git a/vcpkg.json b/vcpkg.json index b2c601dc..dca39fb7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -41,7 +41,10 @@ }, "boost-random", "fmt", - "hidapi", + { + "name": "hidapi", + "platform": "!android" + }, "libpng", "glm", { From 223833cac4512c41a9d53ef630c1bd7b1bb1960c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 13 Jan 2024 20:37:10 +0100 Subject: [PATCH 088/101] Update libraries --- dependencies/vcpkg | 2 +- .../vcpkg_overlay_ports/sdl2/deps.patch | 13 -- .../vcpkg_overlay_ports/sdl2/portfile.cmake | 130 ------------------ dependencies/vcpkg_overlay_ports/sdl2/usage | 8 -- .../vcpkg_overlay_ports/sdl2/vcpkg.json | 58 -------- .../vcpkg_overlay_ports_linux/sdl2/deps.patch | 13 -- .../sdl2/portfile.cmake | 130 ------------------ .../vcpkg_overlay_ports_linux/sdl2/usage | 8 -- .../vcpkg_overlay_ports_linux/sdl2/vcpkg.json | 58 -------- src/Cafe/CMakeLists.txt | 6 +- vcpkg.json | 2 +- 11 files changed, 5 insertions(+), 423 deletions(-) delete mode 100644 dependencies/vcpkg_overlay_ports/sdl2/deps.patch delete mode 100644 dependencies/vcpkg_overlay_ports/sdl2/portfile.cmake delete mode 100644 dependencies/vcpkg_overlay_ports/sdl2/usage delete mode 100644 dependencies/vcpkg_overlay_ports/sdl2/vcpkg.json delete mode 100644 dependencies/vcpkg_overlay_ports_linux/sdl2/deps.patch delete mode 100644 dependencies/vcpkg_overlay_ports_linux/sdl2/portfile.cmake delete mode 100644 dependencies/vcpkg_overlay_ports_linux/sdl2/usage delete mode 100644 dependencies/vcpkg_overlay_ports_linux/sdl2/vcpkg.json diff --git a/dependencies/vcpkg b/dependencies/vcpkg index b81bc3a8..53bef899 160000 --- a/dependencies/vcpkg +++ b/dependencies/vcpkg @@ -1 +1 @@ -Subproject commit b81bc3a83fdbdffe80325eeabb2ec735a1f3c29d +Subproject commit 53bef8994c541b6561884a8395ea35715ece75db diff --git a/dependencies/vcpkg_overlay_ports/sdl2/deps.patch b/dependencies/vcpkg_overlay_ports/sdl2/deps.patch deleted file mode 100644 index a8637d8c..00000000 --- a/dependencies/vcpkg_overlay_ports/sdl2/deps.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake -index 65a98efbe..2f99f28f1 100644 ---- a/cmake/sdlchecks.cmake -+++ b/cmake/sdlchecks.cmake -@@ -352,7 +352,7 @@ endmacro() - # - HAVE_SDL_LOADSO opt - macro(CheckLibSampleRate) - if(SDL_LIBSAMPLERATE) -- find_package(SampleRate QUIET) -+ find_package(SampleRate CONFIG REQUIRED) - if(SampleRate_FOUND AND TARGET SampleRate::samplerate) - set(HAVE_LIBSAMPLERATE TRUE) - set(HAVE_LIBSAMPLERATE_H TRUE) diff --git a/dependencies/vcpkg_overlay_ports/sdl2/portfile.cmake b/dependencies/vcpkg_overlay_ports/sdl2/portfile.cmake deleted file mode 100644 index 39a724c5..00000000 --- a/dependencies/vcpkg_overlay_ports/sdl2/portfile.cmake +++ /dev/null @@ -1,130 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO libsdl-org/SDL - REF "release-${VERSION}" - SHA512 90858ae8c5fdddd5e13724e05ad0970e11bbab1df8a0201c3f4ce354dc6018e5d4ab7279402a263c716aacdaa52745f78531dc225d48d790ee9307e2f6198695 - HEAD_REF main - PATCHES - deps.patch -) - -string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC) -string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED) -string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT) - -vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS - FEATURES - vulkan SDL_VULKAN - x11 SDL_X11 - wayland SDL_WAYLAND - samplerate SDL_LIBSAMPLERATE - ibus SDL_IBUS -) - -if ("x11" IN_LIST FEATURES) - message(WARNING "You will need to install Xorg dependencies to use feature x11:\nsudo apt install libx11-dev libxft-dev libxext-dev\n") -endif() -if ("wayland" IN_LIST FEATURES) - message(WARNING "You will need to install Wayland dependencies to use feature wayland:\nsudo apt install libwayland-dev libxkbcommon-dev libegl1-mesa-dev\n") -endif() -if ("ibus" IN_LIST FEATURES) - message(WARNING "You will need to install ibus dependencies to use feature ibus:\nsudo apt install libibus-1.0-dev\n") -endif() - -if(VCPKG_TARGET_IS_UWP) - set(configure_opts WINDOWS_USE_MSBUILD) -endif() - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - ${configure_opts} - OPTIONS ${FEATURE_OPTIONS} - -DSDL_STATIC=${SDL_STATIC} - -DSDL_SHARED=${SDL_SHARED} - -DSDL_FORCE_STATIC_VCRT=${FORCE_STATIC_VCRT} - -DSDL_LIBC=ON - -DSDL_TEST=OFF - -DSDL_INSTALL_CMAKEDIR="cmake" - -DCMAKE_DISABLE_FIND_PACKAGE_Git=ON - -DSDL_LIBSAMPLERATE_SHARED=OFF - MAYBE_UNUSED_VARIABLES - SDL_FORCE_STATIC_VCRT -) - -vcpkg_cmake_install() -vcpkg_cmake_config_fixup(CONFIG_PATH cmake) - -file(REMOVE_RECURSE - "${CURRENT_PACKAGES_DIR}/debug/include" - "${CURRENT_PACKAGES_DIR}/debug/share" - "${CURRENT_PACKAGES_DIR}/bin/sdl2-config" - "${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config" - "${CURRENT_PACKAGES_DIR}/SDL2.framework" - "${CURRENT_PACKAGES_DIR}/debug/SDL2.framework" - "${CURRENT_PACKAGES_DIR}/share/licenses" - "${CURRENT_PACKAGES_DIR}/share/aclocal" -) - -file(GLOB BINS "${CURRENT_PACKAGES_DIR}/debug/bin/*" "${CURRENT_PACKAGES_DIR}/bin/*") -if(NOT BINS) - file(REMOVE_RECURSE - "${CURRENT_PACKAGES_DIR}/bin" - "${CURRENT_PACKAGES_DIR}/debug/bin" - ) -endif() - -if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_UWP AND NOT VCPKG_TARGET_IS_MINGW) - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/manual-link") - file(RENAME "${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib" "${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib") - endif() - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link") - file(RENAME "${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib" "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib") - endif() - - file(GLOB SHARE_FILES "${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake") - foreach(SHARE_FILE ${SHARE_FILES}) - vcpkg_replace_string("${SHARE_FILE}" "lib/SDL2main" "lib/manual-link/SDL2main") - endforeach() -endif() - -vcpkg_copy_pdbs() - -set(DYLIB_COMPATIBILITY_VERSION_REGEX "set\\(DYLIB_COMPATIBILITY_VERSION (.+)\\)") -set(DYLIB_CURRENT_VERSION_REGEX "set\\(DYLIB_CURRENT_VERSION (.+)\\)") -file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_COMPATIBILITY_VERSION REGEX ${DYLIB_COMPATIBILITY_VERSION_REGEX}) -file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_CURRENT_VERSION REGEX ${DYLIB_CURRENT_VERSION_REGEX}) -string(REGEX REPLACE ${DYLIB_COMPATIBILITY_VERSION_REGEX} "\\1" DYLIB_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}") -string(REGEX REPLACE ${DYLIB_CURRENT_VERSION_REGEX} "\\1" DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}") - -if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2main" "-lSDL2maind") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2 " "-lSDL2d ") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-static " "-lSDL2-staticd ") -endif() - -if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic" AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-lSDL2-static " " ") - endif() - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-staticd " " ") - endif() -endif() - -if(VCPKG_TARGET_IS_UWP) - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "$<$:d>.lib" "") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:") - endif() - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "$<$:d>.lib" "d") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:") - endif() -endif() - -vcpkg_fixup_pkgconfig() - -file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") -vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt") diff --git a/dependencies/vcpkg_overlay_ports/sdl2/usage b/dependencies/vcpkg_overlay_ports/sdl2/usage deleted file mode 100644 index 1cddcd46..00000000 --- a/dependencies/vcpkg_overlay_ports/sdl2/usage +++ /dev/null @@ -1,8 +0,0 @@ -sdl2 provides CMake targets: - - find_package(SDL2 CONFIG REQUIRED) - target_link_libraries(main - PRIVATE - $ - $,SDL2::SDL2,SDL2::SDL2-static> - ) diff --git a/dependencies/vcpkg_overlay_ports/sdl2/vcpkg.json b/dependencies/vcpkg_overlay_ports/sdl2/vcpkg.json deleted file mode 100644 index de2eb9b9..00000000 --- a/dependencies/vcpkg_overlay_ports/sdl2/vcpkg.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "sdl2", - "version": "2.26.5", - "description": "Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.", - "homepage": "https://www.libsdl.org/download-2.0.php", - "license": "Zlib", - "dependencies": [ - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ], - "default-features": [ - "base" - ], - "features": { - "base": { - "description": "Base functionality for SDL", - "dependencies": [ - { - "name": "sdl2", - "default-features": false, - "features": [ - "ibus", - "wayland", - "x11" - ], - "platform": "linux" - } - ] - }, - "ibus": { - "description": "Build with ibus IME support", - "supports": "linux" - }, - "samplerate": { - "description": "Use libsamplerate for audio rate conversion", - "dependencies": [ - "libsamplerate" - ] - }, - "vulkan": { - "description": "Vulkan functionality for SDL" - }, - "wayland": { - "description": "Build with Wayland support", - "supports": "linux" - }, - "x11": { - "description": "Build with X11 support", - "supports": "!windows" - } - } -} diff --git a/dependencies/vcpkg_overlay_ports_linux/sdl2/deps.patch b/dependencies/vcpkg_overlay_ports_linux/sdl2/deps.patch deleted file mode 100644 index a8637d8c..00000000 --- a/dependencies/vcpkg_overlay_ports_linux/sdl2/deps.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake -index 65a98efbe..2f99f28f1 100644 ---- a/cmake/sdlchecks.cmake -+++ b/cmake/sdlchecks.cmake -@@ -352,7 +352,7 @@ endmacro() - # - HAVE_SDL_LOADSO opt - macro(CheckLibSampleRate) - if(SDL_LIBSAMPLERATE) -- find_package(SampleRate QUIET) -+ find_package(SampleRate CONFIG REQUIRED) - if(SampleRate_FOUND AND TARGET SampleRate::samplerate) - set(HAVE_LIBSAMPLERATE TRUE) - set(HAVE_LIBSAMPLERATE_H TRUE) diff --git a/dependencies/vcpkg_overlay_ports_linux/sdl2/portfile.cmake b/dependencies/vcpkg_overlay_ports_linux/sdl2/portfile.cmake deleted file mode 100644 index 39a724c5..00000000 --- a/dependencies/vcpkg_overlay_ports_linux/sdl2/portfile.cmake +++ /dev/null @@ -1,130 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO libsdl-org/SDL - REF "release-${VERSION}" - SHA512 90858ae8c5fdddd5e13724e05ad0970e11bbab1df8a0201c3f4ce354dc6018e5d4ab7279402a263c716aacdaa52745f78531dc225d48d790ee9307e2f6198695 - HEAD_REF main - PATCHES - deps.patch -) - -string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC) -string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED) -string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT) - -vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS - FEATURES - vulkan SDL_VULKAN - x11 SDL_X11 - wayland SDL_WAYLAND - samplerate SDL_LIBSAMPLERATE - ibus SDL_IBUS -) - -if ("x11" IN_LIST FEATURES) - message(WARNING "You will need to install Xorg dependencies to use feature x11:\nsudo apt install libx11-dev libxft-dev libxext-dev\n") -endif() -if ("wayland" IN_LIST FEATURES) - message(WARNING "You will need to install Wayland dependencies to use feature wayland:\nsudo apt install libwayland-dev libxkbcommon-dev libegl1-mesa-dev\n") -endif() -if ("ibus" IN_LIST FEATURES) - message(WARNING "You will need to install ibus dependencies to use feature ibus:\nsudo apt install libibus-1.0-dev\n") -endif() - -if(VCPKG_TARGET_IS_UWP) - set(configure_opts WINDOWS_USE_MSBUILD) -endif() - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - ${configure_opts} - OPTIONS ${FEATURE_OPTIONS} - -DSDL_STATIC=${SDL_STATIC} - -DSDL_SHARED=${SDL_SHARED} - -DSDL_FORCE_STATIC_VCRT=${FORCE_STATIC_VCRT} - -DSDL_LIBC=ON - -DSDL_TEST=OFF - -DSDL_INSTALL_CMAKEDIR="cmake" - -DCMAKE_DISABLE_FIND_PACKAGE_Git=ON - -DSDL_LIBSAMPLERATE_SHARED=OFF - MAYBE_UNUSED_VARIABLES - SDL_FORCE_STATIC_VCRT -) - -vcpkg_cmake_install() -vcpkg_cmake_config_fixup(CONFIG_PATH cmake) - -file(REMOVE_RECURSE - "${CURRENT_PACKAGES_DIR}/debug/include" - "${CURRENT_PACKAGES_DIR}/debug/share" - "${CURRENT_PACKAGES_DIR}/bin/sdl2-config" - "${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config" - "${CURRENT_PACKAGES_DIR}/SDL2.framework" - "${CURRENT_PACKAGES_DIR}/debug/SDL2.framework" - "${CURRENT_PACKAGES_DIR}/share/licenses" - "${CURRENT_PACKAGES_DIR}/share/aclocal" -) - -file(GLOB BINS "${CURRENT_PACKAGES_DIR}/debug/bin/*" "${CURRENT_PACKAGES_DIR}/bin/*") -if(NOT BINS) - file(REMOVE_RECURSE - "${CURRENT_PACKAGES_DIR}/bin" - "${CURRENT_PACKAGES_DIR}/debug/bin" - ) -endif() - -if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_UWP AND NOT VCPKG_TARGET_IS_MINGW) - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/manual-link") - file(RENAME "${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib" "${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib") - endif() - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link") - file(RENAME "${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib" "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib") - endif() - - file(GLOB SHARE_FILES "${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake") - foreach(SHARE_FILE ${SHARE_FILES}) - vcpkg_replace_string("${SHARE_FILE}" "lib/SDL2main" "lib/manual-link/SDL2main") - endforeach() -endif() - -vcpkg_copy_pdbs() - -set(DYLIB_COMPATIBILITY_VERSION_REGEX "set\\(DYLIB_COMPATIBILITY_VERSION (.+)\\)") -set(DYLIB_CURRENT_VERSION_REGEX "set\\(DYLIB_CURRENT_VERSION (.+)\\)") -file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_COMPATIBILITY_VERSION REGEX ${DYLIB_COMPATIBILITY_VERSION_REGEX}) -file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_CURRENT_VERSION REGEX ${DYLIB_CURRENT_VERSION_REGEX}) -string(REGEX REPLACE ${DYLIB_COMPATIBILITY_VERSION_REGEX} "\\1" DYLIB_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}") -string(REGEX REPLACE ${DYLIB_CURRENT_VERSION_REGEX} "\\1" DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}") - -if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2main" "-lSDL2maind") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2 " "-lSDL2d ") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-static " "-lSDL2-staticd ") -endif() - -if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic" AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-lSDL2-static " " ") - endif() - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-staticd " " ") - endif() -endif() - -if(VCPKG_TARGET_IS_UWP) - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "$<$:d>.lib" "") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:") - endif() - if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "$<$:d>.lib" "d") - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:") - endif() -endif() - -vcpkg_fixup_pkgconfig() - -file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") -vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt") diff --git a/dependencies/vcpkg_overlay_ports_linux/sdl2/usage b/dependencies/vcpkg_overlay_ports_linux/sdl2/usage deleted file mode 100644 index 1cddcd46..00000000 --- a/dependencies/vcpkg_overlay_ports_linux/sdl2/usage +++ /dev/null @@ -1,8 +0,0 @@ -sdl2 provides CMake targets: - - find_package(SDL2 CONFIG REQUIRED) - target_link_libraries(main - PRIVATE - $ - $,SDL2::SDL2,SDL2::SDL2-static> - ) diff --git a/dependencies/vcpkg_overlay_ports_linux/sdl2/vcpkg.json b/dependencies/vcpkg_overlay_ports_linux/sdl2/vcpkg.json deleted file mode 100644 index de2eb9b9..00000000 --- a/dependencies/vcpkg_overlay_ports_linux/sdl2/vcpkg.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "sdl2", - "version": "2.26.5", - "description": "Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.", - "homepage": "https://www.libsdl.org/download-2.0.php", - "license": "Zlib", - "dependencies": [ - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ], - "default-features": [ - "base" - ], - "features": { - "base": { - "description": "Base functionality for SDL", - "dependencies": [ - { - "name": "sdl2", - "default-features": false, - "features": [ - "ibus", - "wayland", - "x11" - ], - "platform": "linux" - } - ] - }, - "ibus": { - "description": "Build with ibus IME support", - "supports": "linux" - }, - "samplerate": { - "description": "Use libsamplerate for audio rate conversion", - "dependencies": [ - "libsamplerate" - ] - }, - "vulkan": { - "description": "Vulkan functionality for SDL" - }, - "wayland": { - "description": "Build with Wayland support", - "supports": "linux" - }, - "x11": { - "description": "Build with X11 support", - "supports": "!windows" - } - } -} diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 9e20bb33..20853789 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -535,9 +535,9 @@ endif() if (ENABLE_NSYSHID_LIBUSB) if (ENABLE_VCPKG) - find_package(libusb CONFIG REQUIRED) - target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS}) - target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES}) + find_package(PkgConfig REQUIRED) + pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0) + target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb) else () find_package(libusb MODULE REQUIRED) target_link_libraries(CemuCafe PRIVATE libusb::libusb) diff --git a/vcpkg.json b/vcpkg.json index d14a1a8a..48742b4a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,7 +1,7 @@ { "name": "cemu", "version-string": "1.0", - "builtin-baseline": "b81bc3a83fdbdffe80325eeabb2ec735a1f3c29d", + "builtin-baseline": "53bef8994c541b6561884a8395ea35715ece75db", "dependencies": [ "pugixml", "zlib", From 9b0a1d53dc449fedebd5eb6255a312aa334ffad9 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 14 Jan 2024 23:40:29 +0100 Subject: [PATCH 089/101] Latte: Fix syntax error in generated GLSL --- .../LatteDecompilerEmitGLSL.cpp | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index aa7b7162..f3d2c7a8 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -246,6 +246,22 @@ static void _appendPVPS(LatteDecompilerShaderContext* shaderContext, StringBuf* _appendChannel(src, aluUnit); } +std::string _FormatFloatAsGLSLConstant(float f) +{ + char floatAsStr[64]; + size_t floatAsStrLen = fmt::format_to_n(floatAsStr, 64, "{:#}", f).size; + size_t floatAsStrLenOrg = floatAsStrLen; + if(floatAsStrLen > 0 && floatAsStr[floatAsStrLen-1] == '.') + { + floatAsStr[floatAsStrLen] = '0'; + floatAsStrLen++; + } + cemu_assert(floatAsStrLen < 50); // constant suspiciously long? + floatAsStr[floatAsStrLen] = '\0'; + cemu_assert_debug(floatAsStrLen >= 3); // shortest possible form is "0.0" + return floatAsStr; +} + // tracks PV/PS and register backups struct ALUClauseTemporariesState { @@ -926,15 +942,7 @@ void _emitOperandInputCode(LatteDecompilerShaderContext* shaderContext, LatteDec exponent -= 127; if ((constVal & 0xFF) == 0 && exponent >= -10 && exponent <= 10) { - char floatAsStr[32]; - size_t floatAsStrLen = fmt::format_to_n(floatAsStr, 32, "{:#}", *(float*)&constVal).size; - if(floatAsStrLen > 0 && floatAsStr[floatAsStrLen-1] == '.') - { - floatAsStr[floatAsStrLen] = '0'; - floatAsStrLen++; - } - cemu_assert_debug(floatAsStrLen >= 3); // shortest possible form is "0.0" - src->add(std::string_view(floatAsStr, floatAsStrLen)); + src->add(_FormatFloatAsGLSLConstant(*(float*)&constVal)); } else src->addFmt("intBitsToFloat(0x{:08x})", constVal); @@ -2561,13 +2569,11 @@ void _emitTEXSampleTextureCode(LatteDecompilerShaderContext* shaderContext, Latt // lod or lod bias parameter if( texOpcode == GPU7_TEX_INST_SAMPLE_L || texOpcode == GPU7_TEX_INST_SAMPLE_LB || texOpcode == GPU7_TEX_INST_SAMPLE_C_L) { + src->add(","); if(texOpcode == GPU7_TEX_INST_SAMPLE_LB) - src->addFmt("{}", (float)texInstruction->textureFetch.lodBias / 16.0f); + src->add(_FormatFloatAsGLSLConstant((float)texInstruction->textureFetch.lodBias / 16.0f)); else - { - src->add(","); _emitTEXSampleCoordInputComponent(shaderContext, texInstruction, 3, LATTE_DECOMPILER_DTYPE_FLOAT); - } } else if( texOpcode == GPU7_TEX_INST_SAMPLE_LZ || texOpcode == GPU7_TEX_INST_SAMPLE_C_LZ ) { From f39a5e757b1d82c509c12ba67e88916cf7341573 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:14:42 +0100 Subject: [PATCH 090/101] Add "Open MLC folder" option Also updated Patron supporter list --- src/gui/MainWindow.cpp | 14 ++++++++++---- src/gui/MainWindow.h | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 92594d00..dc9ff0a8 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -75,6 +75,7 @@ enum MAINFRAME_MENU_ID_FILE_LOAD = 20100, MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, + MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, MAINFRAME_MENU_ID_FILE_EXIT, MAINFRAME_MENU_ID_FILE_END_EMULATION, MAINFRAME_MENU_ID_FILE_RECENT_0, @@ -166,7 +167,8 @@ EVT_MOVE(MainWindow::OnMove) // file menu EVT_MENU(MAINFRAME_MENU_ID_FILE_LOAD, MainWindow::OnFileMenu) EVT_MENU(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, MainWindow::OnInstallUpdate) -EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, MainWindow::OnOpenCemuFolder) +EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, MainWindow::OnOpenFolder) +EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, MainWindow::OnOpenFolder) EVT_MENU(MAINFRAME_MENU_ID_FILE_EXIT, MainWindow::OnFileExit) EVT_MENU(MAINFRAME_MENU_ID_FILE_END_EMULATION, MainWindow::OnFileMenu) EVT_MENU_RANGE(MAINFRAME_MENU_ID_FILE_RECENT_0 + 0, MAINFRAME_MENU_ID_FILE_RECENT_LAST, MainWindow::OnFileMenu) @@ -684,9 +686,12 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) } } -void MainWindow::OnOpenCemuFolder(wxCommandEvent& event) +void MainWindow::OnOpenFolder(wxCommandEvent& event) { - wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetUserDataPath())); + if(event.GetId() == MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER) + wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetUserDataPath())); + else if(event.GetId() == MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER) + wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetMlcPath())); } void MainWindow::OnInstallUpdate(wxCommandEvent& event) @@ -2015,7 +2020,7 @@ public: , "Faris Leonhart", "MahvZero", "PlaguedGuardian", "Stuffie", "CaptainLester", "Qtech", "Zaurexus", "Leonidas", "Artifesto" , "Alca259", "SirWestofAsh", "Loli Co.", "The Technical Revolutionary", "MegaYama", "mitori", "Seymordius", "Adrian Josh Cruz", "Manuel Hoenings", "Just A Jabb" , "pgantonio", "CannonXIII", "Lonewolf00708", "AlexsDesign.com", "NoskLo", "MrSirHaku", "xElite_V AKA William H. Johnson", "Zalnor", "Pig", "James \"SE4LS\"", "DairyOrange", "Horoko Lawrence", "bloodmc", "Officer Jenny", "Quasar", "Postposterous", "Jake Jackson", "Kaydax", "CthePredatorG" - , "Hengi", "Pyrochaser"}; + , "Hengi", "Pyrochaser", "luma.x3"}; wxString nameListLeft, nameListRight; for (size_t i = 0; i < patreonSupporterNames.size(); i++) @@ -2107,6 +2112,7 @@ void MainWindow::RecreateMenu() } m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, _("&Open Cemu folder")); + m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, _("&Open MLC folder")); m_fileMenu->AppendSeparator(); m_exitMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_EXIT, _("&Exit")); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 07189b52..25100b72 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -92,7 +92,7 @@ public: void OnMouseWheel(wxMouseEvent& event); void OnClose(wxCloseEvent& event); void OnFileMenu(wxCommandEvent& event); - void OnOpenCemuFolder(wxCommandEvent& event); + void OnOpenFolder(wxCommandEvent& event); void OnLaunchFromFile(wxLaunchGameEvent& event); void OnInstallUpdate(wxCommandEvent& event); void OnFileExit(wxCommandEvent& event); From f58b260cbd566f1f76506246b3b9cd247b1ca511 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:31:59 +0100 Subject: [PATCH 091/101] Fix macos missing dylib file --- src/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de9a6600..b7711018 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,7 +78,7 @@ if (MACOS_BUNDLE) set(MACOSX_BUNDLE_BUNDLE_NAME "Cemu") set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CMAKE_PROJECT_VERSION}) set(MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION}) - set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2023 Cemu Project") + set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2024 Cemu Project") set(MACOSX_BUNDLE_CATEGORY "public.app-category.games") set(MACOSX_MINIMUM_SYSTEM_VERSION "12.0") @@ -100,6 +100,9 @@ if (MACOS_BUNDLE) add_custom_command (TARGET CemuBin POST_BUILD COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}") + + add_custom_command (TARGET CemuBin POST_BUILD + COMMAND bash -c "install_name_tool -change /usr/local/opt/libusb/lib/libusb-1.0.0.dylib @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}") endif() set_target_properties(CemuBin PROPERTIES From 7e778042ee0aa053f6daa6a0c23f7ff0c74b5f6e Mon Sep 17 00:00:00 2001 From: Live session user Date: Mon, 15 Jan 2024 17:46:56 -0800 Subject: [PATCH 092/101] Fix macos missing dylib file --- CMakeLists.txt | 2 + dependencies/vcpkg_overlay_ports_mac/.gitkeep | 0 .../libusb/portfile.cmake | 71 +++++++++++++++++++ .../vcpkg_overlay_ports_mac/libusb/usage | 5 ++ .../vcpkg_overlay_ports_mac/libusb/vcpkg.json | 8 +++ src/CMakeLists.txt | 11 ++- 6 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 dependencies/vcpkg_overlay_ports_mac/.gitkeep create mode 100644 dependencies/vcpkg_overlay_ports_mac/libusb/portfile.cmake create mode 100644 dependencies/vcpkg_overlay_ports_mac/libusb/usage create mode 100644 dependencies/vcpkg_overlay_ports_mac/libusb/vcpkg.json diff --git a/CMakeLists.txt b/CMakeLists.txt index c988508c..ec6abedc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ endif() if (ENABLE_VCPKG) if(UNIX AND NOT APPLE) set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux") + elseif(APPLE) + set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac") else() set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports") endif() diff --git a/dependencies/vcpkg_overlay_ports_mac/.gitkeep b/dependencies/vcpkg_overlay_ports_mac/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/dependencies/vcpkg_overlay_ports_mac/libusb/portfile.cmake b/dependencies/vcpkg_overlay_ports_mac/libusb/portfile.cmake new file mode 100644 index 00000000..7b76bba0 --- /dev/null +++ b/dependencies/vcpkg_overlay_ports_mac/libusb/portfile.cmake @@ -0,0 +1,71 @@ +set(VCPKG_LIBRARY_LINKAGE dynamic) + +if(VCPKG_TARGET_IS_LINUX) + message("${PORT} currently requires the following tools and libraries from the system package manager:\n autoreconf\n libudev\n\nThese can be installed on Ubuntu systems via apt-get install autoconf libudev-dev") +endif() + +set(VERSION 1.0.26) +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO libusb/libusb + REF fcf0c710ef5911ae37fbbf1b39d48a89f6f14e8a # v1.0.26.11791 2023-03-12 + SHA512 0aa6439f7988487adf2a3bff473fec80b5c722a47f117a60696d2aa25c87cc3f20fb6aaca7c66e49be25db6a35eb0bb5f71ed7b211d1b8ee064c5d7f1b985c73 + HEAD_REF master +) + +if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) + + if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic") + set(LIBUSB_PROJECT_TYPE dll) + else() + set(LIBUSB_PROJECT_TYPE static) + endif() + + # The README.md file in the archive is a symlink to README + # which causes issues with the windows MSBUILD process + file(REMOVE "${SOURCE_PATH}/README.md") + + vcpkg_msbuild_install( + SOURCE_PATH "${SOURCE_PATH}" + PROJECT_SUBPATH msvc/libusb_${LIBUSB_PROJECT_TYPE}.vcxproj + ) + + file(INSTALL "${SOURCE_PATH}/libusb/libusb.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include/libusb-1.0") + set(prefix "") + set(exec_prefix [[${prefix}]]) + set(libdir [[${prefix}/lib]]) + set(includedir [[${prefix}/include]]) + configure_file("${SOURCE_PATH}/libusb-1.0.pc.in" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/libusb-1.0.pc" @ONLY) + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/libusb-1.0.pc" " -lusb-1.0" " -llibusb-1.0") + if(NOT VCPKG_BUILD_TYPE) + set(includedir [[${prefix}/../include]]) + configure_file("${SOURCE_PATH}/libusb-1.0.pc.in" "${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/libusb-1.0.pc" @ONLY) + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/libusb-1.0.pc" " -lusb-1.0" " -llibusb-1.0") + endif() +else() + vcpkg_list(SET MAKE_OPTIONS) + vcpkg_list(SET LIBUSB_LINK_LIBRARIES) + if(VCPKG_TARGET_IS_EMSCRIPTEN) + vcpkg_list(APPEND MAKE_OPTIONS BUILD_TRIPLET --host=wasm32) + endif() + if("udev" IN_LIST FEATURES) + vcpkg_list(APPEND MAKE_OPTIONS "--enable-udev") + vcpkg_list(APPEND LIBUSB_LINK_LIBRARIES udev) + else() + vcpkg_list(APPEND MAKE_OPTIONS "--disable-udev") + endif() + vcpkg_configure_make( + SOURCE_PATH "${SOURCE_PATH}" + AUTOCONFIG + OPTIONS + ${MAKE_OPTIONS} + "--enable-examples-build=no" + "--enable-tests-build=no" + ) + vcpkg_install_make() +endif() + +vcpkg_fixup_pkgconfig() + +file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING") diff --git a/dependencies/vcpkg_overlay_ports_mac/libusb/usage b/dependencies/vcpkg_overlay_ports_mac/libusb/usage new file mode 100644 index 00000000..87e6e860 --- /dev/null +++ b/dependencies/vcpkg_overlay_ports_mac/libusb/usage @@ -0,0 +1,5 @@ +libusb can be imported via CMake FindPkgConfig module: + find_package(PkgConfig REQUIRED) + pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0) + + target_link_libraries(main PRIVATE PkgConfig::libusb) diff --git a/dependencies/vcpkg_overlay_ports_mac/libusb/vcpkg.json b/dependencies/vcpkg_overlay_ports_mac/libusb/vcpkg.json new file mode 100644 index 00000000..efc70f3d --- /dev/null +++ b/dependencies/vcpkg_overlay_ports_mac/libusb/vcpkg.json @@ -0,0 +1,8 @@ +{ + "name": "libusb", + "version": "1.0.26.11791", + "port-version": 7, + "description": "a cross-platform library to access USB devices", + "homepage": "https://github.com/libusb/libusb", + "license": "LGPL-2.1-or-later" +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b7711018..7442e37c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,13 +96,10 @@ if (MACOS_BUNDLE) endforeach(folder) add_custom_command (TARGET CemuBin POST_BUILD - COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib") - - add_custom_command (TARGET CemuBin POST_BUILD - COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}") - - add_custom_command (TARGET CemuBin POST_BUILD - COMMAND bash -c "install_name_tool -change /usr/local/opt/libusb/lib/libusb-1.0.0.dylib @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}") + COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib" + COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib" + COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}" + COMMAND bash -c "install_name_tool -change /usr/local/opt/libusb/lib/libusb-1.0.0.dylib @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}") endif() set_target_properties(CemuBin PROPERTIES From f899ab7c34035bc3746112b6459bd15a4a181dd4 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Wed, 17 Jan 2024 01:09:56 +0000 Subject: [PATCH 093/101] Vulkan: Check for 0 size before wayland resize Fixes "Launching games directly with the --title-id argument doesn't work in Wayland" (#999) --- src/gui/canvas/VulkanCanvas.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/canvas/VulkanCanvas.cpp b/src/gui/canvas/VulkanCanvas.cpp index eb56b3c4..8b0c8506 100644 --- a/src/gui/canvas/VulkanCanvas.cpp +++ b/src/gui/canvas/VulkanCanvas.cpp @@ -66,6 +66,10 @@ void VulkanCanvas::OnPaint(wxPaintEvent& event) void VulkanCanvas::OnResize(wxSizeEvent& event) { + const wxSize size = GetSize(); + if (size.GetWidth() == 0 || size.GetHeight() == 0) + return; + #if BOOST_OS_LINUX && HAS_WAYLAND if(m_subsurface) { @@ -73,9 +77,6 @@ void VulkanCanvas::OnResize(wxSizeEvent& event) m_subsurface->setSize(sRect.GetX(), sRect.GetY(), sRect.GetWidth(), sRect.GetHeight()); } #endif - const wxSize size = GetSize(); - if (size.GetWidth() == 0 || size.GetHeight() == 0) - return; const wxRect refreshRect(size); RefreshRect(refreshRect, false); From e53c63b828e856cb4bf11729cdf90fe12544ac9f Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Wed, 17 Jan 2024 01:18:07 +0000 Subject: [PATCH 094/101] Flatpak: Create shortcuts that launch flatpak --- src/gui/components/wxGameList.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 2c78ea3c..5ceaf71f 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1235,6 +1235,7 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { const auto title_id = gameInfo.GetBaseTitleId(); const auto title_name = gameInfo.GetTitleName(); auto exe_path = ActiveSettings::GetExecutablePath(); + const char *flatpak_id = getenv("FLATPAK_ID"); // GetExecutablePath returns the AppImage's temporary mount location, instead of its actual path wxString appimage_path; @@ -1292,22 +1293,31 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) { } } } + + std::string desktop_exec_entry; + if (flatpak_id) + desktop_exec_entry = fmt::format("/usr/bin/flatpak run {0} --title-id {1:016x}", flatpak_id, title_id); + else + desktop_exec_entry = fmt::format("{0:?} --title-id {1:016x}", _pathToUtf8(exe_path), title_id); + // 'Icon' accepts spaces in file name, does not accept quoted file paths // 'Exec' does not accept non-escaped spaces, and can accept quoted file paths - const auto desktop_entry_string = + auto desktop_entry_string = fmt::format("[Desktop Entry]\n" "Name={0}\n" "Comment=Play {0} on Cemu\n" - "Exec={1:?} --title-id {2:016x}\n" - "Icon={3}\n" + "Exec={1}\n" + "Icon={2}\n" "Terminal=false\n" "Type=Application\n" - "Categories=Game;", + "Categories=Game;\n", title_name, - _pathToUtf8(exe_path), - title_id, + desktop_exec_entry, _pathToUtf8(icon_path.value_or(""))); + if (flatpak_id) + desktop_entry_string += fmt::format("X-Flatpak={}\n", flatpak_id); + std::ofstream output_stream(output_path); if (!output_stream.good()) { From 72aacbdcecc064ea7c3b158c433e4803496ac296 Mon Sep 17 00:00:00 2001 From: Mike Lothian Date: Fri, 19 Jan 2024 01:03:57 +0000 Subject: [PATCH 095/101] Vulkan: Don't use glslang internal headers Signed-off-by: Mike Lothian --- src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp | 3 +-- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp index e9936c43..72a1be4c 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp @@ -3,7 +3,6 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" -#include #include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" @@ -91,4 +90,4 @@ PipelineInfo::~PipelineInfo() // remove from cache VulkanRenderer::GetInstance()->unregisterGraphicsPipeline(this); -} \ No newline at end of file +} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 44214606..616f57e2 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -26,7 +26,7 @@ #include "Cafe/HW/Latte/Core/LatteTiming.h" // vsync control -#include +#include #include From 18679af4ec641a4c59753d54751dcab257777eef Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Fri, 19 Jan 2024 14:07:17 +0000 Subject: [PATCH 096/101] Ignore Wii U pro controller --- src/input/api/Wiimote/hidapi/HidapiWiimote.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp index a5701f56..db185675 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -1,9 +1,11 @@ #include "HidapiWiimote.h" +#include static constexpr uint16 WIIMOTE_VENDOR_ID = 0x057e; static constexpr uint16 WIIMOTE_PRODUCT_ID = 0x0306; static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330; static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22; +static constexpr auto PRO_CONTROLLER_NAME = L"Nintendo RVL-CNT-01-UC"; HidapiWiimote::HidapiWiimote(hid_device* dev, std::string_view path) : m_handle(dev), m_path(path) { @@ -30,6 +32,8 @@ std::vector HidapiWiimote::get_devices() { for (auto it = device_enumeration; it != nullptr; it = it->next){ if (it->product_id != WIIMOTE_PRODUCT_ID && it->product_id != WIIMOTE_MP_PRODUCT_ID) continue; + if (std::wcscmp(it->product_string, PRO_CONTROLLER_NAME) == 0) + continue; auto dev = hid_open_path(it->path); if (!dev){ cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, boost::nowide::narrow(hid_error(nullptr))); From 4e4ac0de51b82c455aec71060c109f9e5a1888d9 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:32:24 +0100 Subject: [PATCH 097/101] CI: For the Windows build use as many cores as available --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d23faa31..3c01ba7a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -183,7 +183,7 @@ jobs: - name: "Build Cemu" run: | cd build - cmake --build . --config ${{ env.BUILD_MODE }} -j 2 + cmake --build . --config ${{ env.BUILD_MODE }} - name: Prepare artifact if: ${{ inputs.deploymode == 'release' }} From ca01e923bf03573d5023feb0f4464ce015910ea6 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 20 Jan 2024 00:33:36 +0100 Subject: [PATCH 098/101] Update issue templates --- .../bug-report-feature-request.md | 34 --------- .github/ISSUE_TEMPLATE/config.yml | 2 +- .../ISSUE_TEMPLATE/emulation_bug_report.yaml | 69 +++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_report.yaml | 28 ++++++++ 4 files changed, 98 insertions(+), 35 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-report-feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/emulation_bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature_report.yaml diff --git a/.github/ISSUE_TEMPLATE/bug-report-feature-request.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md deleted file mode 100644 index 61bd5d06..00000000 --- a/.github/ISSUE_TEMPLATE/bug-report-feature-request.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug Report / Feature Request -about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Cemu or you are requesting a feature you believe would make Cemu better. -title: '' -labels: '' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d33d87ed..d71e22d0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,4 +2,4 @@ blank_issues_enabled: false contact_links: - name: Cemu Discord url: https://discord.com/invite/5psYsup - about: If you are experiencing an issue with Cemu, and you need tech support, or if you have a general question, try asking in the official Cemu Discord linked here. Piracy is not allowed. + about: If you need technical support with Cemu or have other questions the best place to ask is on the official Cemu Discord linked here diff --git a/.github/ISSUE_TEMPLATE/emulation_bug_report.yaml b/.github/ISSUE_TEMPLATE/emulation_bug_report.yaml new file mode 100644 index 00000000..75928607 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/emulation_bug_report.yaml @@ -0,0 +1,69 @@ +# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +name: Bug Report +description: Report an issue with Cemu emulator +title: "Enter a title for the bug report here" +labels: bug +body: + - type: markdown + id: md_readme + attributes: + value: | + ## Important: Read First + + If you discovered a bug you can report it here. Please make sure of the following first: + - That you are using the latest version of Cemu + - Only report something if you are sure it's a bug and not any technical issue on your end. For troubleshooting help see the [links page](https://github.com/cemu-project/Cemu#links) + - Problems specific to a single game should be reported on the [compatibility wiki](https://wiki.cemu.info/wiki/Main_Page) instead + - Verify that your problem isn't already mentioned on the [issue tracker](https://github.com/cemu-project/Cemu/issues) + + Additionally, be aware that graphic packs can also causes issues. There is a separate issue tracker for graphic pack bugs over at the [graphic pack repository](https://github.com/cemu-project/cemu_graphic_packs) + - type: textarea + id: current_behavior + attributes: + label: Current Behavior + description: "What the bug is, in a brief description" + validations: + required: true + - type: textarea + id: expected_behavior + attributes: + label: Expected Behavior + description: "What did you expect to happen?" + validations: + required: true + + - type: textarea + id: steps_to_reproduce + attributes: + label: Steps to Reproduce + description: "How to reproduce the issue" + validations: + required: true + - type: textarea + id: sys_info + attributes: + label: System Info (Optional) + description: "Your PC specifications. Usually only the operating system and graphics card is important. But feel free to add more info." + placeholder: | + Info + OS: Windows 10 + GPU: NVIDIA GeForce RTX 4090 + value: | + OS: + GPU: + - type: textarea + id: emulation_settings + attributes: + label: Emulation Settings (Optional) + description: | + Any non-default settings. You can leave this empty if you didn't change anything other than input settings. + validations: + required: false + - type: textarea + id: logs_files + attributes: + label: "Logs (Optional)" + description: | + "Attach `log.txt` from your Cemu folder (*File > Open Cemu folder*)". + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_report.yaml b/.github/ISSUE_TEMPLATE/feature_report.yaml new file mode 100644 index 00000000..a5d8705c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_report.yaml @@ -0,0 +1,28 @@ +# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +name: Feature suggestion +description: Suggest a new feature +title: "Enter a title for the suggestion here" +labels: feature request +body: + - type: markdown + id: md_readme + attributes: + value: | + ## Important: Read First + + While we appreciate suggestions, it is important to note that we are a very small team and there are already many more ideas than we could ever implement in the near future. Therefore, please only suggest something if you believe it is a great addition and the idea is reasonably unique. + + *Avoid* to create suggestions for: + - Overly obvious features ("Game xyz does not work and should be fixed", "Wiimote support should be improved", "You should add an Android port", "Copy feature xyz from another emulator", "A button to pause/stop emulation") + - Niche features which are only interesting to a tiny percentage of users + - Large scale features ("Add a Metal backend for MacOS", "Add ARM support", "Add savestates") + + Note that this doesn't mean we aren't interested in these ideas, but rather we likely have them planned anyway and it's mostly up to finding the time to implement them. + If you believe your idea is worthwhile even if it doesn't meet all the criteria above, you can still try suggesting it but we might close it. + - type: textarea + id: idea_suggestion + attributes: + label: Your suggestion + description: "Describe what your suggestion is in as much detail as possible" + validations: + required: true From 748070bc12e088fd94edb67e3e9d05b57eaf147e Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 20 Jan 2024 21:44:37 +0200 Subject: [PATCH 099/101] Revert "Added libucontext for android & replaced x86 intrinsics with sse2neon implementations" This reverts commit 8e666d32d0147abb6ac6b9ea2610cc5cbb6f291d. --- .gitmodules | 3 - CMakeLists.txt | 1 - dependencies/libucontext | 1 - src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 2 + .../HW/Latte/LatteAddrLib/LatteAddrLib.cpp | 7 + src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 3 + src/Common/cpu_features.cpp | 11 - src/Common/precompiled.h | 30 +- src/Common/sse2neon.h | 9236 ----------------- src/android/app/build.gradle | 3 +- src/android/app/src/main/cpp/EmulationState.h | 4 +- src/util/CMakeLists.txt | 8 +- src/util/Fiber/FiberUnix.cpp | 15 +- src/util/crypto/aes128.cpp | 7 + vcpkg.json | 5 +- 15 files changed, 58 insertions(+), 9278 deletions(-) delete mode 160000 dependencies/libucontext delete mode 100644 src/Common/sse2neon.h diff --git a/.gitmodules b/.gitmodules index 1042b445..0d95d984 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,6 +16,3 @@ [submodule "dependencies/imgui"] path = dependencies/imgui url = https://github.com/ocornut/imgui -[submodule "dependencies/libucontext"] - path = dependencies/libucontext - url = https://github.com/SSimco/libucontext diff --git a/CMakeLists.txt b/CMakeLists.txt index a459d462..bbf26662 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,7 +142,6 @@ find_package(pugixml REQUIRED) find_package(RapidJSON REQUIRED) find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED) if(ANDROID) - add_subdirectory(dependencies/libucontext EXCLUDE_FROM_ALL) find_package(Boost COMPONENTS context iostreams REQUIRED) endif() find_package(libzip REQUIRED) diff --git a/dependencies/libucontext b/dependencies/libucontext deleted file mode 160000 index be80075e..00000000 --- a/dependencies/libucontext +++ /dev/null @@ -1 +0,0 @@ -Subproject commit be80075e957c4a61a6415c280802fea9001201a2 diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index 83ef7c30..5e092c55 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -338,6 +338,7 @@ void LatteShaderCache_Load() if (g_renderer->GetType() == RendererAPI::Vulkan) LatteShaderCache_LoadVulkanPipelineCache(cacheTitleId); +#if !__ANDROID__ g_renderer->BeginFrame(true); if (g_renderer->ImguiBegin(true)) { @@ -350,6 +351,7 @@ void LatteShaderCache_Load() LatteShaderCache_drawBackgroundImage(g_shaderCacheLoaderState.textureDRCId, 854, 480); g_renderer->ImguiEnd(); } +#endif // __ANDROID__ g_renderer->SwapBuffers(true, true); if (g_shaderCacheLoaderState.textureTVId) diff --git a/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp b/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp index 80de2895..c111e5af 100644 --- a/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp +++ b/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp @@ -2,6 +2,9 @@ #include "Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.h" #include "Cafe/OS/libs/gx2/GX2_Surface.h" #include +#if __ANDROID__ +#include +#endif /* Info: @@ -72,7 +75,11 @@ namespace LatteAddrLib uint32 NextPow2(uint32 dim) { +#if __ANDROID__ + return boost::core::bit_ceil(dim); +#else return std::bit_ceil(dim); +#endif } uint32 GetBitsPerPixel(E_HWSURFFMT format, uint32* pElemMode, uint32* pExpandX, uint32* pExpandY) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 9271a1a4..3701a4d7 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -1124,7 +1124,9 @@ namespace coreinit { OSHostThread* hostThread = (OSHostThread*)_thread; + #if defined(ARCH_X86_64) _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero + #endif PPCInterpreter_t* hCPU = &hostThread->ppcInstance; __OSLoadThread(hostThread->m_thread, hCPU, hostThread->selectedCore); @@ -1168,6 +1170,7 @@ namespace coreinit { SetThreadName(fmt::format("OSSchedulerThread[core={}]", (uintptr_t)_assignedCoreIndex).c_str()); t_assignedCoreIndex = (sint32)(uintptr_t)_assignedCoreIndex; + #if defined(ARCH_X86_64) _mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero #endif diff --git a/src/Common/cpu_features.cpp b/src/Common/cpu_features.cpp index d2cb98a6..dfea8851 100644 --- a/src/Common/cpu_features.cpp +++ b/src/Common/cpu_features.cpp @@ -61,17 +61,6 @@ CPUFeaturesImpl::CPUFeaturesImpl() memcpy(m_cpuBrandName + 32, cpuInfo, sizeof(cpuInfo)); } #endif -#if defined(__aarch64__) - x86.ssse3 = true; - x86.sse4_1 = true; - x86.avx = true; - x86.avx2 = true; - x86.lzcnt = true; - x86.movbe = true; - x86.bmi2 = true; - x86.aesni = true; - x86.invariant_tsc = true; -#endif } std::string CPUFeaturesImpl::GetCPUName() diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 77234168..9c31706c 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -41,11 +41,6 @@ #include #endif -#if defined(__aarch64__) -#include "sse2neon.h" -#endif - - // c++ includes #include #include @@ -337,6 +332,23 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor, // On aarch64 we handle some of the x86 intrinsics by implementing them as wrappers #if defined(__aarch64__) +inline void _mm_pause() +{ + asm volatile("yield"); +} + +inline uint64 __rdtsc() +{ + uint64 t; + asm volatile("mrs %0, cntvct_el0" : "=r" (t)); + return t; +} + +inline void _mm_mfence() +{ + +} + inline unsigned char _addcarry_u64(unsigned char carry, unsigned long long a, unsigned long long b, unsigned long long *result) { *result = a + b + (unsigned long long)carry; @@ -504,16 +516,24 @@ inline std::string_view _utf8Wrapper(std::u8string_view input) // convert fs::path to utf8 encoded string inline std::string _pathToUtf8(const fs::path& path) { +#if __ANDROID__ + return path.generic_string(); +#else std::u8string strU8 = path.generic_u8string(); std::string v((const char*)strU8.data(), strU8.size()); return v; +#endif // __ANDROID__ } // convert utf8 encoded string to fs::path inline fs::path _utf8ToPath(std::string_view input) { +#if __ANDROID__ + return fs::path(input); +#else std::basic_string_view v((char8_t*)input.data(), input.size()); return fs::path(v); +#endif // __ANDROID__ } // locale-independent variant of tolower() which also matches Wii U behavior diff --git a/src/Common/sse2neon.h b/src/Common/sse2neon.h deleted file mode 100644 index 32f688c1..00000000 --- a/src/Common/sse2neon.h +++ /dev/null @@ -1,9236 +0,0 @@ -#ifndef SSE2NEON_H -#define SSE2NEON_H - -// This header file provides a simple API translation layer -// between SSE intrinsics to their corresponding Arm/Aarch64 NEON versions -// -// Contributors to this work are: -// John W. Ratcliff -// Brandon Rowlett -// Ken Fast -// Eric van Beurden -// Alexander Potylitsin -// Hasindu Gamaarachchi -// Jim Huang -// Mark Cheng -// Malcolm James MacLeod -// Devin Hussey (easyaspi314) -// Sebastian Pop -// Developer Ecosystem Engineering -// Danila Kutenin -// François Turban (JishinMaster) -// Pei-Hsuan Hung -// Yang-Hao Yuan -// Syoyo Fujita -// Brecht Van Lommel -// Jonathan Hue -// Cuda Chen -// Aymen Qader -// Anthony Roberts - -/* - * sse2neon is freely redistributable under the MIT License. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Tunable configurations */ - -/* Enable precise implementation of math operations - * This would slow down the computation a bit, but gives consistent result with - * x86 SSE. (e.g. would solve a hole or NaN pixel in the rendering result) - */ -/* _mm_min|max_ps|ss|pd|sd */ -#ifndef SSE2NEON_PRECISE_MINMAX -#define SSE2NEON_PRECISE_MINMAX (0) -#endif -/* _mm_rcp_ps and _mm_div_ps */ -#ifndef SSE2NEON_PRECISE_DIV -#define SSE2NEON_PRECISE_DIV (0) -#endif -/* _mm_sqrt_ps and _mm_rsqrt_ps */ -#ifndef SSE2NEON_PRECISE_SQRT -#define SSE2NEON_PRECISE_SQRT (0) -#endif -/* _mm_dp_pd */ -#ifndef SSE2NEON_PRECISE_DP -#define SSE2NEON_PRECISE_DP (0) -#endif - -/* Enable inclusion of windows.h on MSVC platforms - * This makes _mm_clflush functional on windows, as there is no builtin. - */ -#ifndef SSE2NEON_INCLUDE_WINDOWS_H -#define SSE2NEON_INCLUDE_WINDOWS_H (0) -#endif - -/* compiler specific definitions */ -#if defined(__GNUC__) || defined(__clang__) -#pragma push_macro("FORCE_INLINE") -#pragma push_macro("ALIGN_STRUCT") -#define FORCE_INLINE static inline __attribute__((always_inline)) -#define ALIGN_STRUCT(x) __attribute__((aligned(x))) -#define _sse2neon_likely(x) __builtin_expect(!!(x), 1) -#define _sse2neon_unlikely(x) __builtin_expect(!!(x), 0) -#elif defined(_MSC_VER) -#if _MSVC_TRADITIONAL -#error Using the traditional MSVC preprocessor is not supported! Use /Zc:preprocessor instead. -#endif -#ifndef FORCE_INLINE -#define FORCE_INLINE static inline -#endif -#ifndef ALIGN_STRUCT -#define ALIGN_STRUCT(x) __declspec(align(x)) -#endif -#define _sse2neon_likely(x) (x) -#define _sse2neon_unlikely(x) (x) -#else -#pragma message("Macro name collisions may happen with unsupported compilers.") -#endif - -#if defined(__GNUC__) && __GNUC__ < 10 -#warning "GCC versions earlier than 10 are not supported." -#endif - -/* C language does not allow initializing a variable with a function call. */ -#ifdef __cplusplus -#define _sse2neon_const static const -#else -#define _sse2neon_const const -#endif - -#include -#include - -#if defined(_WIN32) -/* Definitions for _mm_{malloc,free} are provided by - * from both MinGW-w64 and MSVC. - */ -#define SSE2NEON_ALLOC_DEFINED -#endif - -/* If using MSVC */ -#ifdef _MSC_VER -#include -#if SSE2NEON_INCLUDE_WINDOWS_H -#include -#include -#endif - -#if !defined(__cplusplus) -#error SSE2NEON only supports C++ compilation with this compiler -#endif - -#ifdef SSE2NEON_ALLOC_DEFINED -#include -#endif - -#if (defined(_M_AMD64) || defined(__x86_64__)) || \ - (defined(_M_ARM64) || defined(__arm64__)) -#define SSE2NEON_HAS_BITSCAN64 -#endif -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define _sse2neon_define0(type, s, body) \ - __extension__({ \ - type _a = (s); \ - body \ - }) -#define _sse2neon_define1(type, s, body) \ - __extension__({ \ - type _a = (s); \ - body \ - }) -#define _sse2neon_define2(type, a, b, body) \ - __extension__({ \ - type _a = (a), _b = (b); \ - body \ - }) -#define _sse2neon_return(ret) (ret) -#else -#define _sse2neon_define0(type, a, body) [=](type _a) { body }(a) -#define _sse2neon_define1(type, a, body) [](type _a) { body }(a) -#define _sse2neon_define2(type, a, b, body) \ - [](type _a, type _b) { body }((a), (b)) -#define _sse2neon_return(ret) return ret -#endif - -#define _sse2neon_init(...) \ - { \ - __VA_ARGS__ \ - } - -/* Compiler barrier */ -#if defined(_MSC_VER) -#define SSE2NEON_BARRIER() _ReadWriteBarrier() -#else -#define SSE2NEON_BARRIER() \ - do { \ - __asm__ __volatile__("" ::: "memory"); \ - (void) 0; \ - } while (0) -#endif - -/* Memory barriers - * __atomic_thread_fence does not include a compiler barrier; instead, - * the barrier is part of __atomic_load/__atomic_store's "volatile-like" - * semantics. - */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) -#include -#endif - -FORCE_INLINE void _sse2neon_smp_mb(void) -{ - SSE2NEON_BARRIER(); -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ - !defined(__STDC_NO_ATOMICS__) - atomic_thread_fence(memory_order_seq_cst); -#elif defined(__GNUC__) || defined(__clang__) - __atomic_thread_fence(__ATOMIC_SEQ_CST); -#else /* MSVC */ - __dmb(_ARM64_BARRIER_ISH); -#endif -} - -/* Architecture-specific build options */ -/* FIXME: #pragma GCC push_options is only available on GCC */ -#if defined(__GNUC__) -#if defined(__arm__) && __ARM_ARCH == 7 -/* According to ARM C Language Extensions Architecture specification, - * __ARM_NEON is defined to a value indicating the Advanced SIMD (NEON) - * architecture supported. - */ -#if !defined(__ARM_NEON) || !defined(__ARM_NEON__) -#error "You must enable NEON instructions (e.g. -mfpu=neon) to use SSE2NEON." -#endif -#if !defined(__clang__) -#pragma GCC push_options -#pragma GCC target("fpu=neon") -#endif -#elif defined(__aarch64__) || defined(_M_ARM64) -#if !defined(__clang__) && !defined(_MSC_VER) -#pragma GCC push_options -#pragma GCC target("+simd") -#endif -#elif __ARM_ARCH == 8 -#if !defined(__ARM_NEON) || !defined(__ARM_NEON__) -#error \ - "You must enable NEON instructions (e.g. -mfpu=neon-fp-armv8) to use SSE2NEON." -#endif -#if !defined(__clang__) && !defined(_MSC_VER) -#pragma GCC push_options -#endif -#else -#error "Unsupported target. Must be either ARMv7-A+NEON or ARMv8-A." -#endif -#endif - -#include -#if (!defined(__aarch64__) && !defined(_M_ARM64)) && (__ARM_ARCH == 8) -#if defined __has_include && __has_include() -#include -#endif -#endif - -/* Apple Silicon cache lines are double of what is commonly used by Intel, AMD - * and other Arm microarchitectures use. - * From sysctl -a on Apple M1: - * hw.cachelinesize: 128 - */ -#if defined(__APPLE__) && (defined(__aarch64__) || defined(__arm64__)) -#define SSE2NEON_CACHELINE_SIZE 128 -#else -#define SSE2NEON_CACHELINE_SIZE 64 -#endif - -/* Rounding functions require either Aarch64 instructions or libm fallback */ -#if !defined(__aarch64__) && !defined(_M_ARM64) -#include -#endif - -/* On ARMv7, some registers, such as PMUSERENR and PMCCNTR, are read-only - * or even not accessible in user mode. - * To write or access to these registers in user mode, - * we have to perform syscall instead. - */ -#if (!defined(__aarch64__) && !defined(_M_ARM64)) -#include -#endif - -/* "__has_builtin" can be used to query support for built-in functions - * provided by gcc/clang and other compilers that support it. - */ -#ifndef __has_builtin /* GCC prior to 10 or non-clang compilers */ -/* Compatibility with gcc <= 9 */ -#if defined(__GNUC__) && (__GNUC__ <= 9) -#define __has_builtin(x) HAS##x -#define HAS__builtin_popcount 1 -#define HAS__builtin_popcountll 1 - -// __builtin_shuffle introduced in GCC 4.7.0 -#if (__GNUC__ >= 5) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) -#define HAS__builtin_shuffle 1 -#else -#define HAS__builtin_shuffle 0 -#endif - -#define HAS__builtin_shufflevector 0 -#define HAS__builtin_nontemporal_store 0 -#else -#define __has_builtin(x) 0 -#endif -#endif - -/** - * MACRO for shuffle parameter for _mm_shuffle_ps(). - * Argument fp3 is a digit[0123] that represents the fp from argument "b" - * of mm_shuffle_ps that will be placed in fp3 of result. fp2 is the same - * for fp2 in result. fp1 is a digit[0123] that represents the fp from - * argument "a" of mm_shuffle_ps that will be places in fp1 of result. - * fp0 is the same for fp0 of result. - */ -#define _MM_SHUFFLE(fp3, fp2, fp1, fp0) \ - (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0))) - -#if __has_builtin(__builtin_shufflevector) -#define _sse2neon_shuffle(type, a, b, ...) \ - __builtin_shufflevector(a, b, __VA_ARGS__) -#elif __has_builtin(__builtin_shuffle) -#define _sse2neon_shuffle(type, a, b, ...) \ - __extension__({ \ - type tmp = {__VA_ARGS__}; \ - __builtin_shuffle(a, b, tmp); \ - }) -#endif - -#ifdef _sse2neon_shuffle -#define vshuffle_s16(a, b, ...) _sse2neon_shuffle(int16x4_t, a, b, __VA_ARGS__) -#define vshuffleq_s16(a, b, ...) _sse2neon_shuffle(int16x8_t, a, b, __VA_ARGS__) -#define vshuffle_s32(a, b, ...) _sse2neon_shuffle(int32x2_t, a, b, __VA_ARGS__) -#define vshuffleq_s32(a, b, ...) _sse2neon_shuffle(int32x4_t, a, b, __VA_ARGS__) -#define vshuffle_s64(a, b, ...) _sse2neon_shuffle(int64x1_t, a, b, __VA_ARGS__) -#define vshuffleq_s64(a, b, ...) _sse2neon_shuffle(int64x2_t, a, b, __VA_ARGS__) -#endif - -/* Rounding mode macros. */ -#define _MM_FROUND_TO_NEAREST_INT 0x00 -#define _MM_FROUND_TO_NEG_INF 0x01 -#define _MM_FROUND_TO_POS_INF 0x02 -#define _MM_FROUND_TO_ZERO 0x03 -#define _MM_FROUND_CUR_DIRECTION 0x04 -#define _MM_FROUND_NO_EXC 0x08 -#define _MM_FROUND_RAISE_EXC 0x00 -#define _MM_FROUND_NINT (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_RAISE_EXC) -#define _MM_FROUND_FLOOR (_MM_FROUND_TO_NEG_INF | _MM_FROUND_RAISE_EXC) -#define _MM_FROUND_CEIL (_MM_FROUND_TO_POS_INF | _MM_FROUND_RAISE_EXC) -#define _MM_FROUND_TRUNC (_MM_FROUND_TO_ZERO | _MM_FROUND_RAISE_EXC) -#define _MM_FROUND_RINT (_MM_FROUND_CUR_DIRECTION | _MM_FROUND_RAISE_EXC) -#define _MM_FROUND_NEARBYINT (_MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC) -#define _MM_ROUND_NEAREST 0x0000 -#define _MM_ROUND_DOWN 0x2000 -#define _MM_ROUND_UP 0x4000 -#define _MM_ROUND_TOWARD_ZERO 0x6000 -/* Flush zero mode macros. */ -#define _MM_FLUSH_ZERO_MASK 0x8000 -#define _MM_FLUSH_ZERO_ON 0x8000 -#define _MM_FLUSH_ZERO_OFF 0x0000 -/* Denormals are zeros mode macros. */ -#define _MM_DENORMALS_ZERO_MASK 0x0040 -#define _MM_DENORMALS_ZERO_ON 0x0040 -#define _MM_DENORMALS_ZERO_OFF 0x0000 - -/* indicate immediate constant argument in a given range */ -#define __constrange(a, b) const - -/* A few intrinsics accept traditional data types like ints or floats, but - * most operate on data types that are specific to SSE. - * If a vector type ends in d, it contains doubles, and if it does not have - * a suffix, it contains floats. An integer vector type can contain any type - * of integer, from chars to shorts to unsigned long longs. - */ -typedef int64x1_t __m64; -typedef float32x4_t __m128; /* 128-bit vector containing 4 floats */ -// On ARM 32-bit architecture, the float64x2_t is not supported. -// The data type __m128d should be represented in a different way for related -// intrinsic conversion. -#if defined(__aarch64__) || defined(_M_ARM64) -typedef float64x2_t __m128d; /* 128-bit vector containing 2 doubles */ -#else -typedef float32x4_t __m128d; -#endif -typedef int64x2_t __m128i; /* 128-bit vector containing integers */ - -// __int64 is defined in the Intrinsics Guide which maps to different datatype -// in different data model -#if !(defined(_WIN32) || defined(_WIN64) || defined(__int64)) -#if (defined(__x86_64__) || defined(__i386__)) -#define __int64 long long -#else -#define __int64 int64_t -#endif -#endif - -/* type-safe casting between types */ - -#define vreinterpretq_m128_f16(x) vreinterpretq_f32_f16(x) -#define vreinterpretq_m128_f32(x) (x) -#define vreinterpretq_m128_f64(x) vreinterpretq_f32_f64(x) - -#define vreinterpretq_m128_u8(x) vreinterpretq_f32_u8(x) -#define vreinterpretq_m128_u16(x) vreinterpretq_f32_u16(x) -#define vreinterpretq_m128_u32(x) vreinterpretq_f32_u32(x) -#define vreinterpretq_m128_u64(x) vreinterpretq_f32_u64(x) - -#define vreinterpretq_m128_s8(x) vreinterpretq_f32_s8(x) -#define vreinterpretq_m128_s16(x) vreinterpretq_f32_s16(x) -#define vreinterpretq_m128_s32(x) vreinterpretq_f32_s32(x) -#define vreinterpretq_m128_s64(x) vreinterpretq_f32_s64(x) - -#define vreinterpretq_f16_m128(x) vreinterpretq_f16_f32(x) -#define vreinterpretq_f32_m128(x) (x) -#define vreinterpretq_f64_m128(x) vreinterpretq_f64_f32(x) - -#define vreinterpretq_u8_m128(x) vreinterpretq_u8_f32(x) -#define vreinterpretq_u16_m128(x) vreinterpretq_u16_f32(x) -#define vreinterpretq_u32_m128(x) vreinterpretq_u32_f32(x) -#define vreinterpretq_u64_m128(x) vreinterpretq_u64_f32(x) - -#define vreinterpretq_s8_m128(x) vreinterpretq_s8_f32(x) -#define vreinterpretq_s16_m128(x) vreinterpretq_s16_f32(x) -#define vreinterpretq_s32_m128(x) vreinterpretq_s32_f32(x) -#define vreinterpretq_s64_m128(x) vreinterpretq_s64_f32(x) - -#define vreinterpretq_m128i_s8(x) vreinterpretq_s64_s8(x) -#define vreinterpretq_m128i_s16(x) vreinterpretq_s64_s16(x) -#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x) -#define vreinterpretq_m128i_s64(x) (x) - -#define vreinterpretq_m128i_u8(x) vreinterpretq_s64_u8(x) -#define vreinterpretq_m128i_u16(x) vreinterpretq_s64_u16(x) -#define vreinterpretq_m128i_u32(x) vreinterpretq_s64_u32(x) -#define vreinterpretq_m128i_u64(x) vreinterpretq_s64_u64(x) - -#define vreinterpretq_f32_m128i(x) vreinterpretq_f32_s64(x) -#define vreinterpretq_f64_m128i(x) vreinterpretq_f64_s64(x) - -#define vreinterpretq_s8_m128i(x) vreinterpretq_s8_s64(x) -#define vreinterpretq_s16_m128i(x) vreinterpretq_s16_s64(x) -#define vreinterpretq_s32_m128i(x) vreinterpretq_s32_s64(x) -#define vreinterpretq_s64_m128i(x) (x) - -#define vreinterpretq_u8_m128i(x) vreinterpretq_u8_s64(x) -#define vreinterpretq_u16_m128i(x) vreinterpretq_u16_s64(x) -#define vreinterpretq_u32_m128i(x) vreinterpretq_u32_s64(x) -#define vreinterpretq_u64_m128i(x) vreinterpretq_u64_s64(x) - -#define vreinterpret_m64_s8(x) vreinterpret_s64_s8(x) -#define vreinterpret_m64_s16(x) vreinterpret_s64_s16(x) -#define vreinterpret_m64_s32(x) vreinterpret_s64_s32(x) -#define vreinterpret_m64_s64(x) (x) - -#define vreinterpret_m64_u8(x) vreinterpret_s64_u8(x) -#define vreinterpret_m64_u16(x) vreinterpret_s64_u16(x) -#define vreinterpret_m64_u32(x) vreinterpret_s64_u32(x) -#define vreinterpret_m64_u64(x) vreinterpret_s64_u64(x) - -#define vreinterpret_m64_f16(x) vreinterpret_s64_f16(x) -#define vreinterpret_m64_f32(x) vreinterpret_s64_f32(x) -#define vreinterpret_m64_f64(x) vreinterpret_s64_f64(x) - -#define vreinterpret_u8_m64(x) vreinterpret_u8_s64(x) -#define vreinterpret_u16_m64(x) vreinterpret_u16_s64(x) -#define vreinterpret_u32_m64(x) vreinterpret_u32_s64(x) -#define vreinterpret_u64_m64(x) vreinterpret_u64_s64(x) - -#define vreinterpret_s8_m64(x) vreinterpret_s8_s64(x) -#define vreinterpret_s16_m64(x) vreinterpret_s16_s64(x) -#define vreinterpret_s32_m64(x) vreinterpret_s32_s64(x) -#define vreinterpret_s64_m64(x) (x) - -#define vreinterpret_f32_m64(x) vreinterpret_f32_s64(x) - -#if defined(__aarch64__) || defined(_M_ARM64) -#define vreinterpretq_m128d_s32(x) vreinterpretq_f64_s32(x) -#define vreinterpretq_m128d_s64(x) vreinterpretq_f64_s64(x) - -#define vreinterpretq_m128d_u64(x) vreinterpretq_f64_u64(x) - -#define vreinterpretq_m128d_f32(x) vreinterpretq_f64_f32(x) -#define vreinterpretq_m128d_f64(x) (x) - -#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f64(x) - -#define vreinterpretq_u32_m128d(x) vreinterpretq_u32_f64(x) -#define vreinterpretq_u64_m128d(x) vreinterpretq_u64_f64(x) - -#define vreinterpretq_f64_m128d(x) (x) -#define vreinterpretq_f32_m128d(x) vreinterpretq_f32_f64(x) -#else -#define vreinterpretq_m128d_s32(x) vreinterpretq_f32_s32(x) -#define vreinterpretq_m128d_s64(x) vreinterpretq_f32_s64(x) - -#define vreinterpretq_m128d_u32(x) vreinterpretq_f32_u32(x) -#define vreinterpretq_m128d_u64(x) vreinterpretq_f32_u64(x) - -#define vreinterpretq_m128d_f32(x) (x) - -#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f32(x) - -#define vreinterpretq_u32_m128d(x) vreinterpretq_u32_f32(x) -#define vreinterpretq_u64_m128d(x) vreinterpretq_u64_f32(x) - -#define vreinterpretq_f32_m128d(x) (x) -#endif - -// A struct is defined in this header file called 'SIMDVec' which can be used -// by applications which attempt to access the contents of an __m128 struct -// directly. It is important to note that accessing the __m128 struct directly -// is bad coding practice by Microsoft: @see: -// https://learn.microsoft.com/en-us/cpp/cpp/m128 -// -// However, some legacy source code may try to access the contents of an __m128 -// struct directly so the developer can use the SIMDVec as an alias for it. Any -// casting must be done manually by the developer, as you cannot cast or -// otherwise alias the base NEON data type for intrinsic operations. -// -// union intended to allow direct access to an __m128 variable using the names -// that the MSVC compiler provides. This union should really only be used when -// trying to access the members of the vector as integer values. GCC/clang -// allow native access to the float members through a simple array access -// operator (in C since 4.6, in C++ since 4.8). -// -// Ideally direct accesses to SIMD vectors should not be used since it can cause -// a performance hit. If it really is needed however, the original __m128 -// variable can be aliased with a pointer to this union and used to access -// individual components. The use of this union should be hidden behind a macro -// that is used throughout the codebase to access the members instead of always -// declaring this type of variable. -typedef union ALIGN_STRUCT(16) SIMDVec { - float m128_f32[4]; // as floats - DON'T USE. Added for convenience. - int8_t m128_i8[16]; // as signed 8-bit integers. - int16_t m128_i16[8]; // as signed 16-bit integers. - int32_t m128_i32[4]; // as signed 32-bit integers. - int64_t m128_i64[2]; // as signed 64-bit integers. - uint8_t m128_u8[16]; // as unsigned 8-bit integers. - uint16_t m128_u16[8]; // as unsigned 16-bit integers. - uint32_t m128_u32[4]; // as unsigned 32-bit integers. - uint64_t m128_u64[2]; // as unsigned 64-bit integers. -} SIMDVec; - -// casting using SIMDVec -#define vreinterpretq_nth_u64_m128i(x, n) (((SIMDVec *) &x)->m128_u64[n]) -#define vreinterpretq_nth_u32_m128i(x, n) (((SIMDVec *) &x)->m128_u32[n]) -#define vreinterpretq_nth_u8_m128i(x, n) (((SIMDVec *) &x)->m128_u8[n]) - -/* SSE macros */ -#define _MM_GET_FLUSH_ZERO_MODE _sse2neon_mm_get_flush_zero_mode -#define _MM_SET_FLUSH_ZERO_MODE _sse2neon_mm_set_flush_zero_mode -#define _MM_GET_DENORMALS_ZERO_MODE _sse2neon_mm_get_denormals_zero_mode -#define _MM_SET_DENORMALS_ZERO_MODE _sse2neon_mm_set_denormals_zero_mode - -// Function declaration -// SSE -FORCE_INLINE unsigned int _MM_GET_ROUNDING_MODE(void); -FORCE_INLINE __m128 _mm_move_ss(__m128, __m128); -FORCE_INLINE __m128 _mm_or_ps(__m128, __m128); -FORCE_INLINE __m128 _mm_set_ps1(float); -FORCE_INLINE __m128 _mm_setzero_ps(void); -// SSE2 -FORCE_INLINE __m128i _mm_and_si128(__m128i, __m128i); -FORCE_INLINE __m128i _mm_castps_si128(__m128); -FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i, __m128i); -FORCE_INLINE __m128i _mm_cvtps_epi32(__m128); -FORCE_INLINE __m128d _mm_move_sd(__m128d, __m128d); -FORCE_INLINE __m128i _mm_or_si128(__m128i, __m128i); -FORCE_INLINE __m128i _mm_set_epi32(int, int, int, int); -FORCE_INLINE __m128i _mm_set_epi64x(int64_t, int64_t); -FORCE_INLINE __m128d _mm_set_pd(double, double); -FORCE_INLINE __m128i _mm_set1_epi32(int); -FORCE_INLINE __m128i _mm_setzero_si128(void); -// SSE4.1 -FORCE_INLINE __m128d _mm_ceil_pd(__m128d); -FORCE_INLINE __m128 _mm_ceil_ps(__m128); -FORCE_INLINE __m128d _mm_floor_pd(__m128d); -FORCE_INLINE __m128 _mm_floor_ps(__m128); -FORCE_INLINE __m128d _mm_round_pd(__m128d, int); -FORCE_INLINE __m128 _mm_round_ps(__m128, int); -// SSE4.2 -FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t, uint8_t); - -/* Backwards compatibility for compilers with lack of specific type support */ - -// Older gcc does not define vld1q_u8_x4 type -#if defined(__GNUC__) && !defined(__clang__) && \ - ((__GNUC__ <= 13 && defined(__arm__)) || \ - (__GNUC__ == 10 && __GNUC_MINOR__ < 3 && defined(__aarch64__)) || \ - (__GNUC__ <= 9 && defined(__aarch64__))) -FORCE_INLINE uint8x16x4_t _sse2neon_vld1q_u8_x4(const uint8_t *p) -{ - uint8x16x4_t ret; - ret.val[0] = vld1q_u8(p + 0); - ret.val[1] = vld1q_u8(p + 16); - ret.val[2] = vld1q_u8(p + 32); - ret.val[3] = vld1q_u8(p + 48); - return ret; -} -#else -// Wraps vld1q_u8_x4 -FORCE_INLINE uint8x16x4_t _sse2neon_vld1q_u8_x4(const uint8_t *p) -{ - return vld1q_u8_x4(p); -} -#endif - -#if !defined(__aarch64__) && !defined(_M_ARM64) -/* emulate vaddv u8 variant */ -FORCE_INLINE uint8_t _sse2neon_vaddv_u8(uint8x8_t v8) -{ - const uint64x1_t v1 = vpaddl_u32(vpaddl_u16(vpaddl_u8(v8))); - return vget_lane_u8(vreinterpret_u8_u64(v1), 0); -} -#else -// Wraps vaddv_u8 -FORCE_INLINE uint8_t _sse2neon_vaddv_u8(uint8x8_t v8) -{ - return vaddv_u8(v8); -} -#endif - -#if !defined(__aarch64__) && !defined(_M_ARM64) -/* emulate vaddvq u8 variant */ -FORCE_INLINE uint8_t _sse2neon_vaddvq_u8(uint8x16_t a) -{ - uint8x8_t tmp = vpadd_u8(vget_low_u8(a), vget_high_u8(a)); - uint8_t res = 0; - for (int i = 0; i < 8; ++i) - res += tmp[i]; - return res; -} -#else -// Wraps vaddvq_u8 -FORCE_INLINE uint8_t _sse2neon_vaddvq_u8(uint8x16_t a) -{ - return vaddvq_u8(a); -} -#endif - -#if !defined(__aarch64__) && !defined(_M_ARM64) -/* emulate vaddvq u16 variant */ -FORCE_INLINE uint16_t _sse2neon_vaddvq_u16(uint16x8_t a) -{ - uint32x4_t m = vpaddlq_u16(a); - uint64x2_t n = vpaddlq_u32(m); - uint64x1_t o = vget_low_u64(n) + vget_high_u64(n); - - return vget_lane_u32((uint32x2_t) o, 0); -} -#else -// Wraps vaddvq_u16 -FORCE_INLINE uint16_t _sse2neon_vaddvq_u16(uint16x8_t a) -{ - return vaddvq_u16(a); -} -#endif - -/* Function Naming Conventions - * The naming convention of SSE intrinsics is straightforward. A generic SSE - * intrinsic function is given as follows: - * _mm__ - * - * The parts of this format are given as follows: - * 1. describes the operation performed by the intrinsic - * 2. identifies the data type of the function's primary arguments - * - * This last part, , is a little complicated. It identifies the - * content of the input values, and can be set to any of the following values: - * + ps - vectors contain floats (ps stands for packed single-precision) - * + pd - vectors contain doubles (pd stands for packed double-precision) - * + epi8/epi16/epi32/epi64 - vectors contain 8-bit/16-bit/32-bit/64-bit - * signed integers - * + epu8/epu16/epu32/epu64 - vectors contain 8-bit/16-bit/32-bit/64-bit - * unsigned integers - * + si128 - unspecified 128-bit vector or 256-bit vector - * + m128/m128i/m128d - identifies input vector types when they are different - * than the type of the returned vector - * - * For example, _mm_setzero_ps. The _mm implies that the function returns - * a 128-bit vector. The _ps at the end implies that the argument vectors - * contain floats. - * - * A complete example: Byte Shuffle - pshufb (_mm_shuffle_epi8) - * // Set packed 16-bit integers. 128 bits, 8 short, per 16 bits - * __m128i v_in = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); - * // Set packed 8-bit integers - * // 128 bits, 16 chars, per 8 bits - * __m128i v_perm = _mm_setr_epi8(1, 0, 2, 3, 8, 9, 10, 11, - * 4, 5, 12, 13, 6, 7, 14, 15); - * // Shuffle packed 8-bit integers - * __m128i v_out = _mm_shuffle_epi8(v_in, v_perm); // pshufb - */ - -/* Constants for use with _mm_prefetch. */ -enum _mm_hint { - _MM_HINT_NTA = 0, /* load data to L1 and L2 cache, mark it as NTA */ - _MM_HINT_T0 = 1, /* load data to L1 and L2 cache */ - _MM_HINT_T1 = 2, /* load data to L2 cache only */ - _MM_HINT_T2 = 3, /* load data to L2 cache only, mark it as NTA */ -}; - -// The bit field mapping to the FPCR(floating-point control register) -typedef struct { - uint16_t res0; - uint8_t res1 : 6; - uint8_t bit22 : 1; - uint8_t bit23 : 1; - uint8_t bit24 : 1; - uint8_t res2 : 7; -#if defined(__aarch64__) || defined(_M_ARM64) - uint32_t res3; -#endif -} fpcr_bitfield; - -// Takes the upper 64 bits of a and places it in the low end of the result -// Takes the lower 64 bits of b and places it into the high end of the result. -FORCE_INLINE __m128 _mm_shuffle_ps_1032(__m128 a, __m128 b) -{ - float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); - float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32(vcombine_f32(a32, b10)); -} - -// takes the lower two 32-bit values from a and swaps them and places in high -// end of result takes the higher two 32 bit values from b and swaps them and -// places in low end of result. -FORCE_INLINE __m128 _mm_shuffle_ps_2301(__m128 a, __m128 b) -{ - float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); - float32x2_t b23 = vrev64_f32(vget_high_f32(vreinterpretq_f32_m128(b))); - return vreinterpretq_m128_f32(vcombine_f32(a01, b23)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_0321(__m128 a, __m128 b) -{ - float32x2_t a21 = vget_high_f32( - vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); - float32x2_t b03 = vget_low_f32( - vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); - return vreinterpretq_m128_f32(vcombine_f32(a21, b03)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_2103(__m128 a, __m128 b) -{ - float32x2_t a03 = vget_low_f32( - vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); - float32x2_t b21 = vget_high_f32( - vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); - return vreinterpretq_m128_f32(vcombine_f32(a03, b21)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_1010(__m128 a, __m128 b) -{ - float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); - float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_1001(__m128 a, __m128 b) -{ - float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); - float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32(vcombine_f32(a01, b10)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_0101(__m128 a, __m128 b) -{ - float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); - float32x2_t b01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(b))); - return vreinterpretq_m128_f32(vcombine_f32(a01, b01)); -} - -// keeps the low 64 bits of b in the low and puts the high 64 bits of a in the -// high -FORCE_INLINE __m128 _mm_shuffle_ps_3210(__m128 a, __m128 b) -{ - float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); - float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32(vcombine_f32(a10, b32)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_0011(__m128 a, __m128 b) -{ - float32x2_t a11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 1); - float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); - return vreinterpretq_m128_f32(vcombine_f32(a11, b00)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_0022(__m128 a, __m128 b) -{ - float32x2_t a22 = - vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); - float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); - return vreinterpretq_m128_f32(vcombine_f32(a22, b00)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_2200(__m128 a, __m128 b) -{ - float32x2_t a00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 0); - float32x2_t b22 = - vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(b)), 0); - return vreinterpretq_m128_f32(vcombine_f32(a00, b22)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_3202(__m128 a, __m128 b) -{ - float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); - float32x2_t a22 = - vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); - float32x2_t a02 = vset_lane_f32(a0, a22, 1); /* TODO: use vzip ?*/ - float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32(vcombine_f32(a02, b32)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_1133(__m128 a, __m128 b) -{ - float32x2_t a33 = - vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 1); - float32x2_t b11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 1); - return vreinterpretq_m128_f32(vcombine_f32(a33, b11)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_2010(__m128 a, __m128 b) -{ - float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); - float32_t b2 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 2); - float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); - float32x2_t b20 = vset_lane_f32(b2, b00, 1); - return vreinterpretq_m128_f32(vcombine_f32(a10, b20)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_2001(__m128 a, __m128 b) -{ - float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); - float32_t b2 = vgetq_lane_f32(b, 2); - float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); - float32x2_t b20 = vset_lane_f32(b2, b00, 1); - return vreinterpretq_m128_f32(vcombine_f32(a01, b20)); -} - -FORCE_INLINE __m128 _mm_shuffle_ps_2032(__m128 a, __m128 b) -{ - float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); - float32_t b2 = vgetq_lane_f32(b, 2); - float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); - float32x2_t b20 = vset_lane_f32(b2, b00, 1); - return vreinterpretq_m128_f32(vcombine_f32(a32, b20)); -} - -// For MSVC, we check only if it is ARM64, as every single ARM64 processor -// supported by WoA has crypto extensions. If this changes in the future, -// this can be verified via the runtime-only method of: -// IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) -#if (defined(_M_ARM64) && !defined(__clang__)) || \ - (defined(__ARM_FEATURE_CRYPTO) && \ - (defined(__aarch64__) || __has_builtin(__builtin_arm_crypto_vmullp64))) -// Wraps vmull_p64 -FORCE_INLINE uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) -{ - poly64_t a = vget_lane_p64(vreinterpret_p64_u64(_a), 0); - poly64_t b = vget_lane_p64(vreinterpret_p64_u64(_b), 0); -#if defined(_MSC_VER) - __n64 a1 = {a}, b1 = {b}; - return vreinterpretq_u64_p128(vmull_p64(a1, b1)); -#else - return vreinterpretq_u64_p128(vmull_p64(a, b)); -#endif -} -#else // ARMv7 polyfill -// ARMv7/some A64 lacks vmull_p64, but it has vmull_p8. -// -// vmull_p8 calculates 8 8-bit->16-bit polynomial multiplies, but we need a -// 64-bit->128-bit polynomial multiply. -// -// It needs some work and is somewhat slow, but it is still faster than all -// known scalar methods. -// -// Algorithm adapted to C from -// https://www.workofard.com/2017/07/ghash-for-low-end-cores/, which is adapted -// from "Fast Software Polynomial Multiplication on ARM Processors Using the -// NEON Engine" by Danilo Camara, Conrado Gouvea, Julio Lopez and Ricardo Dahab -// (https://hal.inria.fr/hal-01506572) -static uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) -{ - poly8x8_t a = vreinterpret_p8_u64(_a); - poly8x8_t b = vreinterpret_p8_u64(_b); - - // Masks - uint8x16_t k48_32 = vcombine_u8(vcreate_u8(0x0000ffffffffffff), - vcreate_u8(0x00000000ffffffff)); - uint8x16_t k16_00 = vcombine_u8(vcreate_u8(0x000000000000ffff), - vcreate_u8(0x0000000000000000)); - - // Do the multiplies, rotating with vext to get all combinations - uint8x16_t d = vreinterpretq_u8_p16(vmull_p8(a, b)); // D = A0 * B0 - uint8x16_t e = - vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 1))); // E = A0 * B1 - uint8x16_t f = - vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 1), b)); // F = A1 * B0 - uint8x16_t g = - vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 2))); // G = A0 * B2 - uint8x16_t h = - vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 2), b)); // H = A2 * B0 - uint8x16_t i = - vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 3))); // I = A0 * B3 - uint8x16_t j = - vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 3), b)); // J = A3 * B0 - uint8x16_t k = - vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 4))); // L = A0 * B4 - - // Add cross products - uint8x16_t l = veorq_u8(e, f); // L = E + F - uint8x16_t m = veorq_u8(g, h); // M = G + H - uint8x16_t n = veorq_u8(i, j); // N = I + J - - // Interleave. Using vzip1 and vzip2 prevents Clang from emitting TBL - // instructions. -#if defined(__aarch64__) - uint8x16_t lm_p0 = vreinterpretq_u8_u64( - vzip1q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); - uint8x16_t lm_p1 = vreinterpretq_u8_u64( - vzip2q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); - uint8x16_t nk_p0 = vreinterpretq_u8_u64( - vzip1q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); - uint8x16_t nk_p1 = vreinterpretq_u8_u64( - vzip2q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); -#else - uint8x16_t lm_p0 = vcombine_u8(vget_low_u8(l), vget_low_u8(m)); - uint8x16_t lm_p1 = vcombine_u8(vget_high_u8(l), vget_high_u8(m)); - uint8x16_t nk_p0 = vcombine_u8(vget_low_u8(n), vget_low_u8(k)); - uint8x16_t nk_p1 = vcombine_u8(vget_high_u8(n), vget_high_u8(k)); -#endif - // t0 = (L) (P0 + P1) << 8 - // t1 = (M) (P2 + P3) << 16 - uint8x16_t t0t1_tmp = veorq_u8(lm_p0, lm_p1); - uint8x16_t t0t1_h = vandq_u8(lm_p1, k48_32); - uint8x16_t t0t1_l = veorq_u8(t0t1_tmp, t0t1_h); - - // t2 = (N) (P4 + P5) << 24 - // t3 = (K) (P6 + P7) << 32 - uint8x16_t t2t3_tmp = veorq_u8(nk_p0, nk_p1); - uint8x16_t t2t3_h = vandq_u8(nk_p1, k16_00); - uint8x16_t t2t3_l = veorq_u8(t2t3_tmp, t2t3_h); - - // De-interleave -#if defined(__aarch64__) - uint8x16_t t0 = vreinterpretq_u8_u64( - vuzp1q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); - uint8x16_t t1 = vreinterpretq_u8_u64( - vuzp2q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); - uint8x16_t t2 = vreinterpretq_u8_u64( - vuzp1q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); - uint8x16_t t3 = vreinterpretq_u8_u64( - vuzp2q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); -#else - uint8x16_t t1 = vcombine_u8(vget_high_u8(t0t1_l), vget_high_u8(t0t1_h)); - uint8x16_t t0 = vcombine_u8(vget_low_u8(t0t1_l), vget_low_u8(t0t1_h)); - uint8x16_t t3 = vcombine_u8(vget_high_u8(t2t3_l), vget_high_u8(t2t3_h)); - uint8x16_t t2 = vcombine_u8(vget_low_u8(t2t3_l), vget_low_u8(t2t3_h)); -#endif - // Shift the cross products - uint8x16_t t0_shift = vextq_u8(t0, t0, 15); // t0 << 8 - uint8x16_t t1_shift = vextq_u8(t1, t1, 14); // t1 << 16 - uint8x16_t t2_shift = vextq_u8(t2, t2, 13); // t2 << 24 - uint8x16_t t3_shift = vextq_u8(t3, t3, 12); // t3 << 32 - - // Accumulate the products - uint8x16_t cross1 = veorq_u8(t0_shift, t1_shift); - uint8x16_t cross2 = veorq_u8(t2_shift, t3_shift); - uint8x16_t mix = veorq_u8(d, cross1); - uint8x16_t r = veorq_u8(mix, cross2); - return vreinterpretq_u64_u8(r); -} -#endif // ARMv7 polyfill - -// C equivalent: -// __m128i _mm_shuffle_epi32_default(__m128i a, -// __constrange(0, 255) int imm) { -// __m128i ret; -// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; -// ret[2] = a[(imm >> 4) & 0x03]; ret[3] = a[(imm >> 6) & 0x03]; -// return ret; -// } -#define _mm_shuffle_epi32_default(a, imm) \ - vreinterpretq_m128i_s32(vsetq_lane_s32( \ - vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 6) & 0x3), \ - vsetq_lane_s32( \ - vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 4) & 0x3), \ - vsetq_lane_s32(vgetq_lane_s32(vreinterpretq_s32_m128i(a), \ - ((imm) >> 2) & 0x3), \ - vmovq_n_s32(vgetq_lane_s32( \ - vreinterpretq_s32_m128i(a), (imm) & (0x3))), \ - 1), \ - 2), \ - 3)) - -// Takes the upper 64 bits of a and places it in the low end of the result -// Takes the lower 64 bits of a and places it into the high end of the result. -FORCE_INLINE __m128i _mm_shuffle_epi_1032(__m128i a) -{ - int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); - int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); - return vreinterpretq_m128i_s32(vcombine_s32(a32, a10)); -} - -// takes the lower two 32-bit values from a and swaps them and places in low end -// of result takes the higher two 32 bit values from a and swaps them and places -// in high end of result. -FORCE_INLINE __m128i _mm_shuffle_epi_2301(__m128i a) -{ - int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); - int32x2_t a23 = vrev64_s32(vget_high_s32(vreinterpretq_s32_m128i(a))); - return vreinterpretq_m128i_s32(vcombine_s32(a01, a23)); -} - -// rotates the least significant 32 bits into the most significant 32 bits, and -// shifts the rest down -FORCE_INLINE __m128i _mm_shuffle_epi_0321(__m128i a) -{ - return vreinterpretq_m128i_s32( - vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 1)); -} - -// rotates the most significant 32 bits into the least significant 32 bits, and -// shifts the rest up -FORCE_INLINE __m128i _mm_shuffle_epi_2103(__m128i a) -{ - return vreinterpretq_m128i_s32( - vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 3)); -} - -// gets the lower 64 bits of a, and places it in the upper 64 bits -// gets the lower 64 bits of a and places it in the lower 64 bits -FORCE_INLINE __m128i _mm_shuffle_epi_1010(__m128i a) -{ - int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); - return vreinterpretq_m128i_s32(vcombine_s32(a10, a10)); -} - -// gets the lower 64 bits of a, swaps the 0 and 1 elements, and places it in the -// lower 64 bits gets the lower 64 bits of a, and places it in the upper 64 bits -FORCE_INLINE __m128i _mm_shuffle_epi_1001(__m128i a) -{ - int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); - int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); - return vreinterpretq_m128i_s32(vcombine_s32(a01, a10)); -} - -// gets the lower 64 bits of a, swaps the 0 and 1 elements and places it in the -// upper 64 bits gets the lower 64 bits of a, swaps the 0 and 1 elements, and -// places it in the lower 64 bits -FORCE_INLINE __m128i _mm_shuffle_epi_0101(__m128i a) -{ - int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); - return vreinterpretq_m128i_s32(vcombine_s32(a01, a01)); -} - -FORCE_INLINE __m128i _mm_shuffle_epi_2211(__m128i a) -{ - int32x2_t a11 = vdup_lane_s32(vget_low_s32(vreinterpretq_s32_m128i(a)), 1); - int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); - return vreinterpretq_m128i_s32(vcombine_s32(a11, a22)); -} - -FORCE_INLINE __m128i _mm_shuffle_epi_0122(__m128i a) -{ - int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); - int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); - return vreinterpretq_m128i_s32(vcombine_s32(a22, a01)); -} - -FORCE_INLINE __m128i _mm_shuffle_epi_3332(__m128i a) -{ - int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); - int32x2_t a33 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 1); - return vreinterpretq_m128i_s32(vcombine_s32(a32, a33)); -} - -#if defined(__aarch64__) || defined(_M_ARM64) -#define _mm_shuffle_epi32_splat(a, imm) \ - vreinterpretq_m128i_s32(vdupq_laneq_s32(vreinterpretq_s32_m128i(a), (imm))) -#else -#define _mm_shuffle_epi32_splat(a, imm) \ - vreinterpretq_m128i_s32( \ - vdupq_n_s32(vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)))) -#endif - -// NEON does not support a general purpose permute intrinsic. -// Shuffle single-precision (32-bit) floating-point elements in a using the -// control in imm8, and store the results in dst. -// -// C equivalent: -// __m128 _mm_shuffle_ps_default(__m128 a, __m128 b, -// __constrange(0, 255) int imm) { -// __m128 ret; -// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; -// ret[2] = b[(imm >> 4) & 0x03]; ret[3] = b[(imm >> 6) & 0x03]; -// return ret; -// } -// -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_ps -#define _mm_shuffle_ps_default(a, b, imm) \ - vreinterpretq_m128_f32(vsetq_lane_f32( \ - vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 6) & 0x3), \ - vsetq_lane_f32( \ - vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 4) & 0x3), \ - vsetq_lane_f32( \ - vgetq_lane_f32(vreinterpretq_f32_m128(a), ((imm) >> 2) & 0x3), \ - vmovq_n_f32( \ - vgetq_lane_f32(vreinterpretq_f32_m128(a), (imm) & (0x3))), \ - 1), \ - 2), \ - 3)) - -// Shuffle 16-bit integers in the low 64 bits of a using the control in imm8. -// Store the results in the low 64 bits of dst, with the high 64 bits being -// copied from a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shufflelo_epi16 -#define _mm_shufflelo_epi16_function(a, imm) \ - _sse2neon_define1( \ - __m128i, a, int16x8_t ret = vreinterpretq_s16_m128i(_a); \ - int16x4_t lowBits = vget_low_s16(ret); \ - ret = vsetq_lane_s16(vget_lane_s16(lowBits, (imm) & (0x3)), ret, 0); \ - ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 2) & 0x3), ret, \ - 1); \ - ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 4) & 0x3), ret, \ - 2); \ - ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 6) & 0x3), ret, \ - 3); \ - _sse2neon_return(vreinterpretq_m128i_s16(ret));) - -// Shuffle 16-bit integers in the high 64 bits of a using the control in imm8. -// Store the results in the high 64 bits of dst, with the low 64 bits being -// copied from a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shufflehi_epi16 -#define _mm_shufflehi_epi16_function(a, imm) \ - _sse2neon_define1( \ - __m128i, a, int16x8_t ret = vreinterpretq_s16_m128i(_a); \ - int16x4_t highBits = vget_high_s16(ret); \ - ret = vsetq_lane_s16(vget_lane_s16(highBits, (imm) & (0x3)), ret, 4); \ - ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 2) & 0x3), ret, \ - 5); \ - ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 4) & 0x3), ret, \ - 6); \ - ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 6) & 0x3), ret, \ - 7); \ - _sse2neon_return(vreinterpretq_m128i_s16(ret));) - -/* MMX */ - -//_mm_empty is a no-op on arm -FORCE_INLINE void _mm_empty(void) {} - -/* SSE */ - -// Add packed single-precision (32-bit) floating-point elements in a and b, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_ps -FORCE_INLINE __m128 _mm_add_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_f32( - vaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Add the lower single-precision (32-bit) floating-point element in a and b, -// store the result in the lower element of dst, and copy the upper 3 packed -// elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_ss -FORCE_INLINE __m128 _mm_add_ss(__m128 a, __m128 b) -{ - float32_t b0 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); - float32x4_t value = vsetq_lane_f32(b0, vdupq_n_f32(0), 0); - // the upper values in the result must be the remnants of . - return vreinterpretq_m128_f32(vaddq_f32(a, value)); -} - -// Compute the bitwise AND of packed single-precision (32-bit) floating-point -// elements in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_and_ps -FORCE_INLINE __m128 _mm_and_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_s32( - vandq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); -} - -// Compute the bitwise NOT of packed single-precision (32-bit) floating-point -// elements in a and then AND with b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_andnot_ps -FORCE_INLINE __m128 _mm_andnot_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_s32( - vbicq_s32(vreinterpretq_s32_m128(b), - vreinterpretq_s32_m128(a))); // *NOTE* argument swap -} - -// Average packed unsigned 16-bit integers in a and b, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_pu16 -FORCE_INLINE __m64 _mm_avg_pu16(__m64 a, __m64 b) -{ - return vreinterpret_m64_u16( - vrhadd_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b))); -} - -// Average packed unsigned 8-bit integers in a and b, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_pu8 -FORCE_INLINE __m64 _mm_avg_pu8(__m64 a, __m64 b) -{ - return vreinterpret_m64_u8( - vrhadd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for equality, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_ps -FORCE_INLINE __m128 _mm_cmpeq_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32( - vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for equality, store the result in the lower element of dst, and copy the -// upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_ss -FORCE_INLINE __m128 _mm_cmpeq_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpeq_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for greater-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_ps -FORCE_INLINE __m128 _mm_cmpge_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32( - vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for greater-than-or-equal, store the result in the lower element of dst, -// and copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_ss -FORCE_INLINE __m128 _mm_cmpge_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpge_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for greater-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_ps -FORCE_INLINE __m128 _mm_cmpgt_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32( - vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for greater-than, store the result in the lower element of dst, and copy -// the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_ss -FORCE_INLINE __m128 _mm_cmpgt_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpgt_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for less-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_ps -FORCE_INLINE __m128 _mm_cmple_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32( - vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for less-than-or-equal, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_ss -FORCE_INLINE __m128 _mm_cmple_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmple_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for less-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_ps -FORCE_INLINE __m128 _mm_cmplt_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32( - vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for less-than, store the result in the lower element of dst, and copy the -// upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_ss -FORCE_INLINE __m128 _mm_cmplt_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmplt_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for not-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_ps -FORCE_INLINE __m128 _mm_cmpneq_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32(vmvnq_u32( - vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for not-equal, store the result in the lower element of dst, and copy the -// upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_ss -FORCE_INLINE __m128 _mm_cmpneq_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpneq_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for not-greater-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_ps -FORCE_INLINE __m128 _mm_cmpnge_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32(vmvnq_u32( - vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for not-greater-than-or-equal, store the result in the lower element of -// dst, and copy the upper 3 packed elements from a to the upper elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_ss -FORCE_INLINE __m128 _mm_cmpnge_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpnge_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for not-greater-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpngt_ps -FORCE_INLINE __m128 _mm_cmpngt_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32(vmvnq_u32( - vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for not-greater-than, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpngt_ss -FORCE_INLINE __m128 _mm_cmpngt_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpngt_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for not-less-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_ps -FORCE_INLINE __m128 _mm_cmpnle_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32(vmvnq_u32( - vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for not-less-than-or-equal, store the result in the lower element of dst, -// and copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_ss -FORCE_INLINE __m128 _mm_cmpnle_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpnle_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// for not-less-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_ps -FORCE_INLINE __m128 _mm_cmpnlt_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_u32(vmvnq_u32( - vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b for not-less-than, store the result in the lower element of dst, and copy -// the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_ss -FORCE_INLINE __m128 _mm_cmpnlt_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpnlt_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// to see if neither is NaN, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_ps -// -// See also: -// http://stackoverflow.com/questions/8627331/what-does-ordered-unordered-comparison-mean -// http://stackoverflow.com/questions/29349621/neon-isnanval-intrinsics -FORCE_INLINE __m128 _mm_cmpord_ps(__m128 a, __m128 b) -{ - // Note: NEON does not have ordered compare builtin - // Need to compare a eq a and b eq b to check for NaN - // Do AND of results to get final - uint32x4_t ceqaa = - vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); - uint32x4_t ceqbb = - vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_u32(vandq_u32(ceqaa, ceqbb)); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b to see if neither is NaN, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_ss -FORCE_INLINE __m128 _mm_cmpord_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpord_ps(a, b)); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b -// to see if either is NaN, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_ps -FORCE_INLINE __m128 _mm_cmpunord_ps(__m128 a, __m128 b) -{ - uint32x4_t f32a = - vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); - uint32x4_t f32b = - vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_u32(vmvnq_u32(vandq_u32(f32a, f32b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b to see if either is NaN, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_ss -FORCE_INLINE __m128 _mm_cmpunord_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_cmpunord_ps(a, b)); -} - -// Compare the lower single-precision (32-bit) floating-point element in a and b -// for equality, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comieq_ss -FORCE_INLINE int _mm_comieq_ss(__m128 a, __m128 b) -{ - uint32x4_t a_eq_b = - vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); - return vgetq_lane_u32(a_eq_b, 0) & 0x1; -} - -// Compare the lower single-precision (32-bit) floating-point element in a and b -// for greater-than-or-equal, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comige_ss -FORCE_INLINE int _mm_comige_ss(__m128 a, __m128 b) -{ - uint32x4_t a_ge_b = - vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); - return vgetq_lane_u32(a_ge_b, 0) & 0x1; -} - -// Compare the lower single-precision (32-bit) floating-point element in a and b -// for greater-than, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comigt_ss -FORCE_INLINE int _mm_comigt_ss(__m128 a, __m128 b) -{ - uint32x4_t a_gt_b = - vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); - return vgetq_lane_u32(a_gt_b, 0) & 0x1; -} - -// Compare the lower single-precision (32-bit) floating-point element in a and b -// for less-than-or-equal, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comile_ss -FORCE_INLINE int _mm_comile_ss(__m128 a, __m128 b) -{ - uint32x4_t a_le_b = - vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); - return vgetq_lane_u32(a_le_b, 0) & 0x1; -} - -// Compare the lower single-precision (32-bit) floating-point element in a and b -// for less-than, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comilt_ss -FORCE_INLINE int _mm_comilt_ss(__m128 a, __m128 b) -{ - uint32x4_t a_lt_b = - vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); - return vgetq_lane_u32(a_lt_b, 0) & 0x1; -} - -// Compare the lower single-precision (32-bit) floating-point element in a and b -// for not-equal, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comineq_ss -FORCE_INLINE int _mm_comineq_ss(__m128 a, __m128 b) -{ - return !_mm_comieq_ss(a, b); -} - -// Convert packed signed 32-bit integers in b to packed single-precision -// (32-bit) floating-point elements, store the results in the lower 2 elements -// of dst, and copy the upper 2 packed elements from a to the upper elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_pi2ps -FORCE_INLINE __m128 _mm_cvt_pi2ps(__m128 a, __m64 b) -{ - return vreinterpretq_m128_f32( - vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), - vget_high_f32(vreinterpretq_f32_m128(a)))); -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 32-bit integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_ps2pi -FORCE_INLINE __m64 _mm_cvt_ps2pi(__m128 a) -{ -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - return vreinterpret_m64_s32( - vget_low_s32(vcvtnq_s32_f32(vrndiq_f32(vreinterpretq_f32_m128(a))))); -#else - return vreinterpret_m64_s32(vcvt_s32_f32(vget_low_f32( - vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION))))); -#endif -} - -// Convert the signed 32-bit integer b to a single-precision (32-bit) -// floating-point element, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_si2ss -FORCE_INLINE __m128 _mm_cvt_si2ss(__m128 a, int b) -{ - return vreinterpretq_m128_f32( - vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); -} - -// Convert the lower single-precision (32-bit) floating-point element in a to a -// 32-bit integer, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvt_ss2si -FORCE_INLINE int _mm_cvt_ss2si(__m128 a) -{ -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - return vgetq_lane_s32(vcvtnq_s32_f32(vrndiq_f32(vreinterpretq_f32_m128(a))), - 0); -#else - float32_t data = vgetq_lane_f32( - vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION)), 0); - return (int32_t) data; -#endif -} - -// Convert packed 16-bit integers in a to packed single-precision (32-bit) -// floating-point elements, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi16_ps -FORCE_INLINE __m128 _mm_cvtpi16_ps(__m64 a) -{ - return vreinterpretq_m128_f32( - vcvtq_f32_s32(vmovl_s16(vreinterpret_s16_m64(a)))); -} - -// Convert packed 32-bit integers in b to packed single-precision (32-bit) -// floating-point elements, store the results in the lower 2 elements of dst, -// and copy the upper 2 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi32_ps -FORCE_INLINE __m128 _mm_cvtpi32_ps(__m128 a, __m64 b) -{ - return vreinterpretq_m128_f32( - vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), - vget_high_f32(vreinterpretq_f32_m128(a)))); -} - -// Convert packed signed 32-bit integers in a to packed single-precision -// (32-bit) floating-point elements, store the results in the lower 2 elements -// of dst, then convert the packed signed 32-bit integers in b to -// single-precision (32-bit) floating-point element, and store the results in -// the upper 2 elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi32x2_ps -FORCE_INLINE __m128 _mm_cvtpi32x2_ps(__m64 a, __m64 b) -{ - return vreinterpretq_m128_f32(vcvtq_f32_s32( - vcombine_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b)))); -} - -// Convert the lower packed 8-bit integers in a to packed single-precision -// (32-bit) floating-point elements, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi8_ps -FORCE_INLINE __m128 _mm_cvtpi8_ps(__m64 a) -{ - return vreinterpretq_m128_f32(vcvtq_f32_s32( - vmovl_s16(vget_low_s16(vmovl_s8(vreinterpret_s8_m64(a)))))); -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 16-bit integers, and store the results in dst. Note: this intrinsic -// will generate 0x7FFF, rather than 0x8000, for input values between 0x7FFF and -// 0x7FFFFFFF. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pi16 -FORCE_INLINE __m64 _mm_cvtps_pi16(__m128 a) -{ - return vreinterpret_m64_s16( - vqmovn_s32(vreinterpretq_s32_m128i(_mm_cvtps_epi32(a)))); -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 32-bit integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pi32 -#define _mm_cvtps_pi32(a) _mm_cvt_ps2pi(a) - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 8-bit integers, and store the results in lower 4 elements of dst. -// Note: this intrinsic will generate 0x7F, rather than 0x80, for input values -// between 0x7F and 0x7FFFFFFF. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pi8 -FORCE_INLINE __m64 _mm_cvtps_pi8(__m128 a) -{ - return vreinterpret_m64_s8(vqmovn_s16( - vcombine_s16(vreinterpret_s16_m64(_mm_cvtps_pi16(a)), vdup_n_s16(0)))); -} - -// Convert packed unsigned 16-bit integers in a to packed single-precision -// (32-bit) floating-point elements, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpu16_ps -FORCE_INLINE __m128 _mm_cvtpu16_ps(__m64 a) -{ - return vreinterpretq_m128_f32( - vcvtq_f32_u32(vmovl_u16(vreinterpret_u16_m64(a)))); -} - -// Convert the lower packed unsigned 8-bit integers in a to packed -// single-precision (32-bit) floating-point elements, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpu8_ps -FORCE_INLINE __m128 _mm_cvtpu8_ps(__m64 a) -{ - return vreinterpretq_m128_f32(vcvtq_f32_u32( - vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_m64(a)))))); -} - -// Convert the signed 32-bit integer b to a single-precision (32-bit) -// floating-point element, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi32_ss -#define _mm_cvtsi32_ss(a, b) _mm_cvt_si2ss(a, b) - -// Convert the signed 64-bit integer b to a single-precision (32-bit) -// floating-point element, store the result in the lower element of dst, and -// copy the upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64_ss -FORCE_INLINE __m128 _mm_cvtsi64_ss(__m128 a, int64_t b) -{ - return vreinterpretq_m128_f32( - vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); -} - -// Copy the lower single-precision (32-bit) floating-point element of a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_f32 -FORCE_INLINE float _mm_cvtss_f32(__m128 a) -{ - return vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); -} - -// Convert the lower single-precision (32-bit) floating-point element in a to a -// 32-bit integer, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_si32 -#define _mm_cvtss_si32(a) _mm_cvt_ss2si(a) - -// Convert the lower single-precision (32-bit) floating-point element in a to a -// 64-bit integer, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_si64 -FORCE_INLINE int64_t _mm_cvtss_si64(__m128 a) -{ -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - return (int64_t) vgetq_lane_f32(vrndiq_f32(vreinterpretq_f32_m128(a)), 0); -#else - float32_t data = vgetq_lane_f32( - vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION)), 0); - return (int64_t) data; -#endif -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 32-bit integers with truncation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtt_ps2pi -FORCE_INLINE __m64 _mm_cvtt_ps2pi(__m128 a) -{ - return vreinterpret_m64_s32( - vget_low_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)))); -} - -// Convert the lower single-precision (32-bit) floating-point element in a to a -// 32-bit integer with truncation, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtt_ss2si -FORCE_INLINE int _mm_cvtt_ss2si(__m128 a) -{ - return vgetq_lane_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)), 0); -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 32-bit integers with truncation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttps_pi32 -#define _mm_cvttps_pi32(a) _mm_cvtt_ps2pi(a) - -// Convert the lower single-precision (32-bit) floating-point element in a to a -// 32-bit integer with truncation, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttss_si32 -#define _mm_cvttss_si32(a) _mm_cvtt_ss2si(a) - -// Convert the lower single-precision (32-bit) floating-point element in a to a -// 64-bit integer with truncation, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttss_si64 -FORCE_INLINE int64_t _mm_cvttss_si64(__m128 a) -{ - return (int64_t) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); -} - -// Divide packed single-precision (32-bit) floating-point elements in a by -// packed elements in b, and store the results in dst. -// Due to ARMv7-A NEON's lack of a precise division intrinsic, we implement -// division by multiplying a by b's reciprocal before using the Newton-Raphson -// method to approximate the results. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_ps -FORCE_INLINE __m128 _mm_div_ps(__m128 a, __m128 b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vdivq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -#else - float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(b)); - recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(b))); - // Additional Netwon-Raphson iteration for accuracy - recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(b))); - return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(a), recip)); -#endif -} - -// Divide the lower single-precision (32-bit) floating-point element in a by the -// lower single-precision (32-bit) floating-point element in b, store the result -// in the lower element of dst, and copy the upper 3 packed elements from a to -// the upper elements of dst. -// Warning: ARMv7-A does not produce the same result compared to Intel and not -// IEEE-compliant. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_ss -FORCE_INLINE __m128 _mm_div_ss(__m128 a, __m128 b) -{ - float32_t value = - vgetq_lane_f32(vreinterpretq_f32_m128(_mm_div_ps(a, b)), 0); - return vreinterpretq_m128_f32( - vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); -} - -// Extract a 16-bit integer from a, selected with imm8, and store the result in -// the lower element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_pi16 -#define _mm_extract_pi16(a, imm) \ - (int32_t) vget_lane_u16(vreinterpret_u16_m64(a), (imm)) - -// Free aligned memory that was allocated with _mm_malloc. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_free -#if !defined(SSE2NEON_ALLOC_DEFINED) -FORCE_INLINE void _mm_free(void *addr) -{ - free(addr); -} -#endif - -FORCE_INLINE uint64_t _sse2neon_get_fpcr(void) -{ - uint64_t value; -#if defined(_MSC_VER) - value = _ReadStatusReg(ARM64_FPCR); -#else - __asm__ __volatile__("mrs %0, FPCR" : "=r"(value)); /* read */ -#endif - return value; -} - -FORCE_INLINE void _sse2neon_set_fpcr(uint64_t value) -{ -#if defined(_MSC_VER) - _WriteStatusReg(ARM64_FPCR, value); -#else - __asm__ __volatile__("msr FPCR, %0" ::"r"(value)); /* write */ -#endif -} - -// Macro: Get the flush zero bits from the MXCSR control and status register. -// The flush zero may contain any of the following flags: _MM_FLUSH_ZERO_ON or -// _MM_FLUSH_ZERO_OFF -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_GET_FLUSH_ZERO_MODE -FORCE_INLINE unsigned int _sse2neon_mm_get_flush_zero_mode(void) -{ - union { - fpcr_bitfield field; -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t value; -#else - uint32_t value; -#endif - } r; - -#if defined(__aarch64__) || defined(_M_ARM64) - r.value = _sse2neon_get_fpcr(); -#else - __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ -#endif - - return r.field.bit24 ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF; -} - -// Macro: Get the rounding mode bits from the MXCSR control and status register. -// The rounding mode may contain any of the following flags: _MM_ROUND_NEAREST, -// _MM_ROUND_DOWN, _MM_ROUND_UP, _MM_ROUND_TOWARD_ZERO -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_GET_ROUNDING_MODE -FORCE_INLINE unsigned int _MM_GET_ROUNDING_MODE(void) -{ - union { - fpcr_bitfield field; -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t value; -#else - uint32_t value; -#endif - } r; - -#if defined(__aarch64__) || defined(_M_ARM64) - r.value = _sse2neon_get_fpcr(); -#else - __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ -#endif - - if (r.field.bit22) { - return r.field.bit23 ? _MM_ROUND_TOWARD_ZERO : _MM_ROUND_UP; - } else { - return r.field.bit23 ? _MM_ROUND_DOWN : _MM_ROUND_NEAREST; - } -} - -// Copy a to dst, and insert the 16-bit integer i into dst at the location -// specified by imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_pi16 -#define _mm_insert_pi16(a, b, imm) \ - vreinterpret_m64_s16(vset_lane_s16((b), vreinterpret_s16_m64(a), (imm))) - -// Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point -// elements) from memory into dst. mem_addr must be aligned on a 16-byte -// boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_ps -FORCE_INLINE __m128 _mm_load_ps(const float *p) -{ - return vreinterpretq_m128_f32(vld1q_f32(p)); -} - -// Load a single-precision (32-bit) floating-point element from memory into all -// elements of dst. -// -// dst[31:0] := MEM[mem_addr+31:mem_addr] -// dst[63:32] := MEM[mem_addr+31:mem_addr] -// dst[95:64] := MEM[mem_addr+31:mem_addr] -// dst[127:96] := MEM[mem_addr+31:mem_addr] -// -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_ps1 -#define _mm_load_ps1 _mm_load1_ps - -// Load a single-precision (32-bit) floating-point element from memory into the -// lower of dst, and zero the upper 3 elements. mem_addr does not need to be -// aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_ss -FORCE_INLINE __m128 _mm_load_ss(const float *p) -{ - return vreinterpretq_m128_f32(vsetq_lane_f32(*p, vdupq_n_f32(0), 0)); -} - -// Load a single-precision (32-bit) floating-point element from memory into all -// elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load1_ps -FORCE_INLINE __m128 _mm_load1_ps(const float *p) -{ - return vreinterpretq_m128_f32(vld1q_dup_f32(p)); -} - -// Load 2 single-precision (32-bit) floating-point elements from memory into the -// upper 2 elements of dst, and copy the lower 2 elements from a to dst. -// mem_addr does not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadh_pi -FORCE_INLINE __m128 _mm_loadh_pi(__m128 a, __m64 const *p) -{ - return vreinterpretq_m128_f32( - vcombine_f32(vget_low_f32(a), vld1_f32((const float32_t *) p))); -} - -// Load 2 single-precision (32-bit) floating-point elements from memory into the -// lower 2 elements of dst, and copy the upper 2 elements from a to dst. -// mem_addr does not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadl_pi -FORCE_INLINE __m128 _mm_loadl_pi(__m128 a, __m64 const *p) -{ - return vreinterpretq_m128_f32( - vcombine_f32(vld1_f32((const float32_t *) p), vget_high_f32(a))); -} - -// Load 4 single-precision (32-bit) floating-point elements from memory into dst -// in reverse order. mem_addr must be aligned on a 16-byte boundary or a -// general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadr_ps -FORCE_INLINE __m128 _mm_loadr_ps(const float *p) -{ - float32x4_t v = vrev64q_f32(vld1q_f32(p)); - return vreinterpretq_m128_f32(vextq_f32(v, v, 2)); -} - -// Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point -// elements) from memory into dst. mem_addr does not need to be aligned on any -// particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_ps -FORCE_INLINE __m128 _mm_loadu_ps(const float *p) -{ - // for neon, alignment doesn't matter, so _mm_load_ps and _mm_loadu_ps are - // equivalent for neon - return vreinterpretq_m128_f32(vld1q_f32(p)); -} - -// Load unaligned 16-bit integer from memory into the first element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si16 -FORCE_INLINE __m128i _mm_loadu_si16(const void *p) -{ - return vreinterpretq_m128i_s16( - vsetq_lane_s16(*(const int16_t *) p, vdupq_n_s16(0), 0)); -} - -// Load unaligned 64-bit integer from memory into the first element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si64 -FORCE_INLINE __m128i _mm_loadu_si64(const void *p) -{ - return vreinterpretq_m128i_s64( - vcombine_s64(vld1_s64((const int64_t *) p), vdup_n_s64(0))); -} - -// Allocate size bytes of memory, aligned to the alignment specified in align, -// and return a pointer to the allocated memory. _mm_free should be used to free -// memory that is allocated with _mm_malloc. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_malloc -#if !defined(SSE2NEON_ALLOC_DEFINED) -FORCE_INLINE void *_mm_malloc(size_t size, size_t align) -{ - void *ptr; - if (align == 1) - return malloc(size); - if (align == 2 || (sizeof(void *) == 8 && align == 4)) - align = sizeof(void *); - if (!posix_memalign(&ptr, align, size)) - return ptr; - return NULL; -} -#endif - -// Conditionally store 8-bit integer elements from a into memory using mask -// (elements are not stored when the highest bit is not set in the corresponding -// element) and a non-temporal memory hint. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maskmove_si64 -FORCE_INLINE void _mm_maskmove_si64(__m64 a, __m64 mask, char *mem_addr) -{ - int8x8_t shr_mask = vshr_n_s8(vreinterpret_s8_m64(mask), 7); - __m128 b = _mm_load_ps((const float *) mem_addr); - int8x8_t masked = - vbsl_s8(vreinterpret_u8_s8(shr_mask), vreinterpret_s8_m64(a), - vreinterpret_s8_u64(vget_low_u64(vreinterpretq_u64_m128(b)))); - vst1_s8((int8_t *) mem_addr, masked); -} - -// Conditionally store 8-bit integer elements from a into memory using mask -// (elements are not stored when the highest bit is not set in the corresponding -// element) and a non-temporal memory hint. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_maskmovq -#define _m_maskmovq(a, mask, mem_addr) _mm_maskmove_si64(a, mask, mem_addr) - -// Compare packed signed 16-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_pi16 -FORCE_INLINE __m64 _mm_max_pi16(__m64 a, __m64 b) -{ - return vreinterpret_m64_s16( - vmax_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b, -// and store packed maximum values in dst. dst does not follow the IEEE Standard -// for Floating-Point Arithmetic (IEEE 754) maximum value when inputs are NaN or -// signed-zero values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_ps -FORCE_INLINE __m128 _mm_max_ps(__m128 a, __m128 b) -{ -#if SSE2NEON_PRECISE_MINMAX - float32x4_t _a = vreinterpretq_f32_m128(a); - float32x4_t _b = vreinterpretq_f32_m128(b); - return vreinterpretq_m128_f32(vbslq_f32(vcgtq_f32(_a, _b), _a, _b)); -#else - return vreinterpretq_m128_f32( - vmaxq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -#endif -} - -// Compare packed unsigned 8-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_pu8 -FORCE_INLINE __m64 _mm_max_pu8(__m64 a, __m64 b) -{ - return vreinterpret_m64_u8( - vmax_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b, store the maximum value in the lower element of dst, and copy the upper 3 -// packed elements from a to the upper element of dst. dst does not follow the -// IEEE Standard for Floating-Point Arithmetic (IEEE 754) maximum value when -// inputs are NaN or signed-zero values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_ss -FORCE_INLINE __m128 _mm_max_ss(__m128 a, __m128 b) -{ - float32_t value = vgetq_lane_f32(_mm_max_ps(a, b), 0); - return vreinterpretq_m128_f32( - vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); -} - -// Compare packed signed 16-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_pi16 -FORCE_INLINE __m64 _mm_min_pi16(__m64 a, __m64 b) -{ - return vreinterpret_m64_s16( - vmin_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); -} - -// Compare packed single-precision (32-bit) floating-point elements in a and b, -// and store packed minimum values in dst. dst does not follow the IEEE Standard -// for Floating-Point Arithmetic (IEEE 754) minimum value when inputs are NaN or -// signed-zero values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_ps -FORCE_INLINE __m128 _mm_min_ps(__m128 a, __m128 b) -{ -#if SSE2NEON_PRECISE_MINMAX - float32x4_t _a = vreinterpretq_f32_m128(a); - float32x4_t _b = vreinterpretq_f32_m128(b); - return vreinterpretq_m128_f32(vbslq_f32(vcltq_f32(_a, _b), _a, _b)); -#else - return vreinterpretq_m128_f32( - vminq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -#endif -} - -// Compare packed unsigned 8-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_pu8 -FORCE_INLINE __m64 _mm_min_pu8(__m64 a, __m64 b) -{ - return vreinterpret_m64_u8( - vmin_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); -} - -// Compare the lower single-precision (32-bit) floating-point elements in a and -// b, store the minimum value in the lower element of dst, and copy the upper 3 -// packed elements from a to the upper element of dst. dst does not follow the -// IEEE Standard for Floating-Point Arithmetic (IEEE 754) minimum value when -// inputs are NaN or signed-zero values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_ss -FORCE_INLINE __m128 _mm_min_ss(__m128 a, __m128 b) -{ - float32_t value = vgetq_lane_f32(_mm_min_ps(a, b), 0); - return vreinterpretq_m128_f32( - vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); -} - -// Move the lower single-precision (32-bit) floating-point element from b to the -// lower element of dst, and copy the upper 3 packed elements from a to the -// upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_move_ss -FORCE_INLINE __m128 _mm_move_ss(__m128 a, __m128 b) -{ - return vreinterpretq_m128_f32( - vsetq_lane_f32(vgetq_lane_f32(vreinterpretq_f32_m128(b), 0), - vreinterpretq_f32_m128(a), 0)); -} - -// Move the upper 2 single-precision (32-bit) floating-point elements from b to -// the lower 2 elements of dst, and copy the upper 2 elements from a to the -// upper 2 elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movehl_ps -FORCE_INLINE __m128 _mm_movehl_ps(__m128 a, __m128 b) -{ -#if defined(aarch64__) - return vreinterpretq_m128_u64( - vzip2q_u64(vreinterpretq_u64_m128(b), vreinterpretq_u64_m128(a))); -#else - float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); - float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32(vcombine_f32(b32, a32)); -#endif -} - -// Move the lower 2 single-precision (32-bit) floating-point elements from b to -// the upper 2 elements of dst, and copy the lower 2 elements from a to the -// lower 2 elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movelh_ps -FORCE_INLINE __m128 _mm_movelh_ps(__m128 __A, __m128 __B) -{ - float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(__A)); - float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(__B)); - return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); -} - -// Create mask from the most significant bit of each 8-bit element in a, and -// store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_pi8 -FORCE_INLINE int _mm_movemask_pi8(__m64 a) -{ - uint8x8_t input = vreinterpret_u8_m64(a); -#if defined(__aarch64__) || defined(_M_ARM64) - static const int8_t shift[8] = {0, 1, 2, 3, 4, 5, 6, 7}; - uint8x8_t tmp = vshr_n_u8(input, 7); - return vaddv_u8(vshl_u8(tmp, vld1_s8(shift))); -#else - // Refer the implementation of `_mm_movemask_epi8` - uint16x4_t high_bits = vreinterpret_u16_u8(vshr_n_u8(input, 7)); - uint32x2_t paired16 = - vreinterpret_u32_u16(vsra_n_u16(high_bits, high_bits, 7)); - uint8x8_t paired32 = - vreinterpret_u8_u32(vsra_n_u32(paired16, paired16, 14)); - return vget_lane_u8(paired32, 0) | ((int) vget_lane_u8(paired32, 4) << 4); -#endif -} - -// Set each bit of mask dst based on the most significant bit of the -// corresponding packed single-precision (32-bit) floating-point element in a. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_ps -FORCE_INLINE int _mm_movemask_ps(__m128 a) -{ - uint32x4_t input = vreinterpretq_u32_m128(a); -#if defined(__aarch64__) || defined(_M_ARM64) - static const int32_t shift[4] = {0, 1, 2, 3}; - uint32x4_t tmp = vshrq_n_u32(input, 31); - return vaddvq_u32(vshlq_u32(tmp, vld1q_s32(shift))); -#else - // Uses the exact same method as _mm_movemask_epi8, see that for details. - // Shift out everything but the sign bits with a 32-bit unsigned shift - // right. - uint64x2_t high_bits = vreinterpretq_u64_u32(vshrq_n_u32(input, 31)); - // Merge the two pairs together with a 64-bit unsigned shift right + add. - uint8x16_t paired = - vreinterpretq_u8_u64(vsraq_n_u64(high_bits, high_bits, 31)); - // Extract the result. - return vgetq_lane_u8(paired, 0) | (vgetq_lane_u8(paired, 8) << 2); -#endif -} - -// Multiply packed single-precision (32-bit) floating-point elements in a and b, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_ps -FORCE_INLINE __m128 _mm_mul_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_f32( - vmulq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Multiply the lower single-precision (32-bit) floating-point element in a and -// b, store the result in the lower element of dst, and copy the upper 3 packed -// elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_ss -FORCE_INLINE __m128 _mm_mul_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_mul_ps(a, b)); -} - -// Multiply the packed unsigned 16-bit integers in a and b, producing -// intermediate 32-bit integers, and store the high 16 bits of the intermediate -// integers in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhi_pu16 -FORCE_INLINE __m64 _mm_mulhi_pu16(__m64 a, __m64 b) -{ - return vreinterpret_m64_u16(vshrn_n_u32( - vmull_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b)), 16)); -} - -// Compute the bitwise OR of packed single-precision (32-bit) floating-point -// elements in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_or_ps -FORCE_INLINE __m128 _mm_or_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_s32( - vorrq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); -} - -// Average packed unsigned 8-bit integers in a and b, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pavgb -#define _m_pavgb(a, b) _mm_avg_pu8(a, b) - -// Average packed unsigned 16-bit integers in a and b, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pavgw -#define _m_pavgw(a, b) _mm_avg_pu16(a, b) - -// Extract a 16-bit integer from a, selected with imm8, and store the result in -// the lower element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pextrw -#define _m_pextrw(a, imm) _mm_extract_pi16(a, imm) - -// Copy a to dst, and insert the 16-bit integer i into dst at the location -// specified by imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=m_pinsrw -#define _m_pinsrw(a, i, imm) _mm_insert_pi16(a, i, imm) - -// Compare packed signed 16-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmaxsw -#define _m_pmaxsw(a, b) _mm_max_pi16(a, b) - -// Compare packed unsigned 8-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmaxub -#define _m_pmaxub(a, b) _mm_max_pu8(a, b) - -// Compare packed signed 16-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pminsw -#define _m_pminsw(a, b) _mm_min_pi16(a, b) - -// Compare packed unsigned 8-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pminub -#define _m_pminub(a, b) _mm_min_pu8(a, b) - -// Create mask from the most significant bit of each 8-bit element in a, and -// store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmovmskb -#define _m_pmovmskb(a) _mm_movemask_pi8(a) - -// Multiply the packed unsigned 16-bit integers in a and b, producing -// intermediate 32-bit integers, and store the high 16 bits of the intermediate -// integers in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pmulhuw -#define _m_pmulhuw(a, b) _mm_mulhi_pu16(a, b) - -// Fetch the line of data from memory that contains address p to a location in -// the cache hierarchy specified by the locality hint i. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_prefetch -FORCE_INLINE void _mm_prefetch(char const *p, int i) -{ - (void) i; -#if defined(_MSC_VER) - switch (i) { - case _MM_HINT_NTA: - __prefetch2(p, 1); - break; - case _MM_HINT_T0: - __prefetch2(p, 0); - break; - case _MM_HINT_T1: - __prefetch2(p, 2); - break; - case _MM_HINT_T2: - __prefetch2(p, 4); - break; - } -#else - switch (i) { - case _MM_HINT_NTA: - __builtin_prefetch(p, 0, 0); - break; - case _MM_HINT_T0: - __builtin_prefetch(p, 0, 3); - break; - case _MM_HINT_T1: - __builtin_prefetch(p, 0, 2); - break; - case _MM_HINT_T2: - __builtin_prefetch(p, 0, 1); - break; - } -#endif -} - -// Compute the absolute differences of packed unsigned 8-bit integers in a and -// b, then horizontally sum each consecutive 8 differences to produce four -// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low -// 16 bits of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=m_psadbw -#define _m_psadbw(a, b) _mm_sad_pu8(a, b) - -// Shuffle 16-bit integers in a using the control in imm8, and store the results -// in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_m_pshufw -#define _m_pshufw(a, imm) _mm_shuffle_pi16(a, imm) - -// Compute the approximate reciprocal of packed single-precision (32-bit) -// floating-point elements in a, and store the results in dst. The maximum -// relative error for this approximation is less than 1.5*2^-12. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rcp_ps -FORCE_INLINE __m128 _mm_rcp_ps(__m128 in) -{ - float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(in)); - recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(in))); - return vreinterpretq_m128_f32(recip); -} - -// Compute the approximate reciprocal of the lower single-precision (32-bit) -// floating-point element in a, store the result in the lower element of dst, -// and copy the upper 3 packed elements from a to the upper elements of dst. The -// maximum relative error for this approximation is less than 1.5*2^-12. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rcp_ss -FORCE_INLINE __m128 _mm_rcp_ss(__m128 a) -{ - return _mm_move_ss(a, _mm_rcp_ps(a)); -} - -// Compute the approximate reciprocal square root of packed single-precision -// (32-bit) floating-point elements in a, and store the results in dst. The -// maximum relative error for this approximation is less than 1.5*2^-12. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rsqrt_ps -FORCE_INLINE __m128 _mm_rsqrt_ps(__m128 in) -{ - float32x4_t out = vrsqrteq_f32(vreinterpretq_f32_m128(in)); - - // Generate masks for detecting whether input has any 0.0f/-0.0f - // (which becomes positive/negative infinity by IEEE-754 arithmetic rules). - const uint32x4_t pos_inf = vdupq_n_u32(0x7F800000); - const uint32x4_t neg_inf = vdupq_n_u32(0xFF800000); - const uint32x4_t has_pos_zero = - vceqq_u32(pos_inf, vreinterpretq_u32_f32(out)); - const uint32x4_t has_neg_zero = - vceqq_u32(neg_inf, vreinterpretq_u32_f32(out)); - - out = vmulq_f32( - out, vrsqrtsq_f32(vmulq_f32(vreinterpretq_f32_m128(in), out), out)); - - // Set output vector element to infinity/negative-infinity if - // the corresponding input vector element is 0.0f/-0.0f. - out = vbslq_f32(has_pos_zero, (float32x4_t) pos_inf, out); - out = vbslq_f32(has_neg_zero, (float32x4_t) neg_inf, out); - - return vreinterpretq_m128_f32(out); -} - -// Compute the approximate reciprocal square root of the lower single-precision -// (32-bit) floating-point element in a, store the result in the lower element -// of dst, and copy the upper 3 packed elements from a to the upper elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_rsqrt_ss -FORCE_INLINE __m128 _mm_rsqrt_ss(__m128 in) -{ - return vsetq_lane_f32(vgetq_lane_f32(_mm_rsqrt_ps(in), 0), in, 0); -} - -// Compute the absolute differences of packed unsigned 8-bit integers in a and -// b, then horizontally sum each consecutive 8 differences to produce four -// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low -// 16 bits of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_pu8 -FORCE_INLINE __m64 _mm_sad_pu8(__m64 a, __m64 b) -{ - uint64x1_t t = vpaddl_u32(vpaddl_u16( - vpaddl_u8(vabd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))))); - return vreinterpret_m64_u16( - vset_lane_u16((int) vget_lane_u64(t, 0), vdup_n_u16(0), 0)); -} - -// Macro: Set the flush zero bits of the MXCSR control and status register to -// the value in unsigned 32-bit integer a. The flush zero may contain any of the -// following flags: _MM_FLUSH_ZERO_ON or _MM_FLUSH_ZERO_OFF -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_SET_FLUSH_ZERO_MODE -FORCE_INLINE void _sse2neon_mm_set_flush_zero_mode(unsigned int flag) -{ - // AArch32 Advanced SIMD arithmetic always uses the Flush-to-zero setting, - // regardless of the value of the FZ bit. - union { - fpcr_bitfield field; -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t value; -#else - uint32_t value; -#endif - } r; - -#if defined(__aarch64__) || defined(_M_ARM64) - r.value = _sse2neon_get_fpcr(); -#else - __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ -#endif - - r.field.bit24 = (flag & _MM_FLUSH_ZERO_MASK) == _MM_FLUSH_ZERO_ON; - -#if defined(__aarch64__) || defined(_M_ARM64) - _sse2neon_set_fpcr(r.value); -#else - __asm__ __volatile__("vmsr FPSCR, %0" ::"r"(r)); /* write */ -#endif -} - -// Set packed single-precision (32-bit) floating-point elements in dst with the -// supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_ps -FORCE_INLINE __m128 _mm_set_ps(float w, float z, float y, float x) -{ - float ALIGN_STRUCT(16) data[4] = {x, y, z, w}; - return vreinterpretq_m128_f32(vld1q_f32(data)); -} - -// Broadcast single-precision (32-bit) floating-point value a to all elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_ps1 -FORCE_INLINE __m128 _mm_set_ps1(float _w) -{ - return vreinterpretq_m128_f32(vdupq_n_f32(_w)); -} - -// Macro: Set the rounding mode bits of the MXCSR control and status register to -// the value in unsigned 32-bit integer a. The rounding mode may contain any of -// the following flags: _MM_ROUND_NEAREST, _MM_ROUND_DOWN, _MM_ROUND_UP, -// _MM_ROUND_TOWARD_ZERO -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_MM_SET_ROUNDING_MODE -FORCE_INLINE void _MM_SET_ROUNDING_MODE(int rounding) -{ - union { - fpcr_bitfield field; -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t value; -#else - uint32_t value; -#endif - } r; - -#if defined(__aarch64__) || defined(_M_ARM64) - r.value = _sse2neon_get_fpcr(); -#else - __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ -#endif - - switch (rounding) { - case _MM_ROUND_TOWARD_ZERO: - r.field.bit22 = 1; - r.field.bit23 = 1; - break; - case _MM_ROUND_DOWN: - r.field.bit22 = 0; - r.field.bit23 = 1; - break; - case _MM_ROUND_UP: - r.field.bit22 = 1; - r.field.bit23 = 0; - break; - default: //_MM_ROUND_NEAREST - r.field.bit22 = 0; - r.field.bit23 = 0; - } - -#if defined(__aarch64__) || defined(_M_ARM64) - _sse2neon_set_fpcr(r.value); -#else - __asm__ __volatile__("vmsr FPSCR, %0" ::"r"(r)); /* write */ -#endif -} - -// Copy single-precision (32-bit) floating-point element a to the lower element -// of dst, and zero the upper 3 elements. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_ss -FORCE_INLINE __m128 _mm_set_ss(float a) -{ - return vreinterpretq_m128_f32(vsetq_lane_f32(a, vdupq_n_f32(0), 0)); -} - -// Broadcast single-precision (32-bit) floating-point value a to all elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_ps -FORCE_INLINE __m128 _mm_set1_ps(float _w) -{ - return vreinterpretq_m128_f32(vdupq_n_f32(_w)); -} - -// Set the MXCSR control and status register with the value in unsigned 32-bit -// integer a. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setcsr -// FIXME: _mm_setcsr() implementation supports changing the rounding mode only. -FORCE_INLINE void _mm_setcsr(unsigned int a) -{ - _MM_SET_ROUNDING_MODE(a); -} - -// Get the unsigned 32-bit value of the MXCSR control and status register. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_getcsr -// FIXME: _mm_getcsr() implementation supports reading the rounding mode only. -FORCE_INLINE unsigned int _mm_getcsr(void) -{ - return _MM_GET_ROUNDING_MODE(); -} - -// Set packed single-precision (32-bit) floating-point elements in dst with the -// supplied values in reverse order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_ps -FORCE_INLINE __m128 _mm_setr_ps(float w, float z, float y, float x) -{ - float ALIGN_STRUCT(16) data[4] = {w, z, y, x}; - return vreinterpretq_m128_f32(vld1q_f32(data)); -} - -// Return vector of type __m128 with all elements set to zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setzero_ps -FORCE_INLINE __m128 _mm_setzero_ps(void) -{ - return vreinterpretq_m128_f32(vdupq_n_f32(0)); -} - -// Shuffle 16-bit integers in a using the control in imm8, and store the results -// in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_pi16 -#ifdef _sse2neon_shuffle -#define _mm_shuffle_pi16(a, imm) \ - vreinterpret_m64_s16(vshuffle_s16( \ - vreinterpret_s16_m64(a), vreinterpret_s16_m64(a), (imm & 0x3), \ - ((imm >> 2) & 0x3), ((imm >> 4) & 0x3), ((imm >> 6) & 0x3))) -#else -#define _mm_shuffle_pi16(a, imm) \ - _sse2neon_define1( \ - __m64, a, int16x4_t ret; \ - ret = vmov_n_s16( \ - vget_lane_s16(vreinterpret_s16_m64(_a), (imm) & (0x3))); \ - ret = vset_lane_s16( \ - vget_lane_s16(vreinterpret_s16_m64(_a), ((imm) >> 2) & 0x3), ret, \ - 1); \ - ret = vset_lane_s16( \ - vget_lane_s16(vreinterpret_s16_m64(_a), ((imm) >> 4) & 0x3), ret, \ - 2); \ - ret = vset_lane_s16( \ - vget_lane_s16(vreinterpret_s16_m64(_a), ((imm) >> 6) & 0x3), ret, \ - 3); \ - _sse2neon_return(vreinterpret_m64_s16(ret));) -#endif - -// Perform a serializing operation on all store-to-memory instructions that were -// issued prior to this instruction. Guarantees that every store instruction -// that precedes, in program order, is globally visible before any store -// instruction which follows the fence in program order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sfence -FORCE_INLINE void _mm_sfence(void) -{ - _sse2neon_smp_mb(); -} - -// Perform a serializing operation on all load-from-memory and store-to-memory -// instructions that were issued prior to this instruction. Guarantees that -// every memory access that precedes, in program order, the memory fence -// instruction is globally visible before any memory instruction which follows -// the fence in program order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mfence -FORCE_INLINE void _mm_mfence(void) -{ - _sse2neon_smp_mb(); -} - -// Perform a serializing operation on all load-from-memory instructions that -// were issued prior to this instruction. Guarantees that every load instruction -// that precedes, in program order, is globally visible before any load -// instruction which follows the fence in program order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_lfence -FORCE_INLINE void _mm_lfence(void) -{ - _sse2neon_smp_mb(); -} - -// FORCE_INLINE __m128 _mm_shuffle_ps(__m128 a, __m128 b, __constrange(0,255) -// int imm) -#ifdef _sse2neon_shuffle -#define _mm_shuffle_ps(a, b, imm) \ - __extension__({ \ - float32x4_t _input1 = vreinterpretq_f32_m128(a); \ - float32x4_t _input2 = vreinterpretq_f32_m128(b); \ - float32x4_t _shuf = \ - vshuffleq_s32(_input1, _input2, (imm) & (0x3), ((imm) >> 2) & 0x3, \ - (((imm) >> 4) & 0x3) + 4, (((imm) >> 6) & 0x3) + 4); \ - vreinterpretq_m128_f32(_shuf); \ - }) -#else // generic -#define _mm_shuffle_ps(a, b, imm) \ - _sse2neon_define2( \ - __m128, a, b, __m128 ret; switch (imm) { \ - case _MM_SHUFFLE(1, 0, 3, 2): \ - ret = _mm_shuffle_ps_1032(_a, _b); \ - break; \ - case _MM_SHUFFLE(2, 3, 0, 1): \ - ret = _mm_shuffle_ps_2301(_a, _b); \ - break; \ - case _MM_SHUFFLE(0, 3, 2, 1): \ - ret = _mm_shuffle_ps_0321(_a, _b); \ - break; \ - case _MM_SHUFFLE(2, 1, 0, 3): \ - ret = _mm_shuffle_ps_2103(_a, _b); \ - break; \ - case _MM_SHUFFLE(1, 0, 1, 0): \ - ret = _mm_movelh_ps(_a, _b); \ - break; \ - case _MM_SHUFFLE(1, 0, 0, 1): \ - ret = _mm_shuffle_ps_1001(_a, _b); \ - break; \ - case _MM_SHUFFLE(0, 1, 0, 1): \ - ret = _mm_shuffle_ps_0101(_a, _b); \ - break; \ - case _MM_SHUFFLE(3, 2, 1, 0): \ - ret = _mm_shuffle_ps_3210(_a, _b); \ - break; \ - case _MM_SHUFFLE(0, 0, 1, 1): \ - ret = _mm_shuffle_ps_0011(_a, _b); \ - break; \ - case _MM_SHUFFLE(0, 0, 2, 2): \ - ret = _mm_shuffle_ps_0022(_a, _b); \ - break; \ - case _MM_SHUFFLE(2, 2, 0, 0): \ - ret = _mm_shuffle_ps_2200(_a, _b); \ - break; \ - case _MM_SHUFFLE(3, 2, 0, 2): \ - ret = _mm_shuffle_ps_3202(_a, _b); \ - break; \ - case _MM_SHUFFLE(3, 2, 3, 2): \ - ret = _mm_movehl_ps(_b, _a); \ - break; \ - case _MM_SHUFFLE(1, 1, 3, 3): \ - ret = _mm_shuffle_ps_1133(_a, _b); \ - break; \ - case _MM_SHUFFLE(2, 0, 1, 0): \ - ret = _mm_shuffle_ps_2010(_a, _b); \ - break; \ - case _MM_SHUFFLE(2, 0, 0, 1): \ - ret = _mm_shuffle_ps_2001(_a, _b); \ - break; \ - case _MM_SHUFFLE(2, 0, 3, 2): \ - ret = _mm_shuffle_ps_2032(_a, _b); \ - break; \ - default: \ - ret = _mm_shuffle_ps_default(_a, _b, (imm)); \ - break; \ - } _sse2neon_return(ret);) -#endif - -// Compute the square root of packed single-precision (32-bit) floating-point -// elements in a, and store the results in dst. -// Due to ARMv7-A NEON's lack of a precise square root intrinsic, we implement -// square root by multiplying input in with its reciprocal square root before -// using the Newton-Raphson method to approximate the results. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_ps -FORCE_INLINE __m128 _mm_sqrt_ps(__m128 in) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32(vsqrtq_f32(vreinterpretq_f32_m128(in))); -#else - float32x4_t recip = vrsqrteq_f32(vreinterpretq_f32_m128(in)); - - // Test for vrsqrteq_f32(0) -> positive infinity case. - // Change to zero, so that s * 1/sqrt(s) result is zero too. - const uint32x4_t pos_inf = vdupq_n_u32(0x7F800000); - const uint32x4_t div_by_zero = - vceqq_u32(pos_inf, vreinterpretq_u32_f32(recip)); - recip = vreinterpretq_f32_u32( - vandq_u32(vmvnq_u32(div_by_zero), vreinterpretq_u32_f32(recip))); - - recip = vmulq_f32( - vrsqrtsq_f32(vmulq_f32(recip, recip), vreinterpretq_f32_m128(in)), - recip); - // Additional Netwon-Raphson iteration for accuracy - recip = vmulq_f32( - vrsqrtsq_f32(vmulq_f32(recip, recip), vreinterpretq_f32_m128(in)), - recip); - - // sqrt(s) = s * 1/sqrt(s) - return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(in), recip)); -#endif -} - -// Compute the square root of the lower single-precision (32-bit) floating-point -// element in a, store the result in the lower element of dst, and copy the -// upper 3 packed elements from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_ss -FORCE_INLINE __m128 _mm_sqrt_ss(__m128 in) -{ - float32_t value = - vgetq_lane_f32(vreinterpretq_f32_m128(_mm_sqrt_ps(in)), 0); - return vreinterpretq_m128_f32( - vsetq_lane_f32(value, vreinterpretq_f32_m128(in), 0)); -} - -// Store 128-bits (composed of 4 packed single-precision (32-bit) floating-point -// elements) from a into memory. mem_addr must be aligned on a 16-byte boundary -// or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_ps -FORCE_INLINE void _mm_store_ps(float *p, __m128 a) -{ - vst1q_f32(p, vreinterpretq_f32_m128(a)); -} - -// Store the lower single-precision (32-bit) floating-point element from a into -// 4 contiguous elements in memory. mem_addr must be aligned on a 16-byte -// boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_ps1 -FORCE_INLINE void _mm_store_ps1(float *p, __m128 a) -{ - float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); - vst1q_f32(p, vdupq_n_f32(a0)); -} - -// Store the lower single-precision (32-bit) floating-point element from a into -// memory. mem_addr does not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_ss -FORCE_INLINE void _mm_store_ss(float *p, __m128 a) -{ - vst1q_lane_f32(p, vreinterpretq_f32_m128(a), 0); -} - -// Store the lower single-precision (32-bit) floating-point element from a into -// 4 contiguous elements in memory. mem_addr must be aligned on a 16-byte -// boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store1_ps -#define _mm_store1_ps _mm_store_ps1 - -// Store the upper 2 single-precision (32-bit) floating-point elements from a -// into memory. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeh_pi -FORCE_INLINE void _mm_storeh_pi(__m64 *p, __m128 a) -{ - *p = vreinterpret_m64_f32(vget_high_f32(a)); -} - -// Store the lower 2 single-precision (32-bit) floating-point elements from a -// into memory. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storel_pi -FORCE_INLINE void _mm_storel_pi(__m64 *p, __m128 a) -{ - *p = vreinterpret_m64_f32(vget_low_f32(a)); -} - -// Store 4 single-precision (32-bit) floating-point elements from a into memory -// in reverse order. mem_addr must be aligned on a 16-byte boundary or a -// general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storer_ps -FORCE_INLINE void _mm_storer_ps(float *p, __m128 a) -{ - float32x4_t tmp = vrev64q_f32(vreinterpretq_f32_m128(a)); - float32x4_t rev = vextq_f32(tmp, tmp, 2); - vst1q_f32(p, rev); -} - -// Store 128-bits (composed of 4 packed single-precision (32-bit) floating-point -// elements) from a into memory. mem_addr does not need to be aligned on any -// particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_ps -FORCE_INLINE void _mm_storeu_ps(float *p, __m128 a) -{ - vst1q_f32(p, vreinterpretq_f32_m128(a)); -} - -// Stores 16-bits of integer data a at the address p. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si16 -FORCE_INLINE void _mm_storeu_si16(void *p, __m128i a) -{ - vst1q_lane_s16((int16_t *) p, vreinterpretq_s16_m128i(a), 0); -} - -// Stores 64-bits of integer data a at the address p. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si64 -FORCE_INLINE void _mm_storeu_si64(void *p, __m128i a) -{ - vst1q_lane_s64((int64_t *) p, vreinterpretq_s64_m128i(a), 0); -} - -// Store 64-bits of integer data from a into memory using a non-temporal memory -// hint. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_pi -FORCE_INLINE void _mm_stream_pi(__m64 *p, __m64 a) -{ - vst1_s64((int64_t *) p, vreinterpret_s64_m64(a)); -} - -// Store 128-bits (composed of 4 packed single-precision (32-bit) floating- -// point elements) from a into memory using a non-temporal memory hint. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_ps -FORCE_INLINE void _mm_stream_ps(float *p, __m128 a) -{ -#if __has_builtin(__builtin_nontemporal_store) - __builtin_nontemporal_store(a, (float32x4_t *) p); -#else - vst1q_f32(p, vreinterpretq_f32_m128(a)); -#endif -} - -// Subtract packed single-precision (32-bit) floating-point elements in b from -// packed single-precision (32-bit) floating-point elements in a, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_ps -FORCE_INLINE __m128 _mm_sub_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_f32( - vsubq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -} - -// Subtract the lower single-precision (32-bit) floating-point element in b from -// the lower single-precision (32-bit) floating-point element in a, store the -// result in the lower element of dst, and copy the upper 3 packed elements from -// a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_ss -FORCE_INLINE __m128 _mm_sub_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_sub_ps(a, b)); -} - -// Macro: Transpose the 4x4 matrix formed by the 4 rows of single-precision -// (32-bit) floating-point elements in row0, row1, row2, and row3, and store the -// transposed matrix in these vectors (row0 now contains column 0, etc.). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=MM_TRANSPOSE4_PS -#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3) \ - do { \ - float32x4x2_t ROW01 = vtrnq_f32(row0, row1); \ - float32x4x2_t ROW23 = vtrnq_f32(row2, row3); \ - row0 = vcombine_f32(vget_low_f32(ROW01.val[0]), \ - vget_low_f32(ROW23.val[0])); \ - row1 = vcombine_f32(vget_low_f32(ROW01.val[1]), \ - vget_low_f32(ROW23.val[1])); \ - row2 = vcombine_f32(vget_high_f32(ROW01.val[0]), \ - vget_high_f32(ROW23.val[0])); \ - row3 = vcombine_f32(vget_high_f32(ROW01.val[1]), \ - vget_high_f32(ROW23.val[1])); \ - } while (0) - -// according to the documentation, these intrinsics behave the same as the -// non-'u' versions. We'll just alias them here. -#define _mm_ucomieq_ss _mm_comieq_ss -#define _mm_ucomige_ss _mm_comige_ss -#define _mm_ucomigt_ss _mm_comigt_ss -#define _mm_ucomile_ss _mm_comile_ss -#define _mm_ucomilt_ss _mm_comilt_ss -#define _mm_ucomineq_ss _mm_comineq_ss - -// Return vector of type __m128i with undefined elements. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_undefined_si128 -FORCE_INLINE __m128i _mm_undefined_si128(void) -{ -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif - __m128i a; -#if defined(_MSC_VER) - a = _mm_setzero_si128(); -#endif - return a; -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif -} - -// Return vector of type __m128 with undefined elements. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_undefined_ps -FORCE_INLINE __m128 _mm_undefined_ps(void) -{ -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif - __m128 a; -#if defined(_MSC_VER) - a = _mm_setzero_ps(); -#endif - return a; -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif -} - -// Unpack and interleave single-precision (32-bit) floating-point elements from -// the high half a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_ps -FORCE_INLINE __m128 _mm_unpackhi_ps(__m128 a, __m128 b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vzip2q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -#else - float32x2_t a1 = vget_high_f32(vreinterpretq_f32_m128(a)); - float32x2_t b1 = vget_high_f32(vreinterpretq_f32_m128(b)); - float32x2x2_t result = vzip_f32(a1, b1); - return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave single-precision (32-bit) floating-point elements from -// the low half of a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_ps -FORCE_INLINE __m128 _mm_unpacklo_ps(__m128 a, __m128 b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vzip1q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -#else - float32x2_t a1 = vget_low_f32(vreinterpretq_f32_m128(a)); - float32x2_t b1 = vget_low_f32(vreinterpretq_f32_m128(b)); - float32x2x2_t result = vzip_f32(a1, b1); - return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); -#endif -} - -// Compute the bitwise XOR of packed single-precision (32-bit) floating-point -// elements in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_xor_ps -FORCE_INLINE __m128 _mm_xor_ps(__m128 a, __m128 b) -{ - return vreinterpretq_m128_s32( - veorq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); -} - -/* SSE2 */ - -// Add packed 16-bit integers in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi16 -FORCE_INLINE __m128i _mm_add_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Add packed 32-bit integers in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi32 -FORCE_INLINE __m128i _mm_add_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vaddq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Add packed 64-bit integers in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi64 -FORCE_INLINE __m128i _mm_add_epi64(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s64( - vaddq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); -} - -// Add packed 8-bit integers in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_epi8 -FORCE_INLINE __m128i _mm_add_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Add packed double-precision (64-bit) floating-point elements in a and b, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_pd -FORCE_INLINE __m128d _mm_add_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2]; - c[0] = da[0] + db[0]; - c[1] = da[1] + db[1]; - return vld1q_f32((float32_t *) c); -#endif -} - -// Add the lower double-precision (64-bit) floating-point element in a and b, -// store the result in the lower element of dst, and copy the upper element from -// a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_sd -FORCE_INLINE __m128d _mm_add_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_add_pd(a, b)); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2]; - c[0] = da[0] + db[0]; - c[1] = da[1]; - return vld1q_f32((float32_t *) c); -#endif -} - -// Add 64-bit integers a and b, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_si64 -FORCE_INLINE __m64 _mm_add_si64(__m64 a, __m64 b) -{ - return vreinterpret_m64_s64( - vadd_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); -} - -// Add packed signed 16-bit integers in a and b using saturation, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epi16 -FORCE_INLINE __m128i _mm_adds_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vqaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Add packed signed 8-bit integers in a and b using saturation, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epi8 -FORCE_INLINE __m128i _mm_adds_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vqaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Add packed unsigned 16-bit integers in a and b using saturation, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epu16 -FORCE_INLINE __m128i _mm_adds_epu16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vqaddq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); -} - -// Add packed unsigned 8-bit integers in a and b using saturation, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_adds_epu8 -FORCE_INLINE __m128i _mm_adds_epu8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vqaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); -} - -// Compute the bitwise AND of packed double-precision (64-bit) floating-point -// elements in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_and_pd -FORCE_INLINE __m128d _mm_and_pd(__m128d a, __m128d b) -{ - return vreinterpretq_m128d_s64( - vandq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); -} - -// Compute the bitwise AND of 128 bits (representing integer data) in a and b, -// and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_and_si128 -FORCE_INLINE __m128i _mm_and_si128(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vandq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Compute the bitwise NOT of packed double-precision (64-bit) floating-point -// elements in a and then AND with b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_andnot_pd -FORCE_INLINE __m128d _mm_andnot_pd(__m128d a, __m128d b) -{ - // *NOTE* argument swap - return vreinterpretq_m128d_s64( - vbicq_s64(vreinterpretq_s64_m128d(b), vreinterpretq_s64_m128d(a))); -} - -// Compute the bitwise NOT of 128 bits (representing integer data) in a and then -// AND with b, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_andnot_si128 -FORCE_INLINE __m128i _mm_andnot_si128(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vbicq_s32(vreinterpretq_s32_m128i(b), - vreinterpretq_s32_m128i(a))); // *NOTE* argument swap -} - -// Average packed unsigned 16-bit integers in a and b, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_epu16 -FORCE_INLINE __m128i _mm_avg_epu16(__m128i a, __m128i b) -{ - return (__m128i) vrhaddq_u16(vreinterpretq_u16_m128i(a), - vreinterpretq_u16_m128i(b)); -} - -// Average packed unsigned 8-bit integers in a and b, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_avg_epu8 -FORCE_INLINE __m128i _mm_avg_epu8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vrhaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); -} - -// Shift a left by imm8 bytes while shifting in zeros, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_bslli_si128 -#define _mm_bslli_si128(a, imm) _mm_slli_si128(a, imm) - -// Shift a right by imm8 bytes while shifting in zeros, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_bsrli_si128 -#define _mm_bsrli_si128(a, imm) _mm_srli_si128(a, imm) - -// Cast vector of type __m128d to type __m128. This intrinsic is only used for -// compilation and does not generate any instructions, thus it has zero latency. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castpd_ps -FORCE_INLINE __m128 _mm_castpd_ps(__m128d a) -{ - return vreinterpretq_m128_s64(vreinterpretq_s64_m128d(a)); -} - -// Cast vector of type __m128d to type __m128i. This intrinsic is only used for -// compilation and does not generate any instructions, thus it has zero latency. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castpd_si128 -FORCE_INLINE __m128i _mm_castpd_si128(__m128d a) -{ - return vreinterpretq_m128i_s64(vreinterpretq_s64_m128d(a)); -} - -// Cast vector of type __m128 to type __m128d. This intrinsic is only used for -// compilation and does not generate any instructions, thus it has zero latency. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castps_pd -FORCE_INLINE __m128d _mm_castps_pd(__m128 a) -{ - return vreinterpretq_m128d_s32(vreinterpretq_s32_m128(a)); -} - -// Cast vector of type __m128 to type __m128i. This intrinsic is only used for -// compilation and does not generate any instructions, thus it has zero latency. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castps_si128 -FORCE_INLINE __m128i _mm_castps_si128(__m128 a) -{ - return vreinterpretq_m128i_s32(vreinterpretq_s32_m128(a)); -} - -// Cast vector of type __m128i to type __m128d. This intrinsic is only used for -// compilation and does not generate any instructions, thus it has zero latency. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castsi128_pd -FORCE_INLINE __m128d _mm_castsi128_pd(__m128i a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vreinterpretq_f64_m128i(a)); -#else - return vreinterpretq_m128d_f32(vreinterpretq_f32_m128i(a)); -#endif -} - -// Cast vector of type __m128i to type __m128. This intrinsic is only used for -// compilation and does not generate any instructions, thus it has zero latency. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castsi128_ps -FORCE_INLINE __m128 _mm_castsi128_ps(__m128i a) -{ - return vreinterpretq_m128_s32(vreinterpretq_s32_m128i(a)); -} - -// Invalidate and flush the cache line that contains p from all levels of the -// cache hierarchy. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clflush -#if defined(__APPLE__) -#include -#endif -FORCE_INLINE void _mm_clflush(void const *p) -{ - (void) p; - - /* sys_icache_invalidate is supported since macOS 10.5. - * However, it does not work on non-jailbroken iOS devices, although the - * compilation is successful. - */ -#if defined(__APPLE__) - sys_icache_invalidate((void *) (uintptr_t) p, SSE2NEON_CACHELINE_SIZE); -#elif defined(__GNUC__) || defined(__clang__) - uintptr_t ptr = (uintptr_t) p; - __builtin___clear_cache((char *) ptr, - (char *) ptr + SSE2NEON_CACHELINE_SIZE); -#elif (_MSC_VER) && SSE2NEON_INCLUDE_WINDOWS_H - FlushInstructionCache(GetCurrentProcess(), p, SSE2NEON_CACHELINE_SIZE); -#endif -} - -// Compare packed 16-bit integers in a and b for equality, and store the results -// in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_epi16 -FORCE_INLINE __m128i _mm_cmpeq_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vceqq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Compare packed 32-bit integers in a and b for equality, and store the results -// in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_epi32 -FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u32( - vceqq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Compare packed 8-bit integers in a and b for equality, and store the results -// in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_epi8 -FORCE_INLINE __m128i _mm_cmpeq_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vceqq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for equality, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_pd -FORCE_INLINE __m128d _mm_cmpeq_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64( - vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) - uint32x4_t cmp = - vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); - uint32x4_t swapped = vrev64q_u32(cmp); - return vreinterpretq_m128d_u32(vandq_u32(cmp, swapped)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for equality, store the result in the lower element of dst, and copy the -// upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpeq_sd -FORCE_INLINE __m128d _mm_cmpeq_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_cmpeq_pd(a, b)); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for greater-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_pd -FORCE_INLINE __m128d _mm_cmpge_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64( - vcgeq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) >= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = (*(double *) &a1) >= (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for greater-than-or-equal, store the result in the lower element of dst, -// and copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpge_sd -FORCE_INLINE __m128d _mm_cmpge_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_cmpge_pd(a, b)); -#else - // expand "_mm_cmpge_pd()" to reduce unnecessary operations - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) >= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = a1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare packed signed 16-bit integers in a and b for greater-than, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_epi16 -FORCE_INLINE __m128i _mm_cmpgt_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vcgtq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Compare packed signed 32-bit integers in a and b for greater-than, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_epi32 -FORCE_INLINE __m128i _mm_cmpgt_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u32( - vcgtq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Compare packed signed 8-bit integers in a and b for greater-than, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_epi8 -FORCE_INLINE __m128i _mm_cmpgt_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vcgtq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for greater-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_pd -FORCE_INLINE __m128d _mm_cmpgt_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64( - vcgtq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) > (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = (*(double *) &a1) > (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for greater-than, store the result in the lower element of dst, and copy -// the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpgt_sd -FORCE_INLINE __m128d _mm_cmpgt_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_cmpgt_pd(a, b)); -#else - // expand "_mm_cmpge_pd()" to reduce unnecessary operations - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) > (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = a1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for less-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_pd -FORCE_INLINE __m128d _mm_cmple_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64( - vcleq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) <= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = (*(double *) &a1) <= (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for less-than-or-equal, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmple_sd -FORCE_INLINE __m128d _mm_cmple_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_cmple_pd(a, b)); -#else - // expand "_mm_cmpge_pd()" to reduce unnecessary operations - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) <= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = a1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare packed signed 16-bit integers in a and b for less-than, and store the -// results in dst. Note: This intrinsic emits the pcmpgtw instruction with the -// order of the operands switched. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_epi16 -FORCE_INLINE __m128i _mm_cmplt_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vcltq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Compare packed signed 32-bit integers in a and b for less-than, and store the -// results in dst. Note: This intrinsic emits the pcmpgtd instruction with the -// order of the operands switched. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_epi32 -FORCE_INLINE __m128i _mm_cmplt_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u32( - vcltq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Compare packed signed 8-bit integers in a and b for less-than, and store the -// results in dst. Note: This intrinsic emits the pcmpgtb instruction with the -// order of the operands switched. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_epi8 -FORCE_INLINE __m128i _mm_cmplt_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vcltq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for less-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_pd -FORCE_INLINE __m128d _mm_cmplt_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64( - vcltq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) < (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = (*(double *) &a1) < (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for less-than, store the result in the lower element of dst, and copy the -// upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmplt_sd -FORCE_INLINE __m128d _mm_cmplt_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_cmplt_pd(a, b)); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) < (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = a1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for not-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_pd -FORCE_INLINE __m128d _mm_cmpneq_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_s32(vmvnq_s32(vreinterpretq_s32_u64( - vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))))); -#else - // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) - uint32x4_t cmp = - vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); - uint32x4_t swapped = vrev64q_u32(cmp); - return vreinterpretq_m128d_u32(vmvnq_u32(vandq_u32(cmp, swapped))); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for not-equal, store the result in the lower element of dst, and copy the -// upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpneq_sd -FORCE_INLINE __m128d _mm_cmpneq_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_cmpneq_pd(a, b)); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for not-greater-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_pd -FORCE_INLINE __m128d _mm_cmpnge_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64(veorq_u64( - vcgeq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), - vdupq_n_u64(UINT64_MAX))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = - !((*(double *) &a0) >= (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = - !((*(double *) &a1) >= (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for not-greater-than-or-equal, store the result in the lower element of -// dst, and copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnge_sd -FORCE_INLINE __m128d _mm_cmpnge_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_cmpnge_pd(a, b)); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for not-greater-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_cmpngt_pd -FORCE_INLINE __m128d _mm_cmpngt_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64(veorq_u64( - vcgtq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), - vdupq_n_u64(UINT64_MAX))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = - !((*(double *) &a0) > (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = - !((*(double *) &a1) > (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for not-greater-than, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpngt_sd -FORCE_INLINE __m128d _mm_cmpngt_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_cmpngt_pd(a, b)); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for not-less-than-or-equal, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_pd -FORCE_INLINE __m128d _mm_cmpnle_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64(veorq_u64( - vcleq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), - vdupq_n_u64(UINT64_MAX))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = - !((*(double *) &a0) <= (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = - !((*(double *) &a1) <= (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for not-less-than-or-equal, store the result in the lower element of dst, -// and copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnle_sd -FORCE_INLINE __m128d _mm_cmpnle_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_cmpnle_pd(a, b)); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// for not-less-than, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_pd -FORCE_INLINE __m128d _mm_cmpnlt_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_u64(veorq_u64( - vcltq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), - vdupq_n_u64(UINT64_MAX))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = - !((*(double *) &a0) < (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); - d[1] = - !((*(double *) &a1) < (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b for not-less-than, store the result in the lower element of dst, and copy -// the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpnlt_sd -FORCE_INLINE __m128d _mm_cmpnlt_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_cmpnlt_pd(a, b)); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// to see if neither is NaN, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_pd -FORCE_INLINE __m128d _mm_cmpord_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - // Excluding NaNs, any two floating point numbers can be compared. - uint64x2_t not_nan_a = - vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(a)); - uint64x2_t not_nan_b = - vceqq_f64(vreinterpretq_f64_m128d(b), vreinterpretq_f64_m128d(b)); - return vreinterpretq_m128d_u64(vandq_u64(not_nan_a, not_nan_b)); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = ((*(double *) &a0) == (*(double *) &a0) && - (*(double *) &b0) == (*(double *) &b0)) - ? ~UINT64_C(0) - : UINT64_C(0); - d[1] = ((*(double *) &a1) == (*(double *) &a1) && - (*(double *) &b1) == (*(double *) &b1)) - ? ~UINT64_C(0) - : UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b to see if neither is NaN, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpord_sd -FORCE_INLINE __m128d _mm_cmpord_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_cmpord_pd(a, b)); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t d[2]; - d[0] = ((*(double *) &a0) == (*(double *) &a0) && - (*(double *) &b0) == (*(double *) &b0)) - ? ~UINT64_C(0) - : UINT64_C(0); - d[1] = a1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b -// to see if either is NaN, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_pd -FORCE_INLINE __m128d _mm_cmpunord_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - // Two NaNs are not equal in comparison operation. - uint64x2_t not_nan_a = - vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(a)); - uint64x2_t not_nan_b = - vceqq_f64(vreinterpretq_f64_m128d(b), vreinterpretq_f64_m128d(b)); - return vreinterpretq_m128d_s32( - vmvnq_s32(vreinterpretq_s32_u64(vandq_u64(not_nan_a, not_nan_b)))); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = ((*(double *) &a0) == (*(double *) &a0) && - (*(double *) &b0) == (*(double *) &b0)) - ? UINT64_C(0) - : ~UINT64_C(0); - d[1] = ((*(double *) &a1) == (*(double *) &a1) && - (*(double *) &b1) == (*(double *) &b1)) - ? UINT64_C(0) - : ~UINT64_C(0); - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b to see if either is NaN, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpunord_sd -FORCE_INLINE __m128d _mm_cmpunord_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_cmpunord_pd(a, b)); -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t d[2]; - d[0] = ((*(double *) &a0) == (*(double *) &a0) && - (*(double *) &b0) == (*(double *) &b0)) - ? UINT64_C(0) - : ~UINT64_C(0); - d[1] = a1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point element in a and b -// for greater-than-or-equal, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comige_sd -FORCE_INLINE int _mm_comige_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vgetq_lane_u64(vcgeq_f64(a, b), 0) & 0x1; -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - - return (*(double *) &a0 >= *(double *) &b0); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point element in a and b -// for greater-than, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comigt_sd -FORCE_INLINE int _mm_comigt_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vgetq_lane_u64(vcgtq_f64(a, b), 0) & 0x1; -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - - return (*(double *) &a0 > *(double *) &b0); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point element in a and b -// for less-than-or-equal, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comile_sd -FORCE_INLINE int _mm_comile_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vgetq_lane_u64(vcleq_f64(a, b), 0) & 0x1; -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - - return (*(double *) &a0 <= *(double *) &b0); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point element in a and b -// for less-than, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comilt_sd -FORCE_INLINE int _mm_comilt_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vgetq_lane_u64(vcltq_f64(a, b), 0) & 0x1; -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - - return (*(double *) &a0 < *(double *) &b0); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point element in a and b -// for equality, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comieq_sd -FORCE_INLINE int _mm_comieq_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vgetq_lane_u64(vceqq_f64(a, b), 0) & 0x1; -#else - uint32x4_t a_not_nan = - vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(a)); - uint32x4_t b_not_nan = - vceqq_u32(vreinterpretq_u32_m128d(b), vreinterpretq_u32_m128d(b)); - uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); - uint32x4_t a_eq_b = - vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); - uint64x2_t and_results = vandq_u64(vreinterpretq_u64_u32(a_and_b_not_nan), - vreinterpretq_u64_u32(a_eq_b)); - return vgetq_lane_u64(and_results, 0) & 0x1; -#endif -} - -// Compare the lower double-precision (64-bit) floating-point element in a and b -// for not-equal, and return the boolean result (0 or 1). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_comineq_sd -FORCE_INLINE int _mm_comineq_sd(__m128d a, __m128d b) -{ - return !_mm_comieq_sd(a, b); -} - -// Convert packed signed 32-bit integers in a to packed double-precision -// (64-bit) floating-point elements, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi32_pd -FORCE_INLINE __m128d _mm_cvtepi32_pd(__m128i a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vcvtq_f64_s64(vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a))))); -#else - double a0 = (double) vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); - double a1 = (double) vgetq_lane_s32(vreinterpretq_s32_m128i(a), 1); - return _mm_set_pd(a1, a0); -#endif -} - -// Convert packed signed 32-bit integers in a to packed single-precision -// (32-bit) floating-point elements, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi32_ps -FORCE_INLINE __m128 _mm_cvtepi32_ps(__m128i a) -{ - return vreinterpretq_m128_f32(vcvtq_f32_s32(vreinterpretq_s32_m128i(a))); -} - -// Convert packed double-precision (64-bit) floating-point elements in a to -// packed 32-bit integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpd_epi32 -FORCE_INLINE __m128i _mm_cvtpd_epi32(__m128d a) -{ -// vrnd32xq_f64 not supported on clang -#if defined(__ARM_FEATURE_FRINT) && !defined(__clang__) - float64x2_t rounded = vrnd32xq_f64(vreinterpretq_f64_m128d(a)); - int64x2_t integers = vcvtq_s64_f64(rounded); - return vreinterpretq_m128i_s32( - vcombine_s32(vmovn_s64(integers), vdup_n_s32(0))); -#else - __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); - double d0 = ((double *) &rnd)[0]; - double d1 = ((double *) &rnd)[1]; - return _mm_set_epi32(0, 0, (int32_t) d1, (int32_t) d0); -#endif -} - -// Convert packed double-precision (64-bit) floating-point elements in a to -// packed 32-bit integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpd_pi32 -FORCE_INLINE __m64 _mm_cvtpd_pi32(__m128d a) -{ - __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); - double d0 = ((double *) &rnd)[0]; - double d1 = ((double *) &rnd)[1]; - int32_t ALIGN_STRUCT(16) data[2] = {(int32_t) d0, (int32_t) d1}; - return vreinterpret_m64_s32(vld1_s32(data)); -} - -// Convert packed double-precision (64-bit) floating-point elements in a to -// packed single-precision (32-bit) floating-point elements, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpd_ps -FORCE_INLINE __m128 _mm_cvtpd_ps(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - float32x2_t tmp = vcvt_f32_f64(vreinterpretq_f64_m128d(a)); - return vreinterpretq_m128_f32(vcombine_f32(tmp, vdup_n_f32(0))); -#else - float a0 = (float) ((double *) &a)[0]; - float a1 = (float) ((double *) &a)[1]; - return _mm_set_ps(0, 0, a1, a0); -#endif -} - -// Convert packed signed 32-bit integers in a to packed double-precision -// (64-bit) floating-point elements, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtpi32_pd -FORCE_INLINE __m128d _mm_cvtpi32_pd(__m64 a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vcvtq_f64_s64(vmovl_s32(vreinterpret_s32_m64(a)))); -#else - double a0 = (double) vget_lane_s32(vreinterpret_s32_m64(a), 0); - double a1 = (double) vget_lane_s32(vreinterpret_s32_m64(a), 1); - return _mm_set_pd(a1, a0); -#endif -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 32-bit integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_epi32 -// *NOTE*. The default rounding mode on SSE is 'round to even', which ARMv7-A -// does not support! It is supported on ARMv8-A however. -FORCE_INLINE __m128i _mm_cvtps_epi32(__m128 a) -{ -#if defined(__ARM_FEATURE_FRINT) - return vreinterpretq_m128i_s32(vcvtq_s32_f32(vrnd32xq_f32(a))); -#elif (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - switch (_MM_GET_ROUNDING_MODE()) { - case _MM_ROUND_NEAREST: - return vreinterpretq_m128i_s32(vcvtnq_s32_f32(a)); - case _MM_ROUND_DOWN: - return vreinterpretq_m128i_s32(vcvtmq_s32_f32(a)); - case _MM_ROUND_UP: - return vreinterpretq_m128i_s32(vcvtpq_s32_f32(a)); - default: // _MM_ROUND_TOWARD_ZERO - return vreinterpretq_m128i_s32(vcvtq_s32_f32(a)); - } -#else - float *f = (float *) &a; - switch (_MM_GET_ROUNDING_MODE()) { - case _MM_ROUND_NEAREST: { - uint32x4_t signmask = vdupq_n_u32(0x80000000); - float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), - vdupq_n_f32(0.5f)); /* +/- 0.5 */ - int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( - vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ - int32x4_t r_trunc = vcvtq_s32_f32( - vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ - int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( - vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ - int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), - vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ - float32x4_t delta = vsubq_f32( - vreinterpretq_f32_m128(a), - vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ - uint32x4_t is_delta_half = - vceqq_f32(delta, half); /* delta == +/- 0.5 */ - return vreinterpretq_m128i_s32( - vbslq_s32(is_delta_half, r_even, r_normal)); - } - case _MM_ROUND_DOWN: - return _mm_set_epi32(floorf(f[3]), floorf(f[2]), floorf(f[1]), - floorf(f[0])); - case _MM_ROUND_UP: - return _mm_set_epi32(ceilf(f[3]), ceilf(f[2]), ceilf(f[1]), - ceilf(f[0])); - default: // _MM_ROUND_TOWARD_ZERO - return _mm_set_epi32((int32_t) f[3], (int32_t) f[2], (int32_t) f[1], - (int32_t) f[0]); - } -#endif -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed double-precision (64-bit) floating-point elements, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_pd -FORCE_INLINE __m128d _mm_cvtps_pd(__m128 a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vcvt_f64_f32(vget_low_f32(vreinterpretq_f32_m128(a)))); -#else - double a0 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); - double a1 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); - return _mm_set_pd(a1, a0); -#endif -} - -// Copy the lower double-precision (64-bit) floating-point element of a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_f64 -FORCE_INLINE double _mm_cvtsd_f64(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return (double) vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0); -#else - return ((double *) &a)[0]; -#endif -} - -// Convert the lower double-precision (64-bit) floating-point element in a to a -// 32-bit integer, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_si32 -FORCE_INLINE int32_t _mm_cvtsd_si32(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return (int32_t) vgetq_lane_f64(vrndiq_f64(vreinterpretq_f64_m128d(a)), 0); -#else - __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); - double ret = ((double *) &rnd)[0]; - return (int32_t) ret; -#endif -} - -// Convert the lower double-precision (64-bit) floating-point element in a to a -// 64-bit integer, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_si64 -FORCE_INLINE int64_t _mm_cvtsd_si64(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return (int64_t) vgetq_lane_f64(vrndiq_f64(vreinterpretq_f64_m128d(a)), 0); -#else - __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); - double ret = ((double *) &rnd)[0]; - return (int64_t) ret; -#endif -} - -// Convert the lower double-precision (64-bit) floating-point element in a to a -// 64-bit integer, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_si64x -#define _mm_cvtsd_si64x _mm_cvtsd_si64 - -// Convert the lower double-precision (64-bit) floating-point element in b to a -// single-precision (32-bit) floating-point element, store the result in the -// lower element of dst, and copy the upper 3 packed elements from a to the -// upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsd_ss -FORCE_INLINE __m128 _mm_cvtsd_ss(__m128 a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32(vsetq_lane_f32( - vget_lane_f32(vcvt_f32_f64(vreinterpretq_f64_m128d(b)), 0), - vreinterpretq_f32_m128(a), 0)); -#else - return vreinterpretq_m128_f32(vsetq_lane_f32((float) ((double *) &b)[0], - vreinterpretq_f32_m128(a), 0)); -#endif -} - -// Copy the lower 32-bit integer in a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si32 -FORCE_INLINE int _mm_cvtsi128_si32(__m128i a) -{ - return vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); -} - -// Copy the lower 64-bit integer in a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si64 -FORCE_INLINE int64_t _mm_cvtsi128_si64(__m128i a) -{ - return vgetq_lane_s64(vreinterpretq_s64_m128i(a), 0); -} - -// Copy the lower 64-bit integer in a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si64x -#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) - -// Convert the signed 32-bit integer b to a double-precision (64-bit) -// floating-point element, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi32_sd -FORCE_INLINE __m128d _mm_cvtsi32_sd(__m128d a, int32_t b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vsetq_lane_f64((double) b, vreinterpretq_f64_m128d(a), 0)); -#else - double bf = (double) b; - return vreinterpretq_m128d_s64( - vsetq_lane_s64(*(int64_t *) &bf, vreinterpretq_s64_m128d(a), 0)); -#endif -} - -// Copy the lower 64-bit integer in a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi128_si64x -#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) - -// Copy 32-bit integer a to the lower elements of dst, and zero the upper -// elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi32_si128 -FORCE_INLINE __m128i _mm_cvtsi32_si128(int a) -{ - return vreinterpretq_m128i_s32(vsetq_lane_s32(a, vdupq_n_s32(0), 0)); -} - -// Convert the signed 64-bit integer b to a double-precision (64-bit) -// floating-point element, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64_sd -FORCE_INLINE __m128d _mm_cvtsi64_sd(__m128d a, int64_t b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vsetq_lane_f64((double) b, vreinterpretq_f64_m128d(a), 0)); -#else - double bf = (double) b; - return vreinterpretq_m128d_s64( - vsetq_lane_s64(*(int64_t *) &bf, vreinterpretq_s64_m128d(a), 0)); -#endif -} - -// Copy 64-bit integer a to the lower element of dst, and zero the upper -// element. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64_si128 -FORCE_INLINE __m128i _mm_cvtsi64_si128(int64_t a) -{ - return vreinterpretq_m128i_s64(vsetq_lane_s64(a, vdupq_n_s64(0), 0)); -} - -// Copy 64-bit integer a to the lower element of dst, and zero the upper -// element. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64x_si128 -#define _mm_cvtsi64x_si128(a) _mm_cvtsi64_si128(a) - -// Convert the signed 64-bit integer b to a double-precision (64-bit) -// floating-point element, store the result in the lower element of dst, and -// copy the upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtsi64x_sd -#define _mm_cvtsi64x_sd(a, b) _mm_cvtsi64_sd(a, b) - -// Convert the lower single-precision (32-bit) floating-point element in b to a -// double-precision (64-bit) floating-point element, store the result in the -// lower element of dst, and copy the upper element from a to the upper element -// of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtss_sd -FORCE_INLINE __m128d _mm_cvtss_sd(__m128d a, __m128 b) -{ - double d = (double) vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vsetq_lane_f64(d, vreinterpretq_f64_m128d(a), 0)); -#else - return vreinterpretq_m128d_s64( - vsetq_lane_s64(*(int64_t *) &d, vreinterpretq_s64_m128d(a), 0)); -#endif -} - -// Convert packed double-precision (64-bit) floating-point elements in a to -// packed 32-bit integers with truncation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttpd_epi32 -FORCE_INLINE __m128i _mm_cvttpd_epi32(__m128d a) -{ - double a0 = ((double *) &a)[0]; - double a1 = ((double *) &a)[1]; - return _mm_set_epi32(0, 0, (int32_t) a1, (int32_t) a0); -} - -// Convert packed double-precision (64-bit) floating-point elements in a to -// packed 32-bit integers with truncation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttpd_pi32 -FORCE_INLINE __m64 _mm_cvttpd_pi32(__m128d a) -{ - double a0 = ((double *) &a)[0]; - double a1 = ((double *) &a)[1]; - int32_t ALIGN_STRUCT(16) data[2] = {(int32_t) a0, (int32_t) a1}; - return vreinterpret_m64_s32(vld1_s32(data)); -} - -// Convert packed single-precision (32-bit) floating-point elements in a to -// packed 32-bit integers with truncation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttps_epi32 -FORCE_INLINE __m128i _mm_cvttps_epi32(__m128 a) -{ - return vreinterpretq_m128i_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a))); -} - -// Convert the lower double-precision (64-bit) floating-point element in a to a -// 32-bit integer with truncation, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttsd_si32 -FORCE_INLINE int32_t _mm_cvttsd_si32(__m128d a) -{ - double ret = *((double *) &a); - return (int32_t) ret; -} - -// Convert the lower double-precision (64-bit) floating-point element in a to a -// 64-bit integer with truncation, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttsd_si64 -FORCE_INLINE int64_t _mm_cvttsd_si64(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vgetq_lane_s64(vcvtq_s64_f64(vreinterpretq_f64_m128d(a)), 0); -#else - double ret = *((double *) &a); - return (int64_t) ret; -#endif -} - -// Convert the lower double-precision (64-bit) floating-point element in a to a -// 64-bit integer with truncation, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvttsd_si64x -#define _mm_cvttsd_si64x(a) _mm_cvttsd_si64(a) - -// Divide packed double-precision (64-bit) floating-point elements in a by -// packed elements in b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_pd -FORCE_INLINE __m128d _mm_div_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vdivq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2]; - c[0] = da[0] / db[0]; - c[1] = da[1] / db[1]; - return vld1q_f32((float32_t *) c); -#endif -} - -// Divide the lower double-precision (64-bit) floating-point element in a by the -// lower double-precision (64-bit) floating-point element in b, store the result -// in the lower element of dst, and copy the upper element from a to the upper -// element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_div_sd -FORCE_INLINE __m128d _mm_div_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - float64x2_t tmp = - vdivq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)); - return vreinterpretq_m128d_f64( - vsetq_lane_f64(vgetq_lane_f64(vreinterpretq_f64_m128d(a), 1), tmp, 1)); -#else - return _mm_move_sd(a, _mm_div_pd(a, b)); -#endif -} - -// Extract a 16-bit integer from a, selected with imm8, and store the result in -// the lower element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi16 -// FORCE_INLINE int _mm_extract_epi16(__m128i a, __constrange(0,8) int imm) -#define _mm_extract_epi16(a, imm) \ - vgetq_lane_u16(vreinterpretq_u16_m128i(a), (imm)) - -// Copy a to dst, and insert the 16-bit integer i into dst at the location -// specified by imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi16 -// FORCE_INLINE __m128i _mm_insert_epi16(__m128i a, int b, -// __constrange(0,8) int imm) -#define _mm_insert_epi16(a, b, imm) \ - vreinterpretq_m128i_s16( \ - vsetq_lane_s16((b), vreinterpretq_s16_m128i(a), (imm))) - -// Load 128-bits (composed of 2 packed double-precision (64-bit) floating-point -// elements) from memory into dst. mem_addr must be aligned on a 16-byte -// boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_pd -FORCE_INLINE __m128d _mm_load_pd(const double *p) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vld1q_f64(p)); -#else - const float *fp = (const float *) p; - float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], fp[2], fp[3]}; - return vreinterpretq_m128d_f32(vld1q_f32(data)); -#endif -} - -// Load a double-precision (64-bit) floating-point element from memory into both -// elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_pd1 -#define _mm_load_pd1 _mm_load1_pd - -// Load a double-precision (64-bit) floating-point element from memory into the -// lower of dst, and zero the upper element. mem_addr does not need to be -// aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_sd -FORCE_INLINE __m128d _mm_load_sd(const double *p) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vsetq_lane_f64(*p, vdupq_n_f64(0), 0)); -#else - const float *fp = (const float *) p; - float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], 0, 0}; - return vreinterpretq_m128d_f32(vld1q_f32(data)); -#endif -} - -// Load 128-bits of integer data from memory into dst. mem_addr must be aligned -// on a 16-byte boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load_si128 -FORCE_INLINE __m128i _mm_load_si128(const __m128i *p) -{ - return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); -} - -// Load a double-precision (64-bit) floating-point element from memory into both -// elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_load1_pd -FORCE_INLINE __m128d _mm_load1_pd(const double *p) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vld1q_dup_f64(p)); -#else - return vreinterpretq_m128d_s64(vdupq_n_s64(*(const int64_t *) p)); -#endif -} - -// Load a double-precision (64-bit) floating-point element from memory into the -// upper element of dst, and copy the lower element from a to dst. mem_addr does -// not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadh_pd -FORCE_INLINE __m128d _mm_loadh_pd(__m128d a, const double *p) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vcombine_f64(vget_low_f64(vreinterpretq_f64_m128d(a)), vld1_f64(p))); -#else - return vreinterpretq_m128d_f32(vcombine_f32( - vget_low_f32(vreinterpretq_f32_m128d(a)), vld1_f32((const float *) p))); -#endif -} - -// Load 64-bit integer from memory into the first element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadl_epi64 -FORCE_INLINE __m128i _mm_loadl_epi64(__m128i const *p) -{ - /* Load the lower 64 bits of the value pointed to by p into the - * lower 64 bits of the result, zeroing the upper 64 bits of the result. - */ - return vreinterpretq_m128i_s32( - vcombine_s32(vld1_s32((int32_t const *) p), vcreate_s32(0))); -} - -// Load a double-precision (64-bit) floating-point element from memory into the -// lower element of dst, and copy the upper element from a to dst. mem_addr does -// not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadl_pd -FORCE_INLINE __m128d _mm_loadl_pd(__m128d a, const double *p) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vcombine_f64(vld1_f64(p), vget_high_f64(vreinterpretq_f64_m128d(a)))); -#else - return vreinterpretq_m128d_f32( - vcombine_f32(vld1_f32((const float *) p), - vget_high_f32(vreinterpretq_f32_m128d(a)))); -#endif -} - -// Load 2 double-precision (64-bit) floating-point elements from memory into dst -// in reverse order. mem_addr must be aligned on a 16-byte boundary or a -// general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadr_pd -FORCE_INLINE __m128d _mm_loadr_pd(const double *p) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - float64x2_t v = vld1q_f64(p); - return vreinterpretq_m128d_f64(vextq_f64(v, v, 1)); -#else - int64x2_t v = vld1q_s64((const int64_t *) p); - return vreinterpretq_m128d_s64(vextq_s64(v, v, 1)); -#endif -} - -// Loads two double-precision from unaligned memory, floating-point values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_pd -FORCE_INLINE __m128d _mm_loadu_pd(const double *p) -{ - return _mm_load_pd(p); -} - -// Load 128-bits of integer data from memory into dst. mem_addr does not need to -// be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si128 -FORCE_INLINE __m128i _mm_loadu_si128(const __m128i *p) -{ - return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); -} - -// Load unaligned 32-bit integer from memory into the first element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loadu_si32 -FORCE_INLINE __m128i _mm_loadu_si32(const void *p) -{ - return vreinterpretq_m128i_s32( - vsetq_lane_s32(*(const int32_t *) p, vdupq_n_s32(0), 0)); -} - -// Multiply packed signed 16-bit integers in a and b, producing intermediate -// signed 32-bit integers. Horizontally add adjacent pairs of intermediate -// 32-bit integers, and pack the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_madd_epi16 -FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) -{ - int32x4_t low = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), - vget_low_s16(vreinterpretq_s16_m128i(b))); -#if defined(__aarch64__) || defined(_M_ARM64) - int32x4_t high = - vmull_high_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b)); - - return vreinterpretq_m128i_s32(vpaddq_s32(low, high)); -#else - int32x4_t high = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), - vget_high_s16(vreinterpretq_s16_m128i(b))); - - int32x2_t low_sum = vpadd_s32(vget_low_s32(low), vget_high_s32(low)); - int32x2_t high_sum = vpadd_s32(vget_low_s32(high), vget_high_s32(high)); - - return vreinterpretq_m128i_s32(vcombine_s32(low_sum, high_sum)); -#endif -} - -// Conditionally store 8-bit integer elements from a into memory using mask -// (elements are not stored when the highest bit is not set in the corresponding -// element) and a non-temporal memory hint. mem_addr does not need to be aligned -// on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maskmoveu_si128 -FORCE_INLINE void _mm_maskmoveu_si128(__m128i a, __m128i mask, char *mem_addr) -{ - int8x16_t shr_mask = vshrq_n_s8(vreinterpretq_s8_m128i(mask), 7); - __m128 b = _mm_load_ps((const float *) mem_addr); - int8x16_t masked = - vbslq_s8(vreinterpretq_u8_s8(shr_mask), vreinterpretq_s8_m128i(a), - vreinterpretq_s8_m128(b)); - vst1q_s8((int8_t *) mem_addr, masked); -} - -// Compare packed signed 16-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epi16 -FORCE_INLINE __m128i _mm_max_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vmaxq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Compare packed unsigned 8-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu8 -FORCE_INLINE __m128i _mm_max_epu8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vmaxq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b, -// and store packed maximum values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_pd -FORCE_INLINE __m128d _mm_max_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) -#if SSE2NEON_PRECISE_MINMAX - float64x2_t _a = vreinterpretq_f64_m128d(a); - float64x2_t _b = vreinterpretq_f64_m128d(b); - return vreinterpretq_m128d_f64(vbslq_f64(vcgtq_f64(_a, _b), _a, _b)); -#else - return vreinterpretq_m128d_f64( - vmaxq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#endif -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) > (*(double *) &b0) ? a0 : b0; - d[1] = (*(double *) &a1) > (*(double *) &b1) ? a1 : b1; - - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b, store the maximum value in the lower element of dst, and copy the upper -// element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_sd -FORCE_INLINE __m128d _mm_max_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_max_pd(a, b)); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2] = {da[0] > db[0] ? da[0] : db[0], da[1]}; - return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) c)); -#endif -} - -// Compare packed signed 16-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epi16 -FORCE_INLINE __m128i _mm_min_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vminq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Compare packed unsigned 8-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epu8 -FORCE_INLINE __m128i _mm_min_epu8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vminq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); -} - -// Compare packed double-precision (64-bit) floating-point elements in a and b, -// and store packed minimum values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_pd -FORCE_INLINE __m128d _mm_min_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) -#if SSE2NEON_PRECISE_MINMAX - float64x2_t _a = vreinterpretq_f64_m128d(a); - float64x2_t _b = vreinterpretq_f64_m128d(b); - return vreinterpretq_m128d_f64(vbslq_f64(vcltq_f64(_a, _b), _a, _b)); -#else - return vreinterpretq_m128d_f64( - vminq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#endif -#else - uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); - uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); - uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); - uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); - uint64_t d[2]; - d[0] = (*(double *) &a0) < (*(double *) &b0) ? a0 : b0; - d[1] = (*(double *) &a1) < (*(double *) &b1) ? a1 : b1; - return vreinterpretq_m128d_u64(vld1q_u64(d)); -#endif -} - -// Compare the lower double-precision (64-bit) floating-point elements in a and -// b, store the minimum value in the lower element of dst, and copy the upper -// element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_sd -FORCE_INLINE __m128d _mm_min_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_min_pd(a, b)); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2] = {da[0] < db[0] ? da[0] : db[0], da[1]}; - return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) c)); -#endif -} - -// Copy the lower 64-bit integer in a to the lower element of dst, and zero the -// upper element. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_move_epi64 -FORCE_INLINE __m128i _mm_move_epi64(__m128i a) -{ - return vreinterpretq_m128i_s64( - vsetq_lane_s64(0, vreinterpretq_s64_m128i(a), 1)); -} - -// Move the lower double-precision (64-bit) floating-point element from b to the -// lower element of dst, and copy the upper element from a to the upper element -// of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_move_sd -FORCE_INLINE __m128d _mm_move_sd(__m128d a, __m128d b) -{ - return vreinterpretq_m128d_f32( - vcombine_f32(vget_low_f32(vreinterpretq_f32_m128d(b)), - vget_high_f32(vreinterpretq_f32_m128d(a)))); -} - -// Create mask from the most significant bit of each 8-bit element in a, and -// store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_epi8 -FORCE_INLINE int _mm_movemask_epi8(__m128i a) -{ - // Use increasingly wide shifts+adds to collect the sign bits - // together. - // Since the widening shifts would be rather confusing to follow in little - // endian, everything will be illustrated in big endian order instead. This - // has a different result - the bits would actually be reversed on a big - // endian machine. - - // Starting input (only half the elements are shown): - // 89 ff 1d c0 00 10 99 33 - uint8x16_t input = vreinterpretq_u8_m128i(a); - - // Shift out everything but the sign bits with an unsigned shift right. - // - // Bytes of the vector:: - // 89 ff 1d c0 00 10 99 33 - // \ \ \ \ \ \ \ \ high_bits = (uint16x4_t)(input >> 7) - // | | | | | | | | - // 01 01 00 01 00 00 01 00 - // - // Bits of first important lane(s): - // 10001001 (89) - // \______ - // | - // 00000001 (01) - uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(input, 7)); - - // Merge the even lanes together with a 16-bit unsigned shift right + add. - // 'xx' represents garbage data which will be ignored in the final result. - // In the important bytes, the add functions like a binary OR. - // - // 01 01 00 01 00 00 01 00 - // \_ | \_ | \_ | \_ | paired16 = (uint32x4_t)(input + (input >> 7)) - // \| \| \| \| - // xx 03 xx 01 xx 00 xx 02 - // - // 00000001 00000001 (01 01) - // \_______ | - // \| - // xxxxxxxx xxxxxx11 (xx 03) - uint32x4_t paired16 = - vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); - - // Repeat with a wider 32-bit shift + add. - // xx 03 xx 01 xx 00 xx 02 - // \____ | \____ | paired32 = (uint64x1_t)(paired16 + (paired16 >> - // 14)) - // \| \| - // xx xx xx 0d xx xx xx 02 - // - // 00000011 00000001 (03 01) - // \\_____ || - // '----.\|| - // xxxxxxxx xxxx1101 (xx 0d) - uint64x2_t paired32 = - vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); - - // Last, an even wider 64-bit shift + add to get our result in the low 8 bit - // lanes. xx xx xx 0d xx xx xx 02 - // \_________ | paired64 = (uint8x8_t)(paired32 + (paired32 >> - // 28)) - // \| - // xx xx xx xx xx xx xx d2 - // - // 00001101 00000010 (0d 02) - // \ \___ | | - // '---. \| | - // xxxxxxxx 11010010 (xx d2) - uint8x16_t paired64 = - vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); - - // Extract the low 8 bits from each 64-bit lane with 2 8-bit extracts. - // xx xx xx xx xx xx xx d2 - // || return paired64[0] - // d2 - // Note: Little endian would return the correct value 4b (01001011) instead. - return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); -} - -// Set each bit of mask dst based on the most significant bit of the -// corresponding packed double-precision (64-bit) floating-point element in a. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movemask_pd -FORCE_INLINE int _mm_movemask_pd(__m128d a) -{ - uint64x2_t input = vreinterpretq_u64_m128d(a); - uint64x2_t high_bits = vshrq_n_u64(input, 63); - return (int) (vgetq_lane_u64(high_bits, 0) | - (vgetq_lane_u64(high_bits, 1) << 1)); -} - -// Copy the lower 64-bit integer in a to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movepi64_pi64 -FORCE_INLINE __m64 _mm_movepi64_pi64(__m128i a) -{ - return vreinterpret_m64_s64(vget_low_s64(vreinterpretq_s64_m128i(a))); -} - -// Copy the 64-bit integer a to the lower element of dst, and zero the upper -// element. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movpi64_epi64 -FORCE_INLINE __m128i _mm_movpi64_epi64(__m64 a) -{ - return vreinterpretq_m128i_s64( - vcombine_s64(vreinterpret_s64_m64(a), vdup_n_s64(0))); -} - -// Multiply the low unsigned 32-bit integers from each packed 64-bit element in -// a and b, and store the unsigned 64-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_epu32 -FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) -{ - // vmull_u32 upcasts instead of masking, so we downcast. - uint32x2_t a_lo = vmovn_u64(vreinterpretq_u64_m128i(a)); - uint32x2_t b_lo = vmovn_u64(vreinterpretq_u64_m128i(b)); - return vreinterpretq_m128i_u64(vmull_u32(a_lo, b_lo)); -} - -// Multiply packed double-precision (64-bit) floating-point elements in a and b, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_pd -FORCE_INLINE __m128d _mm_mul_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vmulq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2]; - c[0] = da[0] * db[0]; - c[1] = da[1] * db[1]; - return vld1q_f32((float32_t *) c); -#endif -} - -// Multiply the lower double-precision (64-bit) floating-point element in a and -// b, store the result in the lower element of dst, and copy the upper element -// from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_mul_sd -FORCE_INLINE __m128d _mm_mul_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_mul_pd(a, b)); -} - -// Multiply the low unsigned 32-bit integers from a and b, and store the -// unsigned 64-bit result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_su32 -FORCE_INLINE __m64 _mm_mul_su32(__m64 a, __m64 b) -{ - return vreinterpret_m64_u64(vget_low_u64( - vmull_u32(vreinterpret_u32_m64(a), vreinterpret_u32_m64(b)))); -} - -// Multiply the packed signed 16-bit integers in a and b, producing intermediate -// 32-bit integers, and store the high 16 bits of the intermediate integers in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhi_epi16 -FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) -{ - /* FIXME: issue with large values because of result saturation */ - // int16x8_t ret = vqdmulhq_s16(vreinterpretq_s16_m128i(a), - // vreinterpretq_s16_m128i(b)); /* =2*a*b */ return - // vreinterpretq_m128i_s16(vshrq_n_s16(ret, 1)); - int16x4_t a3210 = vget_low_s16(vreinterpretq_s16_m128i(a)); - int16x4_t b3210 = vget_low_s16(vreinterpretq_s16_m128i(b)); - int32x4_t ab3210 = vmull_s16(a3210, b3210); /* 3333222211110000 */ - int16x4_t a7654 = vget_high_s16(vreinterpretq_s16_m128i(a)); - int16x4_t b7654 = vget_high_s16(vreinterpretq_s16_m128i(b)); - int32x4_t ab7654 = vmull_s16(a7654, b7654); /* 7777666655554444 */ - uint16x8x2_t r = - vuzpq_u16(vreinterpretq_u16_s32(ab3210), vreinterpretq_u16_s32(ab7654)); - return vreinterpretq_m128i_u16(r.val[1]); -} - -// Multiply the packed unsigned 16-bit integers in a and b, producing -// intermediate 32-bit integers, and store the high 16 bits of the intermediate -// integers in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhi_epu16 -FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) -{ - uint16x4_t a3210 = vget_low_u16(vreinterpretq_u16_m128i(a)); - uint16x4_t b3210 = vget_low_u16(vreinterpretq_u16_m128i(b)); - uint32x4_t ab3210 = vmull_u16(a3210, b3210); -#if defined(__aarch64__) || defined(_M_ARM64) - uint32x4_t ab7654 = - vmull_high_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b)); - uint16x8_t r = vuzp2q_u16(vreinterpretq_u16_u32(ab3210), - vreinterpretq_u16_u32(ab7654)); - return vreinterpretq_m128i_u16(r); -#else - uint16x4_t a7654 = vget_high_u16(vreinterpretq_u16_m128i(a)); - uint16x4_t b7654 = vget_high_u16(vreinterpretq_u16_m128i(b)); - uint32x4_t ab7654 = vmull_u16(a7654, b7654); - uint16x8x2_t r = - vuzpq_u16(vreinterpretq_u16_u32(ab3210), vreinterpretq_u16_u32(ab7654)); - return vreinterpretq_m128i_u16(r.val[1]); -#endif -} - -// Multiply the packed 16-bit integers in a and b, producing intermediate 32-bit -// integers, and store the low 16 bits of the intermediate integers in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mullo_epi16 -FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vmulq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Compute the bitwise OR of packed double-precision (64-bit) floating-point -// elements in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_or_pd -FORCE_INLINE __m128d _mm_or_pd(__m128d a, __m128d b) -{ - return vreinterpretq_m128d_s64( - vorrq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); -} - -// Compute the bitwise OR of 128 bits (representing integer data) in a and b, -// and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_or_si128 -FORCE_INLINE __m128i _mm_or_si128(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vorrq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Convert packed signed 16-bit integers from a and b to packed 8-bit integers -// using signed saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi16 -FORCE_INLINE __m128i _mm_packs_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vcombine_s8(vqmovn_s16(vreinterpretq_s16_m128i(a)), - vqmovn_s16(vreinterpretq_s16_m128i(b)))); -} - -// Convert packed signed 32-bit integers from a and b to packed 16-bit integers -// using signed saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi32 -FORCE_INLINE __m128i _mm_packs_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vcombine_s16(vqmovn_s32(vreinterpretq_s32_m128i(a)), - vqmovn_s32(vreinterpretq_s32_m128i(b)))); -} - -// Convert packed signed 16-bit integers from a and b to packed 8-bit integers -// using unsigned saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi16 -FORCE_INLINE __m128i _mm_packus_epi16(const __m128i a, const __m128i b) -{ - return vreinterpretq_m128i_u8( - vcombine_u8(vqmovun_s16(vreinterpretq_s16_m128i(a)), - vqmovun_s16(vreinterpretq_s16_m128i(b)))); -} - -// Pause the processor. This is typically used in spin-wait loops and depending -// on the x86 processor typical values are in the 40-100 cycle range. The -// 'yield' instruction isn't a good fit because it's effectively a nop on most -// Arm cores. Experience with several databases has shown has shown an 'isb' is -// a reasonable approximation. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_pause -FORCE_INLINE void _mm_pause(void) -{ -#if defined(_MSC_VER) - __isb(_ARM64_BARRIER_SY); -#else - __asm__ __volatile__("isb\n"); -#endif -} - -// Compute the absolute differences of packed unsigned 8-bit integers in a and -// b, then horizontally sum each consecutive 8 differences to produce two -// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low -// 16 bits of 64-bit elements in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8 -FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) -{ - uint16x8_t t = vpaddlq_u8(vabdq_u8((uint8x16_t) a, (uint8x16_t) b)); - return vreinterpretq_m128i_u64(vpaddlq_u32(vpaddlq_u16(t))); -} - -// Set packed 16-bit integers in dst with the supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi16 -FORCE_INLINE __m128i _mm_set_epi16(short i7, - short i6, - short i5, - short i4, - short i3, - short i2, - short i1, - short i0) -{ - int16_t ALIGN_STRUCT(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7}; - return vreinterpretq_m128i_s16(vld1q_s16(data)); -} - -// Set packed 32-bit integers in dst with the supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi32 -FORCE_INLINE __m128i _mm_set_epi32(int i3, int i2, int i1, int i0) -{ - int32_t ALIGN_STRUCT(16) data[4] = {i0, i1, i2, i3}; - return vreinterpretq_m128i_s32(vld1q_s32(data)); -} - -// Set packed 64-bit integers in dst with the supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi64 -FORCE_INLINE __m128i _mm_set_epi64(__m64 i1, __m64 i2) -{ - return _mm_set_epi64x(vget_lane_s64(i1, 0), vget_lane_s64(i2, 0)); -} - -// Set packed 64-bit integers in dst with the supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi64x -FORCE_INLINE __m128i _mm_set_epi64x(int64_t i1, int64_t i2) -{ - return vreinterpretq_m128i_s64( - vcombine_s64(vcreate_s64(i2), vcreate_s64(i1))); -} - -// Set packed 8-bit integers in dst with the supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_epi8 -FORCE_INLINE __m128i _mm_set_epi8(signed char b15, - signed char b14, - signed char b13, - signed char b12, - signed char b11, - signed char b10, - signed char b9, - signed char b8, - signed char b7, - signed char b6, - signed char b5, - signed char b4, - signed char b3, - signed char b2, - signed char b1, - signed char b0) -{ - int8_t ALIGN_STRUCT(16) - data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, - (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, - (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, - (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; - return (__m128i) vld1q_s8(data); -} - -// Set packed double-precision (64-bit) floating-point elements in dst with the -// supplied values. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_pd -FORCE_INLINE __m128d _mm_set_pd(double e1, double e0) -{ - double ALIGN_STRUCT(16) data[2] = {e0, e1}; -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vld1q_f64((float64_t *) data)); -#else - return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) data)); -#endif -} - -// Broadcast double-precision (64-bit) floating-point value a to all elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_pd1 -#define _mm_set_pd1 _mm_set1_pd - -// Copy double-precision (64-bit) floating-point element a to the lower element -// of dst, and zero the upper element. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set_sd -FORCE_INLINE __m128d _mm_set_sd(double a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vsetq_lane_f64(a, vdupq_n_f64(0), 0)); -#else - return _mm_set_pd(0, a); -#endif -} - -// Broadcast 16-bit integer a to all elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi16 -FORCE_INLINE __m128i _mm_set1_epi16(short w) -{ - return vreinterpretq_m128i_s16(vdupq_n_s16(w)); -} - -// Broadcast 32-bit integer a to all elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi32 -FORCE_INLINE __m128i _mm_set1_epi32(int _i) -{ - return vreinterpretq_m128i_s32(vdupq_n_s32(_i)); -} - -// Broadcast 64-bit integer a to all elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi64 -FORCE_INLINE __m128i _mm_set1_epi64(__m64 _i) -{ - return vreinterpretq_m128i_s64(vdupq_lane_s64(_i, 0)); -} - -// Broadcast 64-bit integer a to all elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi64x -FORCE_INLINE __m128i _mm_set1_epi64x(int64_t _i) -{ - return vreinterpretq_m128i_s64(vdupq_n_s64(_i)); -} - -// Broadcast 8-bit integer a to all elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_epi8 -FORCE_INLINE __m128i _mm_set1_epi8(signed char w) -{ - return vreinterpretq_m128i_s8(vdupq_n_s8(w)); -} - -// Broadcast double-precision (64-bit) floating-point value a to all elements of -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_set1_pd -FORCE_INLINE __m128d _mm_set1_pd(double d) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vdupq_n_f64(d)); -#else - return vreinterpretq_m128d_s64(vdupq_n_s64(*(int64_t *) &d)); -#endif -} - -// Set packed 16-bit integers in dst with the supplied values in reverse order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi16 -FORCE_INLINE __m128i _mm_setr_epi16(short w0, - short w1, - short w2, - short w3, - short w4, - short w5, - short w6, - short w7) -{ - int16_t ALIGN_STRUCT(16) data[8] = {w0, w1, w2, w3, w4, w5, w6, w7}; - return vreinterpretq_m128i_s16(vld1q_s16((int16_t *) data)); -} - -// Set packed 32-bit integers in dst with the supplied values in reverse order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi32 -FORCE_INLINE __m128i _mm_setr_epi32(int i3, int i2, int i1, int i0) -{ - int32_t ALIGN_STRUCT(16) data[4] = {i3, i2, i1, i0}; - return vreinterpretq_m128i_s32(vld1q_s32(data)); -} - -// Set packed 64-bit integers in dst with the supplied values in reverse order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi64 -FORCE_INLINE __m128i _mm_setr_epi64(__m64 e1, __m64 e0) -{ - return vreinterpretq_m128i_s64(vcombine_s64(e1, e0)); -} - -// Set packed 8-bit integers in dst with the supplied values in reverse order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_epi8 -FORCE_INLINE __m128i _mm_setr_epi8(signed char b0, - signed char b1, - signed char b2, - signed char b3, - signed char b4, - signed char b5, - signed char b6, - signed char b7, - signed char b8, - signed char b9, - signed char b10, - signed char b11, - signed char b12, - signed char b13, - signed char b14, - signed char b15) -{ - int8_t ALIGN_STRUCT(16) - data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, - (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, - (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, - (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; - return (__m128i) vld1q_s8(data); -} - -// Set packed double-precision (64-bit) floating-point elements in dst with the -// supplied values in reverse order. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setr_pd -FORCE_INLINE __m128d _mm_setr_pd(double e1, double e0) -{ - return _mm_set_pd(e0, e1); -} - -// Return vector of type __m128d with all elements set to zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setzero_pd -FORCE_INLINE __m128d _mm_setzero_pd(void) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vdupq_n_f64(0)); -#else - return vreinterpretq_m128d_f32(vdupq_n_f32(0)); -#endif -} - -// Return vector of type __m128i with all elements set to zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setzero_si128 -FORCE_INLINE __m128i _mm_setzero_si128(void) -{ - return vreinterpretq_m128i_s32(vdupq_n_s32(0)); -} - -// Shuffle 32-bit integers in a using the control in imm8, and store the results -// in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi32 -// FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i a, -// __constrange(0,255) int imm) -#if defined(_sse2neon_shuffle) -#define _mm_shuffle_epi32(a, imm) \ - __extension__({ \ - int32x4_t _input = vreinterpretq_s32_m128i(a); \ - int32x4_t _shuf = \ - vshuffleq_s32(_input, _input, (imm) & (0x3), ((imm) >> 2) & 0x3, \ - ((imm) >> 4) & 0x3, ((imm) >> 6) & 0x3); \ - vreinterpretq_m128i_s32(_shuf); \ - }) -#else // generic -#define _mm_shuffle_epi32(a, imm) \ - _sse2neon_define1( \ - __m128i, a, __m128i ret; switch (imm) { \ - case _MM_SHUFFLE(1, 0, 3, 2): \ - ret = _mm_shuffle_epi_1032(_a); \ - break; \ - case _MM_SHUFFLE(2, 3, 0, 1): \ - ret = _mm_shuffle_epi_2301(_a); \ - break; \ - case _MM_SHUFFLE(0, 3, 2, 1): \ - ret = _mm_shuffle_epi_0321(_a); \ - break; \ - case _MM_SHUFFLE(2, 1, 0, 3): \ - ret = _mm_shuffle_epi_2103(_a); \ - break; \ - case _MM_SHUFFLE(1, 0, 1, 0): \ - ret = _mm_shuffle_epi_1010(_a); \ - break; \ - case _MM_SHUFFLE(1, 0, 0, 1): \ - ret = _mm_shuffle_epi_1001(_a); \ - break; \ - case _MM_SHUFFLE(0, 1, 0, 1): \ - ret = _mm_shuffle_epi_0101(_a); \ - break; \ - case _MM_SHUFFLE(2, 2, 1, 1): \ - ret = _mm_shuffle_epi_2211(_a); \ - break; \ - case _MM_SHUFFLE(0, 1, 2, 2): \ - ret = _mm_shuffle_epi_0122(_a); \ - break; \ - case _MM_SHUFFLE(3, 3, 3, 2): \ - ret = _mm_shuffle_epi_3332(_a); \ - break; \ - case _MM_SHUFFLE(0, 0, 0, 0): \ - ret = _mm_shuffle_epi32_splat(_a, 0); \ - break; \ - case _MM_SHUFFLE(1, 1, 1, 1): \ - ret = _mm_shuffle_epi32_splat(_a, 1); \ - break; \ - case _MM_SHUFFLE(2, 2, 2, 2): \ - ret = _mm_shuffle_epi32_splat(_a, 2); \ - break; \ - case _MM_SHUFFLE(3, 3, 3, 3): \ - ret = _mm_shuffle_epi32_splat(_a, 3); \ - break; \ - default: \ - ret = _mm_shuffle_epi32_default(_a, (imm)); \ - break; \ - } _sse2neon_return(ret);) -#endif - -// Shuffle double-precision (64-bit) floating-point elements using the control -// in imm8, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_pd -#ifdef _sse2neon_shuffle -#define _mm_shuffle_pd(a, b, imm8) \ - vreinterpretq_m128d_s64( \ - vshuffleq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b), \ - imm8 & 0x1, ((imm8 & 0x2) >> 1) + 2)) -#else -#define _mm_shuffle_pd(a, b, imm8) \ - _mm_castsi128_pd(_mm_set_epi64x( \ - vgetq_lane_s64(vreinterpretq_s64_m128d(b), (imm8 & 0x2) >> 1), \ - vgetq_lane_s64(vreinterpretq_s64_m128d(a), imm8 & 0x1))) -#endif - -// FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i a, -// __constrange(0,255) int imm) -#if defined(_sse2neon_shuffle) -#define _mm_shufflehi_epi16(a, imm) \ - __extension__({ \ - int16x8_t _input = vreinterpretq_s16_m128i(a); \ - int16x8_t _shuf = \ - vshuffleq_s16(_input, _input, 0, 1, 2, 3, ((imm) & (0x3)) + 4, \ - (((imm) >> 2) & 0x3) + 4, (((imm) >> 4) & 0x3) + 4, \ - (((imm) >> 6) & 0x3) + 4); \ - vreinterpretq_m128i_s16(_shuf); \ - }) -#else // generic -#define _mm_shufflehi_epi16(a, imm) _mm_shufflehi_epi16_function((a), (imm)) -#endif - -// FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i a, -// __constrange(0,255) int imm) -#if defined(_sse2neon_shuffle) -#define _mm_shufflelo_epi16(a, imm) \ - __extension__({ \ - int16x8_t _input = vreinterpretq_s16_m128i(a); \ - int16x8_t _shuf = vshuffleq_s16( \ - _input, _input, ((imm) & (0x3)), (((imm) >> 2) & 0x3), \ - (((imm) >> 4) & 0x3), (((imm) >> 6) & 0x3), 4, 5, 6, 7); \ - vreinterpretq_m128i_s16(_shuf); \ - }) -#else // generic -#define _mm_shufflelo_epi16(a, imm) _mm_shufflelo_epi16_function((a), (imm)) -#endif - -// Shift packed 16-bit integers in a left by count while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sll_epi16 -FORCE_INLINE __m128i _mm_sll_epi16(__m128i a, __m128i count) -{ - uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); - if (_sse2neon_unlikely(c & ~15)) - return _mm_setzero_si128(); - - int16x8_t vc = vdupq_n_s16((int16_t) c); - return vreinterpretq_m128i_s16(vshlq_s16(vreinterpretq_s16_m128i(a), vc)); -} - -// Shift packed 32-bit integers in a left by count while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sll_epi32 -FORCE_INLINE __m128i _mm_sll_epi32(__m128i a, __m128i count) -{ - uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); - if (_sse2neon_unlikely(c & ~31)) - return _mm_setzero_si128(); - - int32x4_t vc = vdupq_n_s32((int32_t) c); - return vreinterpretq_m128i_s32(vshlq_s32(vreinterpretq_s32_m128i(a), vc)); -} - -// Shift packed 64-bit integers in a left by count while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sll_epi64 -FORCE_INLINE __m128i _mm_sll_epi64(__m128i a, __m128i count) -{ - uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); - if (_sse2neon_unlikely(c & ~63)) - return _mm_setzero_si128(); - - int64x2_t vc = vdupq_n_s64((int64_t) c); - return vreinterpretq_m128i_s64(vshlq_s64(vreinterpretq_s64_m128i(a), vc)); -} - -// Shift packed 16-bit integers in a left by imm8 while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_epi16 -FORCE_INLINE __m128i _mm_slli_epi16(__m128i a, int imm) -{ - if (_sse2neon_unlikely(imm & ~15)) - return _mm_setzero_si128(); - return vreinterpretq_m128i_s16( - vshlq_s16(vreinterpretq_s16_m128i(a), vdupq_n_s16(imm))); -} - -// Shift packed 32-bit integers in a left by imm8 while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_epi32 -FORCE_INLINE __m128i _mm_slli_epi32(__m128i a, int imm) -{ - if (_sse2neon_unlikely(imm & ~31)) - return _mm_setzero_si128(); - return vreinterpretq_m128i_s32( - vshlq_s32(vreinterpretq_s32_m128i(a), vdupq_n_s32(imm))); -} - -// Shift packed 64-bit integers in a left by imm8 while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_epi64 -FORCE_INLINE __m128i _mm_slli_epi64(__m128i a, int imm) -{ - if (_sse2neon_unlikely(imm & ~63)) - return _mm_setzero_si128(); - return vreinterpretq_m128i_s64( - vshlq_s64(vreinterpretq_s64_m128i(a), vdupq_n_s64(imm))); -} - -// Shift a left by imm8 bytes while shifting in zeros, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_slli_si128 -#define _mm_slli_si128(a, imm) \ - _sse2neon_define1( \ - __m128i, a, int8x16_t ret; \ - if (_sse2neon_unlikely(imm == 0)) ret = vreinterpretq_s8_m128i(_a); \ - else if (_sse2neon_unlikely((imm) & ~15)) ret = vdupq_n_s8(0); \ - else ret = vextq_s8(vdupq_n_s8(0), vreinterpretq_s8_m128i(_a), \ - ((imm <= 0 || imm > 15) ? 0 : (16 - imm))); \ - _sse2neon_return(vreinterpretq_m128i_s8(ret));) - -// Compute the square root of packed double-precision (64-bit) floating-point -// elements in a, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_pd -FORCE_INLINE __m128d _mm_sqrt_pd(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vsqrtq_f64(vreinterpretq_f64_m128d(a))); -#else - double a0 = sqrt(((double *) &a)[0]); - double a1 = sqrt(((double *) &a)[1]); - return _mm_set_pd(a1, a0); -#endif -} - -// Compute the square root of the lower double-precision (64-bit) floating-point -// element in b, store the result in the lower element of dst, and copy the -// upper element from a to the upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_sd -FORCE_INLINE __m128d _mm_sqrt_sd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return _mm_move_sd(a, _mm_sqrt_pd(b)); -#else - return _mm_set_pd(((double *) &a)[1], sqrt(((double *) &b)[0])); -#endif -} - -// Shift packed 16-bit integers in a right by count while shifting in sign bits, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sra_epi16 -FORCE_INLINE __m128i _mm_sra_epi16(__m128i a, __m128i count) -{ - int64_t c = vgetq_lane_s64(count, 0); - if (_sse2neon_unlikely(c & ~15)) - return _mm_cmplt_epi16(a, _mm_setzero_si128()); - return vreinterpretq_m128i_s16( - vshlq_s16((int16x8_t) a, vdupq_n_s16((int) -c))); -} - -// Shift packed 32-bit integers in a right by count while shifting in sign bits, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sra_epi32 -FORCE_INLINE __m128i _mm_sra_epi32(__m128i a, __m128i count) -{ - int64_t c = vgetq_lane_s64(count, 0); - if (_sse2neon_unlikely(c & ~31)) - return _mm_cmplt_epi32(a, _mm_setzero_si128()); - return vreinterpretq_m128i_s32( - vshlq_s32((int32x4_t) a, vdupq_n_s32((int) -c))); -} - -// Shift packed 16-bit integers in a right by imm8 while shifting in sign -// bits, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srai_epi16 -FORCE_INLINE __m128i _mm_srai_epi16(__m128i a, int imm) -{ - const int count = (imm & ~15) ? 15 : imm; - return (__m128i) vshlq_s16((int16x8_t) a, vdupq_n_s16(-count)); -} - -// Shift packed 32-bit integers in a right by imm8 while shifting in sign bits, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srai_epi32 -// FORCE_INLINE __m128i _mm_srai_epi32(__m128i a, __constrange(0,255) int imm) -#define _mm_srai_epi32(a, imm) \ - _sse2neon_define0( \ - __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) == 0)) { \ - ret = _a; \ - } else if (_sse2neon_likely(0 < (imm) && (imm) < 32)) { \ - ret = vreinterpretq_m128i_s32( \ - vshlq_s32(vreinterpretq_s32_m128i(_a), vdupq_n_s32(-(imm)))); \ - } else { \ - ret = vreinterpretq_m128i_s32( \ - vshrq_n_s32(vreinterpretq_s32_m128i(_a), 31)); \ - } _sse2neon_return(ret);) - -// Shift packed 16-bit integers in a right by count while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srl_epi16 -FORCE_INLINE __m128i _mm_srl_epi16(__m128i a, __m128i count) -{ - uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); - if (_sse2neon_unlikely(c & ~15)) - return _mm_setzero_si128(); - - int16x8_t vc = vdupq_n_s16(-(int16_t) c); - return vreinterpretq_m128i_u16(vshlq_u16(vreinterpretq_u16_m128i(a), vc)); -} - -// Shift packed 32-bit integers in a right by count while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srl_epi32 -FORCE_INLINE __m128i _mm_srl_epi32(__m128i a, __m128i count) -{ - uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); - if (_sse2neon_unlikely(c & ~31)) - return _mm_setzero_si128(); - - int32x4_t vc = vdupq_n_s32(-(int32_t) c); - return vreinterpretq_m128i_u32(vshlq_u32(vreinterpretq_u32_m128i(a), vc)); -} - -// Shift packed 64-bit integers in a right by count while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srl_epi64 -FORCE_INLINE __m128i _mm_srl_epi64(__m128i a, __m128i count) -{ - uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); - if (_sse2neon_unlikely(c & ~63)) - return _mm_setzero_si128(); - - int64x2_t vc = vdupq_n_s64(-(int64_t) c); - return vreinterpretq_m128i_u64(vshlq_u64(vreinterpretq_u64_m128i(a), vc)); -} - -// Shift packed 16-bit integers in a right by imm8 while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_epi16 -#define _mm_srli_epi16(a, imm) \ - _sse2neon_define0( \ - __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) & ~15)) { \ - ret = _mm_setzero_si128(); \ - } else { \ - ret = vreinterpretq_m128i_u16( \ - vshlq_u16(vreinterpretq_u16_m128i(_a), vdupq_n_s16(-(imm)))); \ - } _sse2neon_return(ret);) - -// Shift packed 32-bit integers in a right by imm8 while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_epi32 -// FORCE_INLINE __m128i _mm_srli_epi32(__m128i a, __constrange(0,255) int imm) -#define _mm_srli_epi32(a, imm) \ - _sse2neon_define0( \ - __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) & ~31)) { \ - ret = _mm_setzero_si128(); \ - } else { \ - ret = vreinterpretq_m128i_u32( \ - vshlq_u32(vreinterpretq_u32_m128i(_a), vdupq_n_s32(-(imm)))); \ - } _sse2neon_return(ret);) - -// Shift packed 64-bit integers in a right by imm8 while shifting in zeros, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_epi64 -#define _mm_srli_epi64(a, imm) \ - _sse2neon_define0( \ - __m128i, a, __m128i ret; if (_sse2neon_unlikely((imm) & ~63)) { \ - ret = _mm_setzero_si128(); \ - } else { \ - ret = vreinterpretq_m128i_u64( \ - vshlq_u64(vreinterpretq_u64_m128i(_a), vdupq_n_s64(-(imm)))); \ - } _sse2neon_return(ret);) - -// Shift a right by imm8 bytes while shifting in zeros, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_srli_si128 -#define _mm_srli_si128(a, imm) \ - _sse2neon_define1( \ - __m128i, a, int8x16_t ret; \ - if (_sse2neon_unlikely((imm) & ~15)) ret = vdupq_n_s8(0); \ - else ret = vextq_s8(vreinterpretq_s8_m128i(_a), vdupq_n_s8(0), \ - (imm > 15 ? 0 : imm)); \ - _sse2neon_return(vreinterpretq_m128i_s8(ret));) - -// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point -// elements) from a into memory. mem_addr must be aligned on a 16-byte boundary -// or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_pd -FORCE_INLINE void _mm_store_pd(double *mem_addr, __m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - vst1q_f64((float64_t *) mem_addr, vreinterpretq_f64_m128d(a)); -#else - vst1q_f32((float32_t *) mem_addr, vreinterpretq_f32_m128d(a)); -#endif -} - -// Store the lower double-precision (64-bit) floating-point element from a into -// 2 contiguous elements in memory. mem_addr must be aligned on a 16-byte -// boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_pd1 -FORCE_INLINE void _mm_store_pd1(double *mem_addr, __m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - float64x1_t a_low = vget_low_f64(vreinterpretq_f64_m128d(a)); - vst1q_f64((float64_t *) mem_addr, - vreinterpretq_f64_m128d(vcombine_f64(a_low, a_low))); -#else - float32x2_t a_low = vget_low_f32(vreinterpretq_f32_m128d(a)); - vst1q_f32((float32_t *) mem_addr, - vreinterpretq_f32_m128d(vcombine_f32(a_low, a_low))); -#endif -} - -// Store the lower double-precision (64-bit) floating-point element from a into -// memory. mem_addr does not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_store_sd -FORCE_INLINE void _mm_store_sd(double *mem_addr, __m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - vst1_f64((float64_t *) mem_addr, vget_low_f64(vreinterpretq_f64_m128d(a))); -#else - vst1_u64((uint64_t *) mem_addr, vget_low_u64(vreinterpretq_u64_m128d(a))); -#endif -} - -// Store 128-bits of integer data from a into memory. mem_addr must be aligned -// on a 16-byte boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_store_si128 -FORCE_INLINE void _mm_store_si128(__m128i *p, __m128i a) -{ - vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); -} - -// Store the lower double-precision (64-bit) floating-point element from a into -// 2 contiguous elements in memory. mem_addr must be aligned on a 16-byte -// boundary or a general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#expand=9,526,5601&text=_mm_store1_pd -#define _mm_store1_pd _mm_store_pd1 - -// Store the upper double-precision (64-bit) floating-point element from a into -// memory. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeh_pd -FORCE_INLINE void _mm_storeh_pd(double *mem_addr, __m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - vst1_f64((float64_t *) mem_addr, vget_high_f64(vreinterpretq_f64_m128d(a))); -#else - vst1_f32((float32_t *) mem_addr, vget_high_f32(vreinterpretq_f32_m128d(a))); -#endif -} - -// Store 64-bit integer from the first element of a into memory. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storel_epi64 -FORCE_INLINE void _mm_storel_epi64(__m128i *a, __m128i b) -{ - vst1_u64((uint64_t *) a, vget_low_u64(vreinterpretq_u64_m128i(b))); -} - -// Store the lower double-precision (64-bit) floating-point element from a into -// memory. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storel_pd -FORCE_INLINE void _mm_storel_pd(double *mem_addr, __m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - vst1_f64((float64_t *) mem_addr, vget_low_f64(vreinterpretq_f64_m128d(a))); -#else - vst1_f32((float32_t *) mem_addr, vget_low_f32(vreinterpretq_f32_m128d(a))); -#endif -} - -// Store 2 double-precision (64-bit) floating-point elements from a into memory -// in reverse order. mem_addr must be aligned on a 16-byte boundary or a -// general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storer_pd -FORCE_INLINE void _mm_storer_pd(double *mem_addr, __m128d a) -{ - float32x4_t f = vreinterpretq_f32_m128d(a); - _mm_store_pd(mem_addr, vreinterpretq_m128d_f32(vextq_f32(f, f, 2))); -} - -// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point -// elements) from a into memory. mem_addr does not need to be aligned on any -// particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_pd -FORCE_INLINE void _mm_storeu_pd(double *mem_addr, __m128d a) -{ - _mm_store_pd(mem_addr, a); -} - -// Store 128-bits of integer data from a into memory. mem_addr does not need to -// be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si128 -FORCE_INLINE void _mm_storeu_si128(__m128i *p, __m128i a) -{ - vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); -} - -// Store 32-bit integer from the first element of a into memory. mem_addr does -// not need to be aligned on any particular boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_storeu_si32 -FORCE_INLINE void _mm_storeu_si32(void *p, __m128i a) -{ - vst1q_lane_s32((int32_t *) p, vreinterpretq_s32_m128i(a), 0); -} - -// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point -// elements) from a into memory using a non-temporal memory hint. mem_addr must -// be aligned on a 16-byte boundary or a general-protection exception may be -// generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_pd -FORCE_INLINE void _mm_stream_pd(double *p, __m128d a) -{ -#if __has_builtin(__builtin_nontemporal_store) - __builtin_nontemporal_store(a, (__m128d *) p); -#elif defined(__aarch64__) || defined(_M_ARM64) - vst1q_f64(p, vreinterpretq_f64_m128d(a)); -#else - vst1q_s64((int64_t *) p, vreinterpretq_s64_m128d(a)); -#endif -} - -// Store 128-bits of integer data from a into memory using a non-temporal memory -// hint. mem_addr must be aligned on a 16-byte boundary or a general-protection -// exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_si128 -FORCE_INLINE void _mm_stream_si128(__m128i *p, __m128i a) -{ -#if __has_builtin(__builtin_nontemporal_store) - __builtin_nontemporal_store(a, p); -#else - vst1q_s64((int64_t *) p, vreinterpretq_s64_m128i(a)); -#endif -} - -// Store 32-bit integer a into memory using a non-temporal hint to minimize -// cache pollution. If the cache line containing address mem_addr is already in -// the cache, the cache will be updated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_si32 -FORCE_INLINE void _mm_stream_si32(int *p, int a) -{ - vst1q_lane_s32((int32_t *) p, vdupq_n_s32(a), 0); -} - -// Store 64-bit integer a into memory using a non-temporal hint to minimize -// cache pollution. If the cache line containing address mem_addr is already in -// the cache, the cache will be updated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_si64 -FORCE_INLINE void _mm_stream_si64(__int64 *p, __int64 a) -{ - vst1_s64((int64_t *) p, vdup_n_s64((int64_t) a)); -} - -// Subtract packed 16-bit integers in b from packed 16-bit integers in a, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi16 -FORCE_INLINE __m128i _mm_sub_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Subtract packed 32-bit integers in b from packed 32-bit integers in a, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi32 -FORCE_INLINE __m128i _mm_sub_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vsubq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Subtract packed 64-bit integers in b from packed 64-bit integers in a, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi64 -FORCE_INLINE __m128i _mm_sub_epi64(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s64( - vsubq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); -} - -// Subtract packed 8-bit integers in b from packed 8-bit integers in a, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_epi8 -FORCE_INLINE __m128i _mm_sub_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Subtract packed double-precision (64-bit) floating-point elements in b from -// packed double-precision (64-bit) floating-point elements in a, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_sub_pd -FORCE_INLINE __m128d _mm_sub_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vsubq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[2]; - c[0] = da[0] - db[0]; - c[1] = da[1] - db[1]; - return vld1q_f32((float32_t *) c); -#endif -} - -// Subtract the lower double-precision (64-bit) floating-point element in b from -// the lower double-precision (64-bit) floating-point element in a, store the -// result in the lower element of dst, and copy the upper element from a to the -// upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_sd -FORCE_INLINE __m128d _mm_sub_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_sub_pd(a, b)); -} - -// Subtract 64-bit integer b from 64-bit integer a, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sub_si64 -FORCE_INLINE __m64 _mm_sub_si64(__m64 a, __m64 b) -{ - return vreinterpret_m64_s64( - vsub_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); -} - -// Subtract packed signed 16-bit integers in b from packed 16-bit integers in a -// using saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epi16 -FORCE_INLINE __m128i _mm_subs_epi16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s16( - vqsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -} - -// Subtract packed signed 8-bit integers in b from packed 8-bit integers in a -// using saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epi8 -FORCE_INLINE __m128i _mm_subs_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vqsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Subtract packed unsigned 16-bit integers in b from packed unsigned 16-bit -// integers in a using saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epu16 -FORCE_INLINE __m128i _mm_subs_epu16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vqsubq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); -} - -// Subtract packed unsigned 8-bit integers in b from packed unsigned 8-bit -// integers in a using saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_subs_epu8 -FORCE_INLINE __m128i _mm_subs_epu8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8( - vqsubq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); -} - -#define _mm_ucomieq_sd _mm_comieq_sd -#define _mm_ucomige_sd _mm_comige_sd -#define _mm_ucomigt_sd _mm_comigt_sd -#define _mm_ucomile_sd _mm_comile_sd -#define _mm_ucomilt_sd _mm_comilt_sd -#define _mm_ucomineq_sd _mm_comineq_sd - -// Return vector of type __m128d with undefined elements. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_undefined_pd -FORCE_INLINE __m128d _mm_undefined_pd(void) -{ -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif - __m128d a; -#if defined(_MSC_VER) - a = _mm_setzero_pd(); -#endif - return a; -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif -} - -// Unpack and interleave 16-bit integers from the high half of a and b, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi16 -FORCE_INLINE __m128i _mm_unpackhi_epi16(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s16( - vzip2q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -#else - int16x4_t a1 = vget_high_s16(vreinterpretq_s16_m128i(a)); - int16x4_t b1 = vget_high_s16(vreinterpretq_s16_m128i(b)); - int16x4x2_t result = vzip_s16(a1, b1); - return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave 32-bit integers from the high half of a and b, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi32 -FORCE_INLINE __m128i _mm_unpackhi_epi32(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s32( - vzip2q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -#else - int32x2_t a1 = vget_high_s32(vreinterpretq_s32_m128i(a)); - int32x2_t b1 = vget_high_s32(vreinterpretq_s32_m128i(b)); - int32x2x2_t result = vzip_s32(a1, b1); - return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave 64-bit integers from the high half of a and b, and -// store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi64 -FORCE_INLINE __m128i _mm_unpackhi_epi64(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s64( - vzip2q_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); -#else - int64x1_t a_h = vget_high_s64(vreinterpretq_s64_m128i(a)); - int64x1_t b_h = vget_high_s64(vreinterpretq_s64_m128i(b)); - return vreinterpretq_m128i_s64(vcombine_s64(a_h, b_h)); -#endif -} - -// Unpack and interleave 8-bit integers from the high half of a and b, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_epi8 -FORCE_INLINE __m128i _mm_unpackhi_epi8(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s8( - vzip2q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -#else - int8x8_t a1 = - vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(a))); - int8x8_t b1 = - vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(b))); - int8x8x2_t result = vzip_s8(a1, b1); - return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave double-precision (64-bit) floating-point elements from -// the high half of a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpackhi_pd -FORCE_INLINE __m128d _mm_unpackhi_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vzip2q_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - return vreinterpretq_m128d_s64( - vcombine_s64(vget_high_s64(vreinterpretq_s64_m128d(a)), - vget_high_s64(vreinterpretq_s64_m128d(b)))); -#endif -} - -// Unpack and interleave 16-bit integers from the low half of a and b, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi16 -FORCE_INLINE __m128i _mm_unpacklo_epi16(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s16( - vzip1q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); -#else - int16x4_t a1 = vget_low_s16(vreinterpretq_s16_m128i(a)); - int16x4_t b1 = vget_low_s16(vreinterpretq_s16_m128i(b)); - int16x4x2_t result = vzip_s16(a1, b1); - return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave 32-bit integers from the low half of a and b, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi32 -FORCE_INLINE __m128i _mm_unpacklo_epi32(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s32( - vzip1q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -#else - int32x2_t a1 = vget_low_s32(vreinterpretq_s32_m128i(a)); - int32x2_t b1 = vget_low_s32(vreinterpretq_s32_m128i(b)); - int32x2x2_t result = vzip_s32(a1, b1); - return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave 64-bit integers from the low half of a and b, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi64 -FORCE_INLINE __m128i _mm_unpacklo_epi64(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s64( - vzip1q_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); -#else - int64x1_t a_l = vget_low_s64(vreinterpretq_s64_m128i(a)); - int64x1_t b_l = vget_low_s64(vreinterpretq_s64_m128i(b)); - return vreinterpretq_m128i_s64(vcombine_s64(a_l, b_l)); -#endif -} - -// Unpack and interleave 8-bit integers from the low half of a and b, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_epi8 -FORCE_INLINE __m128i _mm_unpacklo_epi8(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s8( - vzip1q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -#else - int8x8_t a1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(a))); - int8x8_t b1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(b))); - int8x8x2_t result = vzip_s8(a1, b1); - return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); -#endif -} - -// Unpack and interleave double-precision (64-bit) floating-point elements from -// the low half of a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_unpacklo_pd -FORCE_INLINE __m128d _mm_unpacklo_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vzip1q_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - return vreinterpretq_m128d_s64( - vcombine_s64(vget_low_s64(vreinterpretq_s64_m128d(a)), - vget_low_s64(vreinterpretq_s64_m128d(b)))); -#endif -} - -// Compute the bitwise XOR of packed double-precision (64-bit) floating-point -// elements in a and b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_xor_pd -FORCE_INLINE __m128d _mm_xor_pd(__m128d a, __m128d b) -{ - return vreinterpretq_m128d_s64( - veorq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); -} - -// Compute the bitwise XOR of 128 bits (representing integer data) in a and b, -// and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_xor_si128 -FORCE_INLINE __m128i _mm_xor_si128(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - veorq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -/* SSE3 */ - -// Alternatively add and subtract packed double-precision (64-bit) -// floating-point elements in a to/from packed elements in b, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_addsub_pd -FORCE_INLINE __m128d _mm_addsub_pd(__m128d a, __m128d b) -{ - _sse2neon_const __m128d mask = _mm_set_pd(1.0f, -1.0f); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vfmaq_f64(vreinterpretq_f64_m128d(a), - vreinterpretq_f64_m128d(b), - vreinterpretq_f64_m128d(mask))); -#else - return _mm_add_pd(_mm_mul_pd(b, mask), a); -#endif -} - -// Alternatively add and subtract packed single-precision (32-bit) -// floating-point elements in a to/from packed elements in b, and store the -// results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=addsub_ps -FORCE_INLINE __m128 _mm_addsub_ps(__m128 a, __m128 b) -{ - _sse2neon_const __m128 mask = _mm_setr_ps(-1.0f, 1.0f, -1.0f, 1.0f); -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_FMA) /* VFPv4+ */ - return vreinterpretq_m128_f32(vfmaq_f32(vreinterpretq_f32_m128(a), - vreinterpretq_f32_m128(mask), - vreinterpretq_f32_m128(b))); -#else - return _mm_add_ps(_mm_mul_ps(b, mask), a); -#endif -} - -// Horizontally add adjacent pairs of double-precision (64-bit) floating-point -// elements in a and b, and pack the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_pd -FORCE_INLINE __m128d _mm_hadd_pd(__m128d a, __m128d b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vpaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); -#else - double *da = (double *) &a; - double *db = (double *) &b; - double c[] = {da[0] + da[1], db[0] + db[1]}; - return vreinterpretq_m128d_u64(vld1q_u64((uint64_t *) c)); -#endif -} - -// Horizontally add adjacent pairs of single-precision (32-bit) floating-point -// elements in a and b, and pack the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_ps -FORCE_INLINE __m128 _mm_hadd_ps(__m128 a, __m128 b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vpaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); -#else - float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); - float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); - float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); - float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); - return vreinterpretq_m128_f32( - vcombine_f32(vpadd_f32(a10, a32), vpadd_f32(b10, b32))); -#endif -} - -// Horizontally subtract adjacent pairs of double-precision (64-bit) -// floating-point elements in a and b, and pack the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_pd -FORCE_INLINE __m128d _mm_hsub_pd(__m128d _a, __m128d _b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - float64x2_t a = vreinterpretq_f64_m128d(_a); - float64x2_t b = vreinterpretq_f64_m128d(_b); - return vreinterpretq_m128d_f64( - vsubq_f64(vuzp1q_f64(a, b), vuzp2q_f64(a, b))); -#else - double *da = (double *) &_a; - double *db = (double *) &_b; - double c[] = {da[0] - da[1], db[0] - db[1]}; - return vreinterpretq_m128d_u64(vld1q_u64((uint64_t *) c)); -#endif -} - -// Horizontally subtract adjacent pairs of single-precision (32-bit) -// floating-point elements in a and b, and pack the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_ps -FORCE_INLINE __m128 _mm_hsub_ps(__m128 _a, __m128 _b) -{ - float32x4_t a = vreinterpretq_f32_m128(_a); - float32x4_t b = vreinterpretq_f32_m128(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vsubq_f32(vuzp1q_f32(a, b), vuzp2q_f32(a, b))); -#else - float32x4x2_t c = vuzpq_f32(a, b); - return vreinterpretq_m128_f32(vsubq_f32(c.val[0], c.val[1])); -#endif -} - -// Load 128-bits of integer data from unaligned memory into dst. This intrinsic -// may perform better than _mm_loadu_si128 when the data crosses a cache line -// boundary. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_lddqu_si128 -#define _mm_lddqu_si128 _mm_loadu_si128 - -// Load a double-precision (64-bit) floating-point element from memory into both -// elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_loaddup_pd -#define _mm_loaddup_pd _mm_load1_pd - -// Duplicate the low double-precision (64-bit) floating-point element from a, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movedup_pd -FORCE_INLINE __m128d _mm_movedup_pd(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64( - vdupq_laneq_f64(vreinterpretq_f64_m128d(a), 0)); -#else - return vreinterpretq_m128d_u64( - vdupq_n_u64(vgetq_lane_u64(vreinterpretq_u64_m128d(a), 0))); -#endif -} - -// Duplicate odd-indexed single-precision (32-bit) floating-point elements -// from a, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_movehdup_ps -FORCE_INLINE __m128 _mm_movehdup_ps(__m128 a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vtrn2q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a))); -#elif defined(_sse2neon_shuffle) - return vreinterpretq_m128_f32(vshuffleq_s32( - vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 1, 1, 3, 3)); -#else - float32_t a1 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); - float32_t a3 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 3); - float ALIGN_STRUCT(16) data[4] = {a1, a1, a3, a3}; - return vreinterpretq_m128_f32(vld1q_f32(data)); -#endif -} - -// Duplicate even-indexed single-precision (32-bit) floating-point elements -// from a, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_moveldup_ps -FORCE_INLINE __m128 _mm_moveldup_ps(__m128 a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128_f32( - vtrn1q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a))); -#elif defined(_sse2neon_shuffle) - return vreinterpretq_m128_f32(vshuffleq_s32( - vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 0, 0, 2, 2)); -#else - float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); - float32_t a2 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 2); - float ALIGN_STRUCT(16) data[4] = {a0, a0, a2, a2}; - return vreinterpretq_m128_f32(vld1q_f32(data)); -#endif -} - -/* SSSE3 */ - -// Compute the absolute value of packed signed 16-bit integers in a, and store -// the unsigned results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_epi16 -FORCE_INLINE __m128i _mm_abs_epi16(__m128i a) -{ - return vreinterpretq_m128i_s16(vabsq_s16(vreinterpretq_s16_m128i(a))); -} - -// Compute the absolute value of packed signed 32-bit integers in a, and store -// the unsigned results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_epi32 -FORCE_INLINE __m128i _mm_abs_epi32(__m128i a) -{ - return vreinterpretq_m128i_s32(vabsq_s32(vreinterpretq_s32_m128i(a))); -} - -// Compute the absolute value of packed signed 8-bit integers in a, and store -// the unsigned results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_epi8 -FORCE_INLINE __m128i _mm_abs_epi8(__m128i a) -{ - return vreinterpretq_m128i_s8(vabsq_s8(vreinterpretq_s8_m128i(a))); -} - -// Compute the absolute value of packed signed 16-bit integers in a, and store -// the unsigned results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_pi16 -FORCE_INLINE __m64 _mm_abs_pi16(__m64 a) -{ - return vreinterpret_m64_s16(vabs_s16(vreinterpret_s16_m64(a))); -} - -// Compute the absolute value of packed signed 32-bit integers in a, and store -// the unsigned results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_pi32 -FORCE_INLINE __m64 _mm_abs_pi32(__m64 a) -{ - return vreinterpret_m64_s32(vabs_s32(vreinterpret_s32_m64(a))); -} - -// Compute the absolute value of packed signed 8-bit integers in a, and store -// the unsigned results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_abs_pi8 -FORCE_INLINE __m64 _mm_abs_pi8(__m64 a) -{ - return vreinterpret_m64_s8(vabs_s8(vreinterpret_s8_m64(a))); -} - -// Concatenate 16-byte blocks in a and b into a 32-byte temporary result, shift -// the result right by imm8 bytes, and store the low 16 bytes in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_alignr_epi8 -#if defined(__GNUC__) && !defined(__clang__) -#define _mm_alignr_epi8(a, b, imm) \ - __extension__({ \ - uint8x16_t _a = vreinterpretq_u8_m128i(a); \ - uint8x16_t _b = vreinterpretq_u8_m128i(b); \ - __m128i ret; \ - if (_sse2neon_unlikely((imm) & ~31)) \ - ret = vreinterpretq_m128i_u8(vdupq_n_u8(0)); \ - else if (imm >= 16) \ - ret = _mm_srli_si128(a, imm >= 16 ? imm - 16 : 0); \ - else \ - ret = \ - vreinterpretq_m128i_u8(vextq_u8(_b, _a, imm < 16 ? imm : 0)); \ - ret; \ - }) - -#else -#define _mm_alignr_epi8(a, b, imm) \ - _sse2neon_define2( \ - __m128i, a, b, uint8x16_t __a = vreinterpretq_u8_m128i(_a); \ - uint8x16_t __b = vreinterpretq_u8_m128i(_b); __m128i ret; \ - if (_sse2neon_unlikely((imm) & ~31)) ret = \ - vreinterpretq_m128i_u8(vdupq_n_u8(0)); \ - else if (imm >= 16) ret = \ - _mm_srli_si128(_a, imm >= 16 ? imm - 16 : 0); \ - else ret = \ - vreinterpretq_m128i_u8(vextq_u8(__b, __a, imm < 16 ? imm : 0)); \ - _sse2neon_return(ret);) - -#endif - -// Concatenate 8-byte blocks in a and b into a 16-byte temporary result, shift -// the result right by imm8 bytes, and store the low 8 bytes in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_alignr_pi8 -#define _mm_alignr_pi8(a, b, imm) \ - _sse2neon_define2( \ - __m64, a, b, __m64 ret; if (_sse2neon_unlikely((imm) >= 16)) { \ - ret = vreinterpret_m64_s8(vdup_n_s8(0)); \ - } else { \ - uint8x8_t tmp_low; \ - uint8x8_t tmp_high; \ - if ((imm) >= 8) { \ - const int idx = (imm) -8; \ - tmp_low = vreinterpret_u8_m64(_a); \ - tmp_high = vdup_n_u8(0); \ - ret = vreinterpret_m64_u8(vext_u8(tmp_low, tmp_high, idx)); \ - } else { \ - const int idx = (imm); \ - tmp_low = vreinterpret_u8_m64(_b); \ - tmp_high = vreinterpret_u8_m64(_a); \ - ret = vreinterpret_m64_u8(vext_u8(tmp_low, tmp_high, idx)); \ - } \ - } _sse2neon_return(ret);) - -// Horizontally add adjacent pairs of 16-bit integers in a and b, and pack the -// signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_epi16 -FORCE_INLINE __m128i _mm_hadd_epi16(__m128i _a, __m128i _b) -{ - int16x8_t a = vreinterpretq_s16_m128i(_a); - int16x8_t b = vreinterpretq_s16_m128i(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s16(vpaddq_s16(a, b)); -#else - return vreinterpretq_m128i_s16( - vcombine_s16(vpadd_s16(vget_low_s16(a), vget_high_s16(a)), - vpadd_s16(vget_low_s16(b), vget_high_s16(b)))); -#endif -} - -// Horizontally add adjacent pairs of 32-bit integers in a and b, and pack the -// signed 32-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_epi32 -FORCE_INLINE __m128i _mm_hadd_epi32(__m128i _a, __m128i _b) -{ - int32x4_t a = vreinterpretq_s32_m128i(_a); - int32x4_t b = vreinterpretq_s32_m128i(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s32(vpaddq_s32(a, b)); -#else - return vreinterpretq_m128i_s32( - vcombine_s32(vpadd_s32(vget_low_s32(a), vget_high_s32(a)), - vpadd_s32(vget_low_s32(b), vget_high_s32(b)))); -#endif -} - -// Horizontally add adjacent pairs of 16-bit integers in a and b, and pack the -// signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_pi16 -FORCE_INLINE __m64 _mm_hadd_pi16(__m64 a, __m64 b) -{ - return vreinterpret_m64_s16( - vpadd_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); -} - -// Horizontally add adjacent pairs of 32-bit integers in a and b, and pack the -// signed 32-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadd_pi32 -FORCE_INLINE __m64 _mm_hadd_pi32(__m64 a, __m64 b) -{ - return vreinterpret_m64_s32( - vpadd_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b))); -} - -// Horizontally add adjacent pairs of signed 16-bit integers in a and b using -// saturation, and pack the signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadds_epi16 -FORCE_INLINE __m128i _mm_hadds_epi16(__m128i _a, __m128i _b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - int16x8_t a = vreinterpretq_s16_m128i(_a); - int16x8_t b = vreinterpretq_s16_m128i(_b); - return vreinterpretq_s64_s16( - vqaddq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); -#else - int32x4_t a = vreinterpretq_s32_m128i(_a); - int32x4_t b = vreinterpretq_s32_m128i(_b); - // Interleave using vshrn/vmovn - // [a0|a2|a4|a6|b0|b2|b4|b6] - // [a1|a3|a5|a7|b1|b3|b5|b7] - int16x8_t ab0246 = vcombine_s16(vmovn_s32(a), vmovn_s32(b)); - int16x8_t ab1357 = vcombine_s16(vshrn_n_s32(a, 16), vshrn_n_s32(b, 16)); - // Saturated add - return vreinterpretq_m128i_s16(vqaddq_s16(ab0246, ab1357)); -#endif -} - -// Horizontally add adjacent pairs of signed 16-bit integers in a and b using -// saturation, and pack the signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hadds_pi16 -FORCE_INLINE __m64 _mm_hadds_pi16(__m64 _a, __m64 _b) -{ - int16x4_t a = vreinterpret_s16_m64(_a); - int16x4_t b = vreinterpret_s16_m64(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpret_s64_s16(vqadd_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); -#else - int16x4x2_t res = vuzp_s16(a, b); - return vreinterpret_s64_s16(vqadd_s16(res.val[0], res.val[1])); -#endif -} - -// Horizontally subtract adjacent pairs of 16-bit integers in a and b, and pack -// the signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_epi16 -FORCE_INLINE __m128i _mm_hsub_epi16(__m128i _a, __m128i _b) -{ - int16x8_t a = vreinterpretq_s16_m128i(_a); - int16x8_t b = vreinterpretq_s16_m128i(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s16( - vsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); -#else - int16x8x2_t c = vuzpq_s16(a, b); - return vreinterpretq_m128i_s16(vsubq_s16(c.val[0], c.val[1])); -#endif -} - -// Horizontally subtract adjacent pairs of 32-bit integers in a and b, and pack -// the signed 32-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_epi32 -FORCE_INLINE __m128i _mm_hsub_epi32(__m128i _a, __m128i _b) -{ - int32x4_t a = vreinterpretq_s32_m128i(_a); - int32x4_t b = vreinterpretq_s32_m128i(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s32( - vsubq_s32(vuzp1q_s32(a, b), vuzp2q_s32(a, b))); -#else - int32x4x2_t c = vuzpq_s32(a, b); - return vreinterpretq_m128i_s32(vsubq_s32(c.val[0], c.val[1])); -#endif -} - -// Horizontally subtract adjacent pairs of 16-bit integers in a and b, and pack -// the signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsub_pi16 -FORCE_INLINE __m64 _mm_hsub_pi16(__m64 _a, __m64 _b) -{ - int16x4_t a = vreinterpret_s16_m64(_a); - int16x4_t b = vreinterpret_s16_m64(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpret_m64_s16(vsub_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); -#else - int16x4x2_t c = vuzp_s16(a, b); - return vreinterpret_m64_s16(vsub_s16(c.val[0], c.val[1])); -#endif -} - -// Horizontally subtract adjacent pairs of 32-bit integers in a and b, and pack -// the signed 32-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_hsub_pi32 -FORCE_INLINE __m64 _mm_hsub_pi32(__m64 _a, __m64 _b) -{ - int32x2_t a = vreinterpret_s32_m64(_a); - int32x2_t b = vreinterpret_s32_m64(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpret_m64_s32(vsub_s32(vuzp1_s32(a, b), vuzp2_s32(a, b))); -#else - int32x2x2_t c = vuzp_s32(a, b); - return vreinterpret_m64_s32(vsub_s32(c.val[0], c.val[1])); -#endif -} - -// Horizontally subtract adjacent pairs of signed 16-bit integers in a and b -// using saturation, and pack the signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsubs_epi16 -FORCE_INLINE __m128i _mm_hsubs_epi16(__m128i _a, __m128i _b) -{ - int16x8_t a = vreinterpretq_s16_m128i(_a); - int16x8_t b = vreinterpretq_s16_m128i(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s16( - vqsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); -#else - int16x8x2_t c = vuzpq_s16(a, b); - return vreinterpretq_m128i_s16(vqsubq_s16(c.val[0], c.val[1])); -#endif -} - -// Horizontally subtract adjacent pairs of signed 16-bit integers in a and b -// using saturation, and pack the signed 16-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_hsubs_pi16 -FORCE_INLINE __m64 _mm_hsubs_pi16(__m64 _a, __m64 _b) -{ - int16x4_t a = vreinterpret_s16_m64(_a); - int16x4_t b = vreinterpret_s16_m64(_b); -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpret_m64_s16(vqsub_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); -#else - int16x4x2_t c = vuzp_s16(a, b); - return vreinterpret_m64_s16(vqsub_s16(c.val[0], c.val[1])); -#endif -} - -// Vertically multiply each unsigned 8-bit integer from a with the corresponding -// signed 8-bit integer from b, producing intermediate signed 16-bit integers. -// Horizontally add adjacent pairs of intermediate signed 16-bit integers, -// and pack the saturated results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16 -FORCE_INLINE __m128i _mm_maddubs_epi16(__m128i _a, __m128i _b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - uint8x16_t a = vreinterpretq_u8_m128i(_a); - int8x16_t b = vreinterpretq_s8_m128i(_b); - int16x8_t tl = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(a))), - vmovl_s8(vget_low_s8(b))); - int16x8_t th = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(a))), - vmovl_s8(vget_high_s8(b))); - return vreinterpretq_m128i_s16( - vqaddq_s16(vuzp1q_s16(tl, th), vuzp2q_s16(tl, th))); -#else - // This would be much simpler if x86 would choose to zero extend OR sign - // extend, not both. This could probably be optimized better. - uint16x8_t a = vreinterpretq_u16_m128i(_a); - int16x8_t b = vreinterpretq_s16_m128i(_b); - - // Zero extend a - int16x8_t a_odd = vreinterpretq_s16_u16(vshrq_n_u16(a, 8)); - int16x8_t a_even = vreinterpretq_s16_u16(vbicq_u16(a, vdupq_n_u16(0xff00))); - - // Sign extend by shifting left then shifting right. - int16x8_t b_even = vshrq_n_s16(vshlq_n_s16(b, 8), 8); - int16x8_t b_odd = vshrq_n_s16(b, 8); - - // multiply - int16x8_t prod1 = vmulq_s16(a_even, b_even); - int16x8_t prod2 = vmulq_s16(a_odd, b_odd); - - // saturated add - return vreinterpretq_m128i_s16(vqaddq_s16(prod1, prod2)); -#endif -} - -// Vertically multiply each unsigned 8-bit integer from a with the corresponding -// signed 8-bit integer from b, producing intermediate signed 16-bit integers. -// Horizontally add adjacent pairs of intermediate signed 16-bit integers, and -// pack the saturated results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_pi16 -FORCE_INLINE __m64 _mm_maddubs_pi16(__m64 _a, __m64 _b) -{ - uint16x4_t a = vreinterpret_u16_m64(_a); - int16x4_t b = vreinterpret_s16_m64(_b); - - // Zero extend a - int16x4_t a_odd = vreinterpret_s16_u16(vshr_n_u16(a, 8)); - int16x4_t a_even = vreinterpret_s16_u16(vand_u16(a, vdup_n_u16(0xff))); - - // Sign extend by shifting left then shifting right. - int16x4_t b_even = vshr_n_s16(vshl_n_s16(b, 8), 8); - int16x4_t b_odd = vshr_n_s16(b, 8); - - // multiply - int16x4_t prod1 = vmul_s16(a_even, b_even); - int16x4_t prod2 = vmul_s16(a_odd, b_odd); - - // saturated add - return vreinterpret_m64_s16(vqadd_s16(prod1, prod2)); -} - -// Multiply packed signed 16-bit integers in a and b, producing intermediate -// signed 32-bit integers. Shift right by 15 bits while rounding up, and store -// the packed 16-bit integers in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16 -FORCE_INLINE __m128i _mm_mulhrs_epi16(__m128i a, __m128i b) -{ - // Has issues due to saturation - // return vreinterpretq_m128i_s16(vqrdmulhq_s16(a, b)); - - // Multiply - int32x4_t mul_lo = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), - vget_low_s16(vreinterpretq_s16_m128i(b))); - int32x4_t mul_hi = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), - vget_high_s16(vreinterpretq_s16_m128i(b))); - - // Rounding narrowing shift right - // narrow = (int16_t)((mul + 16384) >> 15); - int16x4_t narrow_lo = vrshrn_n_s32(mul_lo, 15); - int16x4_t narrow_hi = vrshrn_n_s32(mul_hi, 15); - - // Join together - return vreinterpretq_m128i_s16(vcombine_s16(narrow_lo, narrow_hi)); -} - -// Multiply packed signed 16-bit integers in a and b, producing intermediate -// signed 32-bit integers. Truncate each intermediate integer to the 18 most -// significant bits, round by adding 1, and store bits [16:1] to dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_pi16 -FORCE_INLINE __m64 _mm_mulhrs_pi16(__m64 a, __m64 b) -{ - int32x4_t mul_extend = - vmull_s16((vreinterpret_s16_m64(a)), (vreinterpret_s16_m64(b))); - - // Rounding narrowing shift right - return vreinterpret_m64_s16(vrshrn_n_s32(mul_extend, 15)); -} - -// Shuffle packed 8-bit integers in a according to shuffle control mask in the -// corresponding 8-bit element of b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8 -FORCE_INLINE __m128i _mm_shuffle_epi8(__m128i a, __m128i b) -{ - int8x16_t tbl = vreinterpretq_s8_m128i(a); // input a - uint8x16_t idx = vreinterpretq_u8_m128i(b); // input b - uint8x16_t idx_masked = - vandq_u8(idx, vdupq_n_u8(0x8F)); // avoid using meaningless bits -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_s8(vqtbl1q_s8(tbl, idx_masked)); -#elif defined(__GNUC__) - int8x16_t ret; - // %e and %f represent the even and odd D registers - // respectively. - __asm__ __volatile__( - "vtbl.8 %e[ret], {%e[tbl], %f[tbl]}, %e[idx]\n" - "vtbl.8 %f[ret], {%e[tbl], %f[tbl]}, %f[idx]\n" - : [ret] "=&w"(ret) - : [tbl] "w"(tbl), [idx] "w"(idx_masked)); - return vreinterpretq_m128i_s8(ret); -#else - // use this line if testing on aarch64 - int8x8x2_t a_split = {vget_low_s8(tbl), vget_high_s8(tbl)}; - return vreinterpretq_m128i_s8( - vcombine_s8(vtbl2_s8(a_split, vget_low_u8(idx_masked)), - vtbl2_s8(a_split, vget_high_u8(idx_masked)))); -#endif -} - -// Shuffle packed 8-bit integers in a according to shuffle control mask in the -// corresponding 8-bit element of b, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_pi8 -FORCE_INLINE __m64 _mm_shuffle_pi8(__m64 a, __m64 b) -{ - const int8x8_t controlMask = - vand_s8(vreinterpret_s8_m64(b), vdup_n_s8((int8_t) (0x1 << 7 | 0x07))); - int8x8_t res = vtbl1_s8(vreinterpret_s8_m64(a), controlMask); - return vreinterpret_m64_s8(res); -} - -// Negate packed 16-bit integers in a when the corresponding signed -// 16-bit integer in b is negative, and store the results in dst. -// Element in dst are zeroed out when the corresponding element -// in b is zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_epi16 -FORCE_INLINE __m128i _mm_sign_epi16(__m128i _a, __m128i _b) -{ - int16x8_t a = vreinterpretq_s16_m128i(_a); - int16x8_t b = vreinterpretq_s16_m128i(_b); - - // signed shift right: faster than vclt - // (b < 0) ? 0xFFFF : 0 - uint16x8_t ltMask = vreinterpretq_u16_s16(vshrq_n_s16(b, 15)); - // (b == 0) ? 0xFFFF : 0 -#if defined(__aarch64__) || defined(_M_ARM64) - int16x8_t zeroMask = vreinterpretq_s16_u16(vceqzq_s16(b)); -#else - int16x8_t zeroMask = vreinterpretq_s16_u16(vceqq_s16(b, vdupq_n_s16(0))); -#endif - - // bitwise select either a or negative 'a' (vnegq_s16(a) equals to negative - // 'a') based on ltMask - int16x8_t masked = vbslq_s16(ltMask, vnegq_s16(a), a); - // res = masked & (~zeroMask) - int16x8_t res = vbicq_s16(masked, zeroMask); - return vreinterpretq_m128i_s16(res); -} - -// Negate packed 32-bit integers in a when the corresponding signed -// 32-bit integer in b is negative, and store the results in dst. -// Element in dst are zeroed out when the corresponding element -// in b is zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_epi32 -FORCE_INLINE __m128i _mm_sign_epi32(__m128i _a, __m128i _b) -{ - int32x4_t a = vreinterpretq_s32_m128i(_a); - int32x4_t b = vreinterpretq_s32_m128i(_b); - - // signed shift right: faster than vclt - // (b < 0) ? 0xFFFFFFFF : 0 - uint32x4_t ltMask = vreinterpretq_u32_s32(vshrq_n_s32(b, 31)); - - // (b == 0) ? 0xFFFFFFFF : 0 -#if defined(__aarch64__) || defined(_M_ARM64) - int32x4_t zeroMask = vreinterpretq_s32_u32(vceqzq_s32(b)); -#else - int32x4_t zeroMask = vreinterpretq_s32_u32(vceqq_s32(b, vdupq_n_s32(0))); -#endif - - // bitwise select either a or negative 'a' (vnegq_s32(a) equals to negative - // 'a') based on ltMask - int32x4_t masked = vbslq_s32(ltMask, vnegq_s32(a), a); - // res = masked & (~zeroMask) - int32x4_t res = vbicq_s32(masked, zeroMask); - return vreinterpretq_m128i_s32(res); -} - -// Negate packed 8-bit integers in a when the corresponding signed -// 8-bit integer in b is negative, and store the results in dst. -// Element in dst are zeroed out when the corresponding element -// in b is zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_epi8 -FORCE_INLINE __m128i _mm_sign_epi8(__m128i _a, __m128i _b) -{ - int8x16_t a = vreinterpretq_s8_m128i(_a); - int8x16_t b = vreinterpretq_s8_m128i(_b); - - // signed shift right: faster than vclt - // (b < 0) ? 0xFF : 0 - uint8x16_t ltMask = vreinterpretq_u8_s8(vshrq_n_s8(b, 7)); - - // (b == 0) ? 0xFF : 0 -#if defined(__aarch64__) || defined(_M_ARM64) - int8x16_t zeroMask = vreinterpretq_s8_u8(vceqzq_s8(b)); -#else - int8x16_t zeroMask = vreinterpretq_s8_u8(vceqq_s8(b, vdupq_n_s8(0))); -#endif - - // bitwise select either a or negative 'a' (vnegq_s8(a) return negative 'a') - // based on ltMask - int8x16_t masked = vbslq_s8(ltMask, vnegq_s8(a), a); - // res = masked & (~zeroMask) - int8x16_t res = vbicq_s8(masked, zeroMask); - - return vreinterpretq_m128i_s8(res); -} - -// Negate packed 16-bit integers in a when the corresponding signed 16-bit -// integer in b is negative, and store the results in dst. Element in dst are -// zeroed out when the corresponding element in b is zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_pi16 -FORCE_INLINE __m64 _mm_sign_pi16(__m64 _a, __m64 _b) -{ - int16x4_t a = vreinterpret_s16_m64(_a); - int16x4_t b = vreinterpret_s16_m64(_b); - - // signed shift right: faster than vclt - // (b < 0) ? 0xFFFF : 0 - uint16x4_t ltMask = vreinterpret_u16_s16(vshr_n_s16(b, 15)); - - // (b == 0) ? 0xFFFF : 0 -#if defined(__aarch64__) || defined(_M_ARM64) - int16x4_t zeroMask = vreinterpret_s16_u16(vceqz_s16(b)); -#else - int16x4_t zeroMask = vreinterpret_s16_u16(vceq_s16(b, vdup_n_s16(0))); -#endif - - // bitwise select either a or negative 'a' (vneg_s16(a) return negative 'a') - // based on ltMask - int16x4_t masked = vbsl_s16(ltMask, vneg_s16(a), a); - // res = masked & (~zeroMask) - int16x4_t res = vbic_s16(masked, zeroMask); - - return vreinterpret_m64_s16(res); -} - -// Negate packed 32-bit integers in a when the corresponding signed 32-bit -// integer in b is negative, and store the results in dst. Element in dst are -// zeroed out when the corresponding element in b is zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_pi32 -FORCE_INLINE __m64 _mm_sign_pi32(__m64 _a, __m64 _b) -{ - int32x2_t a = vreinterpret_s32_m64(_a); - int32x2_t b = vreinterpret_s32_m64(_b); - - // signed shift right: faster than vclt - // (b < 0) ? 0xFFFFFFFF : 0 - uint32x2_t ltMask = vreinterpret_u32_s32(vshr_n_s32(b, 31)); - - // (b == 0) ? 0xFFFFFFFF : 0 -#if defined(__aarch64__) || defined(_M_ARM64) - int32x2_t zeroMask = vreinterpret_s32_u32(vceqz_s32(b)); -#else - int32x2_t zeroMask = vreinterpret_s32_u32(vceq_s32(b, vdup_n_s32(0))); -#endif - - // bitwise select either a or negative 'a' (vneg_s32(a) return negative 'a') - // based on ltMask - int32x2_t masked = vbsl_s32(ltMask, vneg_s32(a), a); - // res = masked & (~zeroMask) - int32x2_t res = vbic_s32(masked, zeroMask); - - return vreinterpret_m64_s32(res); -} - -// Negate packed 8-bit integers in a when the corresponding signed 8-bit integer -// in b is negative, and store the results in dst. Element in dst are zeroed out -// when the corresponding element in b is zero. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sign_pi8 -FORCE_INLINE __m64 _mm_sign_pi8(__m64 _a, __m64 _b) -{ - int8x8_t a = vreinterpret_s8_m64(_a); - int8x8_t b = vreinterpret_s8_m64(_b); - - // signed shift right: faster than vclt - // (b < 0) ? 0xFF : 0 - uint8x8_t ltMask = vreinterpret_u8_s8(vshr_n_s8(b, 7)); - - // (b == 0) ? 0xFF : 0 -#if defined(__aarch64__) || defined(_M_ARM64) - int8x8_t zeroMask = vreinterpret_s8_u8(vceqz_s8(b)); -#else - int8x8_t zeroMask = vreinterpret_s8_u8(vceq_s8(b, vdup_n_s8(0))); -#endif - - // bitwise select either a or negative 'a' (vneg_s8(a) return negative 'a') - // based on ltMask - int8x8_t masked = vbsl_s8(ltMask, vneg_s8(a), a); - // res = masked & (~zeroMask) - int8x8_t res = vbic_s8(masked, zeroMask); - - return vreinterpret_m64_s8(res); -} - -/* SSE4.1 */ - -// Blend packed 16-bit integers from a and b using control mask imm8, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blend_epi16 -// FORCE_INLINE __m128i _mm_blend_epi16(__m128i a, __m128i b, -// __constrange(0,255) int imm) -#define _mm_blend_epi16(a, b, imm) \ - _sse2neon_define2( \ - __m128i, a, b, \ - const uint16_t _mask[8] = \ - _sse2neon_init(((imm) & (1 << 0)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 1)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 2)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 3)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 4)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 5)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 6)) ? (uint16_t) -1 : 0x0, \ - ((imm) & (1 << 7)) ? (uint16_t) -1 : 0x0); \ - uint16x8_t _mask_vec = vld1q_u16(_mask); \ - uint16x8_t __a = vreinterpretq_u16_m128i(_a); \ - uint16x8_t __b = vreinterpretq_u16_m128i(_b); _sse2neon_return( \ - vreinterpretq_m128i_u16(vbslq_u16(_mask_vec, __b, __a)));) - -// Blend packed double-precision (64-bit) floating-point elements from a and b -// using control mask imm8, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blend_pd -#define _mm_blend_pd(a, b, imm) \ - _sse2neon_define2( \ - __m128d, a, b, \ - const uint64_t _mask[2] = \ - _sse2neon_init(((imm) & (1 << 0)) ? ~UINT64_C(0) : UINT64_C(0), \ - ((imm) & (1 << 1)) ? ~UINT64_C(0) : UINT64_C(0)); \ - uint64x2_t _mask_vec = vld1q_u64(_mask); \ - uint64x2_t __a = vreinterpretq_u64_m128d(_a); \ - uint64x2_t __b = vreinterpretq_u64_m128d(_b); _sse2neon_return( \ - vreinterpretq_m128d_u64(vbslq_u64(_mask_vec, __b, __a)));) - -// Blend packed single-precision (32-bit) floating-point elements from a and b -// using mask, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blend_ps -FORCE_INLINE __m128 _mm_blend_ps(__m128 _a, __m128 _b, const char imm8) -{ - const uint32_t ALIGN_STRUCT(16) - data[4] = {((imm8) & (1 << 0)) ? UINT32_MAX : 0, - ((imm8) & (1 << 1)) ? UINT32_MAX : 0, - ((imm8) & (1 << 2)) ? UINT32_MAX : 0, - ((imm8) & (1 << 3)) ? UINT32_MAX : 0}; - uint32x4_t mask = vld1q_u32(data); - float32x4_t a = vreinterpretq_f32_m128(_a); - float32x4_t b = vreinterpretq_f32_m128(_b); - return vreinterpretq_m128_f32(vbslq_f32(mask, b, a)); -} - -// Blend packed 8-bit integers from a and b using mask, and store the results in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_epi8 -FORCE_INLINE __m128i _mm_blendv_epi8(__m128i _a, __m128i _b, __m128i _mask) -{ - // Use a signed shift right to create a mask with the sign bit - uint8x16_t mask = - vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_m128i(_mask), 7)); - uint8x16_t a = vreinterpretq_u8_m128i(_a); - uint8x16_t b = vreinterpretq_u8_m128i(_b); - return vreinterpretq_m128i_u8(vbslq_u8(mask, b, a)); -} - -// Blend packed double-precision (64-bit) floating-point elements from a and b -// using mask, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_pd -FORCE_INLINE __m128d _mm_blendv_pd(__m128d _a, __m128d _b, __m128d _mask) -{ - uint64x2_t mask = - vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_m128d(_mask), 63)); -#if defined(__aarch64__) || defined(_M_ARM64) - float64x2_t a = vreinterpretq_f64_m128d(_a); - float64x2_t b = vreinterpretq_f64_m128d(_b); - return vreinterpretq_m128d_f64(vbslq_f64(mask, b, a)); -#else - uint64x2_t a = vreinterpretq_u64_m128d(_a); - uint64x2_t b = vreinterpretq_u64_m128d(_b); - return vreinterpretq_m128d_u64(vbslq_u64(mask, b, a)); -#endif -} - -// Blend packed single-precision (32-bit) floating-point elements from a and b -// using mask, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_ps -FORCE_INLINE __m128 _mm_blendv_ps(__m128 _a, __m128 _b, __m128 _mask) -{ - // Use a signed shift right to create a mask with the sign bit - uint32x4_t mask = - vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_m128(_mask), 31)); - float32x4_t a = vreinterpretq_f32_m128(_a); - float32x4_t b = vreinterpretq_f32_m128(_b); - return vreinterpretq_m128_f32(vbslq_f32(mask, b, a)); -} - -// Round the packed double-precision (64-bit) floating-point elements in a up -// to an integer value, and store the results as packed double-precision -// floating-point elements in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_pd -FORCE_INLINE __m128d _mm_ceil_pd(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vrndpq_f64(vreinterpretq_f64_m128d(a))); -#else - double *f = (double *) &a; - return _mm_set_pd(ceil(f[1]), ceil(f[0])); -#endif -} - -// Round the packed single-precision (32-bit) floating-point elements in a up to -// an integer value, and store the results as packed single-precision -// floating-point elements in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_ps -FORCE_INLINE __m128 _mm_ceil_ps(__m128 a) -{ -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - return vreinterpretq_m128_f32(vrndpq_f32(vreinterpretq_f32_m128(a))); -#else - float *f = (float *) &a; - return _mm_set_ps(ceilf(f[3]), ceilf(f[2]), ceilf(f[1]), ceilf(f[0])); -#endif -} - -// Round the lower double-precision (64-bit) floating-point element in b up to -// an integer value, store the result as a double-precision floating-point -// element in the lower element of dst, and copy the upper element from a to the -// upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_sd -FORCE_INLINE __m128d _mm_ceil_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_ceil_pd(b)); -} - -// Round the lower single-precision (32-bit) floating-point element in b up to -// an integer value, store the result as a single-precision floating-point -// element in the lower element of dst, and copy the upper 3 packed elements -// from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_ceil_ss -FORCE_INLINE __m128 _mm_ceil_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_ceil_ps(b)); -} - -// Compare packed 64-bit integers in a and b for equality, and store the results -// in dst -FORCE_INLINE __m128i _mm_cmpeq_epi64(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_u64( - vceqq_u64(vreinterpretq_u64_m128i(a), vreinterpretq_u64_m128i(b))); -#else - // ARMv7 lacks vceqq_u64 - // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) - uint32x4_t cmp = - vceqq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b)); - uint32x4_t swapped = vrev64q_u32(cmp); - return vreinterpretq_m128i_u32(vandq_u32(cmp, swapped)); -#endif -} - -// Sign extend packed 16-bit integers in a to packed 32-bit integers, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi16_epi32 -FORCE_INLINE __m128i _mm_cvtepi16_epi32(__m128i a) -{ - return vreinterpretq_m128i_s32( - vmovl_s16(vget_low_s16(vreinterpretq_s16_m128i(a)))); -} - -// Sign extend packed 16-bit integers in a to packed 64-bit integers, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi16_epi64 -FORCE_INLINE __m128i _mm_cvtepi16_epi64(__m128i a) -{ - int16x8_t s16x8 = vreinterpretq_s16_m128i(a); /* xxxx xxxx xxxx 0B0A */ - int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ - int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ - return vreinterpretq_m128i_s64(s64x2); -} - -// Sign extend packed 32-bit integers in a to packed 64-bit integers, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi32_epi64 -FORCE_INLINE __m128i _mm_cvtepi32_epi64(__m128i a) -{ - return vreinterpretq_m128i_s64( - vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a)))); -} - -// Sign extend packed 8-bit integers in a to packed 16-bit integers, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi8_epi16 -FORCE_INLINE __m128i _mm_cvtepi8_epi16(__m128i a) -{ - int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ - int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ - return vreinterpretq_m128i_s16(s16x8); -} - -// Sign extend packed 8-bit integers in a to packed 32-bit integers, and store -// the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi8_epi32 -FORCE_INLINE __m128i _mm_cvtepi8_epi32(__m128i a) -{ - int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ - int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ - int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000D 000C 000B 000A */ - return vreinterpretq_m128i_s32(s32x4); -} - -// Sign extend packed 8-bit integers in the low 8 bytes of a to packed 64-bit -// integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepi8_epi64 -FORCE_INLINE __m128i _mm_cvtepi8_epi64(__m128i a) -{ - int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx xxBA */ - int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0x0x 0B0A */ - int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ - int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ - return vreinterpretq_m128i_s64(s64x2); -} - -// Zero extend packed unsigned 16-bit integers in a to packed 32-bit integers, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu16_epi32 -FORCE_INLINE __m128i _mm_cvtepu16_epi32(__m128i a) -{ - return vreinterpretq_m128i_u32( - vmovl_u16(vget_low_u16(vreinterpretq_u16_m128i(a)))); -} - -// Zero extend packed unsigned 16-bit integers in a to packed 64-bit integers, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu16_epi64 -FORCE_INLINE __m128i _mm_cvtepu16_epi64(__m128i a) -{ - uint16x8_t u16x8 = vreinterpretq_u16_m128i(a); /* xxxx xxxx xxxx 0B0A */ - uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ - uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ - return vreinterpretq_m128i_u64(u64x2); -} - -// Zero extend packed unsigned 32-bit integers in a to packed 64-bit integers, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu32_epi64 -FORCE_INLINE __m128i _mm_cvtepu32_epi64(__m128i a) -{ - return vreinterpretq_m128i_u64( - vmovl_u32(vget_low_u32(vreinterpretq_u32_m128i(a)))); -} - -// Zero extend packed unsigned 8-bit integers in a to packed 16-bit integers, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu8_epi16 -FORCE_INLINE __m128i _mm_cvtepu8_epi16(__m128i a) -{ - uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx HGFE DCBA */ - uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0H0G 0F0E 0D0C 0B0A */ - return vreinterpretq_m128i_u16(u16x8); -} - -// Zero extend packed unsigned 8-bit integers in a to packed 32-bit integers, -// and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu8_epi32 -FORCE_INLINE __m128i _mm_cvtepu8_epi32(__m128i a) -{ - uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx DCBA */ - uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0D0C 0B0A */ - uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000D 000C 000B 000A */ - return vreinterpretq_m128i_u32(u32x4); -} - -// Zero extend packed unsigned 8-bit integers in the low 8 bytes of a to packed -// 64-bit integers, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtepu8_epi64 -FORCE_INLINE __m128i _mm_cvtepu8_epi64(__m128i a) -{ - uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx xxBA */ - uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0x0x 0B0A */ - uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ - uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ - return vreinterpretq_m128i_u64(u64x2); -} - -// Conditionally multiply the packed double-precision (64-bit) floating-point -// elements in a and b using the high 4 bits in imm8, sum the four products, and -// conditionally store the sum in dst using the low 4 bits of imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_dp_pd -FORCE_INLINE __m128d _mm_dp_pd(__m128d a, __m128d b, const int imm) -{ - // Generate mask value from constant immediate bit value - const int64_t bit0Mask = imm & 0x01 ? UINT64_MAX : 0; - const int64_t bit1Mask = imm & 0x02 ? UINT64_MAX : 0; -#if !SSE2NEON_PRECISE_DP - const int64_t bit4Mask = imm & 0x10 ? UINT64_MAX : 0; - const int64_t bit5Mask = imm & 0x20 ? UINT64_MAX : 0; -#endif - // Conditional multiplication -#if !SSE2NEON_PRECISE_DP - __m128d mul = _mm_mul_pd(a, b); - const __m128d mulMask = - _mm_castsi128_pd(_mm_set_epi64x(bit5Mask, bit4Mask)); - __m128d tmp = _mm_and_pd(mul, mulMask); -#else -#if defined(__aarch64__) || defined(_M_ARM64) - double d0 = (imm & 0x10) ? vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0) * - vgetq_lane_f64(vreinterpretq_f64_m128d(b), 0) - : 0; - double d1 = (imm & 0x20) ? vgetq_lane_f64(vreinterpretq_f64_m128d(a), 1) * - vgetq_lane_f64(vreinterpretq_f64_m128d(b), 1) - : 0; -#else - double d0 = (imm & 0x10) ? ((double *) &a)[0] * ((double *) &b)[0] : 0; - double d1 = (imm & 0x20) ? ((double *) &a)[1] * ((double *) &b)[1] : 0; -#endif - __m128d tmp = _mm_set_pd(d1, d0); -#endif - // Sum the products -#if defined(__aarch64__) || defined(_M_ARM64) - double sum = vpaddd_f64(vreinterpretq_f64_m128d(tmp)); -#else - double sum = *((double *) &tmp) + *(((double *) &tmp) + 1); -#endif - // Conditionally store the sum - const __m128d sumMask = - _mm_castsi128_pd(_mm_set_epi64x(bit1Mask, bit0Mask)); - __m128d res = _mm_and_pd(_mm_set_pd1(sum), sumMask); - return res; -} - -// Conditionally multiply the packed single-precision (32-bit) floating-point -// elements in a and b using the high 4 bits in imm8, sum the four products, -// and conditionally store the sum in dst using the low 4 bits of imm. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_dp_ps -FORCE_INLINE __m128 _mm_dp_ps(__m128 a, __m128 b, const int imm) -{ - float32x4_t elementwise_prod = _mm_mul_ps(a, b); - -#if defined(__aarch64__) || defined(_M_ARM64) - /* shortcuts */ - if (imm == 0xFF) { - return _mm_set1_ps(vaddvq_f32(elementwise_prod)); - } - - if ((imm & 0x0F) == 0x0F) { - if (!(imm & (1 << 4))) - elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 0); - if (!(imm & (1 << 5))) - elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 1); - if (!(imm & (1 << 6))) - elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 2); - if (!(imm & (1 << 7))) - elementwise_prod = vsetq_lane_f32(0.0f, elementwise_prod, 3); - - return _mm_set1_ps(vaddvq_f32(elementwise_prod)); - } -#endif - - float s = 0.0f; - - if (imm & (1 << 4)) - s += vgetq_lane_f32(elementwise_prod, 0); - if (imm & (1 << 5)) - s += vgetq_lane_f32(elementwise_prod, 1); - if (imm & (1 << 6)) - s += vgetq_lane_f32(elementwise_prod, 2); - if (imm & (1 << 7)) - s += vgetq_lane_f32(elementwise_prod, 3); - - const float32_t res[4] = { - (imm & 0x1) ? s : 0.0f, - (imm & 0x2) ? s : 0.0f, - (imm & 0x4) ? s : 0.0f, - (imm & 0x8) ? s : 0.0f, - }; - return vreinterpretq_m128_f32(vld1q_f32(res)); -} - -// Extract a 32-bit integer from a, selected with imm8, and store the result in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi32 -// FORCE_INLINE int _mm_extract_epi32(__m128i a, __constrange(0,4) int imm) -#define _mm_extract_epi32(a, imm) \ - vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)) - -// Extract a 64-bit integer from a, selected with imm8, and store the result in -// dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi64 -// FORCE_INLINE __int64 _mm_extract_epi64(__m128i a, __constrange(0,2) int imm) -#define _mm_extract_epi64(a, imm) \ - vgetq_lane_s64(vreinterpretq_s64_m128i(a), (imm)) - -// Extract an 8-bit integer from a, selected with imm8, and store the result in -// the lower element of dst. FORCE_INLINE int _mm_extract_epi8(__m128i a, -// __constrange(0,16) int imm) -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_extract_epi8 -#define _mm_extract_epi8(a, imm) vgetq_lane_u8(vreinterpretq_u8_m128i(a), (imm)) - -// Extracts the selected single-precision (32-bit) floating-point from a. -// FORCE_INLINE int _mm_extract_ps(__m128 a, __constrange(0,4) int imm) -#define _mm_extract_ps(a, imm) vgetq_lane_s32(vreinterpretq_s32_m128(a), (imm)) - -// Round the packed double-precision (64-bit) floating-point elements in a down -// to an integer value, and store the results as packed double-precision -// floating-point elements in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_pd -FORCE_INLINE __m128d _mm_floor_pd(__m128d a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128d_f64(vrndmq_f64(vreinterpretq_f64_m128d(a))); -#else - double *f = (double *) &a; - return _mm_set_pd(floor(f[1]), floor(f[0])); -#endif -} - -// Round the packed single-precision (32-bit) floating-point elements in a down -// to an integer value, and store the results as packed single-precision -// floating-point elements in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_ps -FORCE_INLINE __m128 _mm_floor_ps(__m128 a) -{ -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - return vreinterpretq_m128_f32(vrndmq_f32(vreinterpretq_f32_m128(a))); -#else - float *f = (float *) &a; - return _mm_set_ps(floorf(f[3]), floorf(f[2]), floorf(f[1]), floorf(f[0])); -#endif -} - -// Round the lower double-precision (64-bit) floating-point element in b down to -// an integer value, store the result as a double-precision floating-point -// element in the lower element of dst, and copy the upper element from a to the -// upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_sd -FORCE_INLINE __m128d _mm_floor_sd(__m128d a, __m128d b) -{ - return _mm_move_sd(a, _mm_floor_pd(b)); -} - -// Round the lower single-precision (32-bit) floating-point element in b down to -// an integer value, store the result as a single-precision floating-point -// element in the lower element of dst, and copy the upper 3 packed elements -// from a to the upper elements of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_floor_ss -FORCE_INLINE __m128 _mm_floor_ss(__m128 a, __m128 b) -{ - return _mm_move_ss(a, _mm_floor_ps(b)); -} - -// Copy a to dst, and insert the 32-bit integer i into dst at the location -// specified by imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi32 -// FORCE_INLINE __m128i _mm_insert_epi32(__m128i a, int b, -// __constrange(0,4) int imm) -#define _mm_insert_epi32(a, b, imm) \ - vreinterpretq_m128i_s32( \ - vsetq_lane_s32((b), vreinterpretq_s32_m128i(a), (imm))) - -// Copy a to dst, and insert the 64-bit integer i into dst at the location -// specified by imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi64 -// FORCE_INLINE __m128i _mm_insert_epi64(__m128i a, __int64 b, -// __constrange(0,2) int imm) -#define _mm_insert_epi64(a, b, imm) \ - vreinterpretq_m128i_s64( \ - vsetq_lane_s64((b), vreinterpretq_s64_m128i(a), (imm))) - -// Copy a to dst, and insert the lower 8-bit integer from i into dst at the -// location specified by imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_insert_epi8 -// FORCE_INLINE __m128i _mm_insert_epi8(__m128i a, int b, -// __constrange(0,16) int imm) -#define _mm_insert_epi8(a, b, imm) \ - vreinterpretq_m128i_s8(vsetq_lane_s8((b), vreinterpretq_s8_m128i(a), (imm))) - -// Copy a to tmp, then insert a single-precision (32-bit) floating-point -// element from b into tmp using the control in imm8. Store tmp to dst using -// the mask in imm8 (elements are zeroed out when the corresponding bit is set). -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=insert_ps -#define _mm_insert_ps(a, b, imm8) \ - _sse2neon_define2( \ - __m128, a, b, \ - float32x4_t tmp1 = \ - vsetq_lane_f32(vgetq_lane_f32(_b, (imm8 >> 6) & 0x3), \ - vreinterpretq_f32_m128(_a), 0); \ - float32x4_t tmp2 = \ - vsetq_lane_f32(vgetq_lane_f32(tmp1, 0), \ - vreinterpretq_f32_m128(_a), ((imm8 >> 4) & 0x3)); \ - const uint32_t data[4] = \ - _sse2neon_init(((imm8) & (1 << 0)) ? UINT32_MAX : 0, \ - ((imm8) & (1 << 1)) ? UINT32_MAX : 0, \ - ((imm8) & (1 << 2)) ? UINT32_MAX : 0, \ - ((imm8) & (1 << 3)) ? UINT32_MAX : 0); \ - uint32x4_t mask = vld1q_u32(data); \ - float32x4_t all_zeros = vdupq_n_f32(0); \ - \ - _sse2neon_return(vreinterpretq_m128_f32( \ - vbslq_f32(mask, all_zeros, vreinterpretq_f32_m128(tmp2))));) - -// Compare packed signed 32-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epi32 -FORCE_INLINE __m128i _mm_max_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vmaxq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Compare packed signed 8-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epi8 -FORCE_INLINE __m128i _mm_max_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vmaxq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Compare packed unsigned 16-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu16 -FORCE_INLINE __m128i _mm_max_epu16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vmaxq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); -} - -// Compare packed unsigned 32-bit integers in a and b, and store packed maximum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu32 -FORCE_INLINE __m128i _mm_max_epu32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u32( - vmaxq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); -} - -// Compare packed signed 32-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epi32 -FORCE_INLINE __m128i _mm_min_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vminq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Compare packed signed 8-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epi8 -FORCE_INLINE __m128i _mm_min_epi8(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s8( - vminq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); -} - -// Compare packed unsigned 16-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_min_epu16 -FORCE_INLINE __m128i _mm_min_epu16(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vminq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); -} - -// Compare packed unsigned 32-bit integers in a and b, and store packed minimum -// values in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_epu32 -FORCE_INLINE __m128i _mm_min_epu32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u32( - vminq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); -} - -// Horizontally compute the minimum amongst the packed unsigned 16-bit integers -// in a, store the minimum and index in dst, and zero the remaining bits in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_minpos_epu16 -FORCE_INLINE __m128i _mm_minpos_epu16(__m128i a) -{ - __m128i dst; - uint16_t min, idx = 0; -#if defined(__aarch64__) || defined(_M_ARM64) - // Find the minimum value - min = vminvq_u16(vreinterpretq_u16_m128i(a)); - - // Get the index of the minimum value - static const uint16_t idxv[] = {0, 1, 2, 3, 4, 5, 6, 7}; - uint16x8_t minv = vdupq_n_u16(min); - uint16x8_t cmeq = vceqq_u16(minv, vreinterpretq_u16_m128i(a)); - idx = vminvq_u16(vornq_u16(vld1q_u16(idxv), cmeq)); -#else - // Find the minimum value - __m64 tmp; - tmp = vreinterpret_m64_u16( - vmin_u16(vget_low_u16(vreinterpretq_u16_m128i(a)), - vget_high_u16(vreinterpretq_u16_m128i(a)))); - tmp = vreinterpret_m64_u16( - vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); - tmp = vreinterpret_m64_u16( - vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); - min = vget_lane_u16(vreinterpret_u16_m64(tmp), 0); - // Get the index of the minimum value - int i; - for (i = 0; i < 8; i++) { - if (min == vgetq_lane_u16(vreinterpretq_u16_m128i(a), 0)) { - idx = (uint16_t) i; - break; - } - a = _mm_srli_si128(a, 2); - } -#endif - // Generate result - dst = _mm_setzero_si128(); - dst = vreinterpretq_m128i_u16( - vsetq_lane_u16(min, vreinterpretq_u16_m128i(dst), 0)); - dst = vreinterpretq_m128i_u16( - vsetq_lane_u16(idx, vreinterpretq_u16_m128i(dst), 1)); - return dst; -} - -// Compute the sum of absolute differences (SADs) of quadruplets of unsigned -// 8-bit integers in a compared to those in b, and store the 16-bit results in -// dst. Eight SADs are performed using one quadruplet from b and eight -// quadruplets from a. One quadruplet is selected from b starting at on the -// offset specified in imm8. Eight quadruplets are formed from sequential 8-bit -// integers selected from a starting at the offset specified in imm8. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8 -FORCE_INLINE __m128i _mm_mpsadbw_epu8(__m128i a, __m128i b, const int imm) -{ - uint8x16_t _a, _b; - - switch (imm & 0x4) { - case 0: - // do nothing - _a = vreinterpretq_u8_m128i(a); - break; - case 4: - _a = vreinterpretq_u8_u32(vextq_u32(vreinterpretq_u32_m128i(a), - vreinterpretq_u32_m128i(a), 1)); - break; - default: -#if defined(__GNUC__) || defined(__clang__) - __builtin_unreachable(); -#elif defined(_MSC_VER) - __assume(0); -#endif - break; - } - - switch (imm & 0x3) { - case 0: - _b = vreinterpretq_u8_u32( - vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 0))); - break; - case 1: - _b = vreinterpretq_u8_u32( - vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 1))); - break; - case 2: - _b = vreinterpretq_u8_u32( - vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 2))); - break; - case 3: - _b = vreinterpretq_u8_u32( - vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 3))); - break; - default: -#if defined(__GNUC__) || defined(__clang__) - __builtin_unreachable(); -#elif defined(_MSC_VER) - __assume(0); -#endif - break; - } - - int16x8_t c04, c15, c26, c37; - uint8x8_t low_b = vget_low_u8(_b); - c04 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a), low_b)); - uint8x16_t _a_1 = vextq_u8(_a, _a, 1); - c15 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a_1), low_b)); - uint8x16_t _a_2 = vextq_u8(_a, _a, 2); - c26 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a_2), low_b)); - uint8x16_t _a_3 = vextq_u8(_a, _a, 3); - c37 = vreinterpretq_s16_u16(vabdl_u8(vget_low_u8(_a_3), low_b)); -#if defined(__aarch64__) || defined(_M_ARM64) - // |0|4|2|6| - c04 = vpaddq_s16(c04, c26); - // |1|5|3|7| - c15 = vpaddq_s16(c15, c37); - - int32x4_t trn1_c = - vtrn1q_s32(vreinterpretq_s32_s16(c04), vreinterpretq_s32_s16(c15)); - int32x4_t trn2_c = - vtrn2q_s32(vreinterpretq_s32_s16(c04), vreinterpretq_s32_s16(c15)); - return vreinterpretq_m128i_s16(vpaddq_s16(vreinterpretq_s16_s32(trn1_c), - vreinterpretq_s16_s32(trn2_c))); -#else - int16x4_t c01, c23, c45, c67; - c01 = vpadd_s16(vget_low_s16(c04), vget_low_s16(c15)); - c23 = vpadd_s16(vget_low_s16(c26), vget_low_s16(c37)); - c45 = vpadd_s16(vget_high_s16(c04), vget_high_s16(c15)); - c67 = vpadd_s16(vget_high_s16(c26), vget_high_s16(c37)); - - return vreinterpretq_m128i_s16( - vcombine_s16(vpadd_s16(c01, c23), vpadd_s16(c45, c67))); -#endif -} - -// Multiply the low signed 32-bit integers from each packed 64-bit element in -// a and b, and store the signed 64-bit results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mul_epi32 -FORCE_INLINE __m128i _mm_mul_epi32(__m128i a, __m128i b) -{ - // vmull_s32 upcasts instead of masking, so we downcast. - int32x2_t a_lo = vmovn_s64(vreinterpretq_s64_m128i(a)); - int32x2_t b_lo = vmovn_s64(vreinterpretq_s64_m128i(b)); - return vreinterpretq_m128i_s64(vmull_s32(a_lo, b_lo)); -} - -// Multiply the packed 32-bit integers in a and b, producing intermediate 64-bit -// integers, and store the low 32 bits of the intermediate integers in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mullo_epi32 -FORCE_INLINE __m128i _mm_mullo_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_s32( - vmulq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - -// Convert packed signed 32-bit integers from a and b to packed 16-bit integers -// using unsigned saturation, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi32 -FORCE_INLINE __m128i _mm_packus_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u16( - vcombine_u16(vqmovun_s32(vreinterpretq_s32_m128i(a)), - vqmovun_s32(vreinterpretq_s32_m128i(b)))); -} - -// Round the packed double-precision (64-bit) floating-point elements in a using -// the rounding parameter, and store the results as packed double-precision -// floating-point elements in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_round_pd -FORCE_INLINE __m128d _mm_round_pd(__m128d a, int rounding) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - switch (rounding) { - case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): - return vreinterpretq_m128d_f64(vrndnq_f64(vreinterpretq_f64_m128d(a))); - case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): - return _mm_floor_pd(a); - case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): - return _mm_ceil_pd(a); - case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): - return vreinterpretq_m128d_f64(vrndq_f64(vreinterpretq_f64_m128d(a))); - default: //_MM_FROUND_CUR_DIRECTION - return vreinterpretq_m128d_f64(vrndiq_f64(vreinterpretq_f64_m128d(a))); - } -#else - double *v_double = (double *) &a; - - if (rounding == (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC) || - (rounding == _MM_FROUND_CUR_DIRECTION && - _MM_GET_ROUNDING_MODE() == _MM_ROUND_NEAREST)) { - double res[2], tmp; - for (int i = 0; i < 2; i++) { - tmp = (v_double[i] < 0) ? -v_double[i] : v_double[i]; - double roundDown = floor(tmp); // Round down value - double roundUp = ceil(tmp); // Round up value - double diffDown = tmp - roundDown; - double diffUp = roundUp - tmp; - if (diffDown < diffUp) { - /* If it's closer to the round down value, then use it */ - res[i] = roundDown; - } else if (diffDown > diffUp) { - /* If it's closer to the round up value, then use it */ - res[i] = roundUp; - } else { - /* If it's equidistant between round up and round down value, - * pick the one which is an even number */ - double half = roundDown / 2; - if (half != floor(half)) { - /* If the round down value is odd, return the round up value - */ - res[i] = roundUp; - } else { - /* If the round up value is odd, return the round down value - */ - res[i] = roundDown; - } - } - res[i] = (v_double[i] < 0) ? -res[i] : res[i]; - } - return _mm_set_pd(res[1], res[0]); - } else if (rounding == (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) || - (rounding == _MM_FROUND_CUR_DIRECTION && - _MM_GET_ROUNDING_MODE() == _MM_ROUND_DOWN)) { - return _mm_floor_pd(a); - } else if (rounding == (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) || - (rounding == _MM_FROUND_CUR_DIRECTION && - _MM_GET_ROUNDING_MODE() == _MM_ROUND_UP)) { - return _mm_ceil_pd(a); - } - return _mm_set_pd(v_double[1] > 0 ? floor(v_double[1]) : ceil(v_double[1]), - v_double[0] > 0 ? floor(v_double[0]) : ceil(v_double[0])); -#endif -} - -// Round the packed single-precision (32-bit) floating-point elements in a using -// the rounding parameter, and store the results as packed single-precision -// floating-point elements in dst. -// software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_ps -FORCE_INLINE __m128 _mm_round_ps(__m128 a, int rounding) -{ -#if (defined(__aarch64__) || defined(_M_ARM64)) || \ - defined(__ARM_FEATURE_DIRECTED_ROUNDING) - switch (rounding) { - case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): - return vreinterpretq_m128_f32(vrndnq_f32(vreinterpretq_f32_m128(a))); - case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): - return _mm_floor_ps(a); - case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): - return _mm_ceil_ps(a); - case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): - return vreinterpretq_m128_f32(vrndq_f32(vreinterpretq_f32_m128(a))); - default: //_MM_FROUND_CUR_DIRECTION - return vreinterpretq_m128_f32(vrndiq_f32(vreinterpretq_f32_m128(a))); - } -#else - float *v_float = (float *) &a; - - if (rounding == (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC) || - (rounding == _MM_FROUND_CUR_DIRECTION && - _MM_GET_ROUNDING_MODE() == _MM_ROUND_NEAREST)) { - uint32x4_t signmask = vdupq_n_u32(0x80000000); - float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), - vdupq_n_f32(0.5f)); /* +/- 0.5 */ - int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( - vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ - int32x4_t r_trunc = vcvtq_s32_f32( - vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ - int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( - vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ - int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), - vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ - float32x4_t delta = vsubq_f32( - vreinterpretq_f32_m128(a), - vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ - uint32x4_t is_delta_half = - vceqq_f32(delta, half); /* delta == +/- 0.5 */ - return vreinterpretq_m128_f32( - vcvtq_f32_s32(vbslq_s32(is_delta_half, r_even, r_normal))); - } else if (rounding == (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) || - (rounding == _MM_FROUND_CUR_DIRECTION && - _MM_GET_ROUNDING_MODE() == _MM_ROUND_DOWN)) { - return _mm_floor_ps(a); - } else if (rounding == (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) || - (rounding == _MM_FROUND_CUR_DIRECTION && - _MM_GET_ROUNDING_MODE() == _MM_ROUND_UP)) { - return _mm_ceil_ps(a); - } - return _mm_set_ps(v_float[3] > 0 ? floorf(v_float[3]) : ceilf(v_float[3]), - v_float[2] > 0 ? floorf(v_float[2]) : ceilf(v_float[2]), - v_float[1] > 0 ? floorf(v_float[1]) : ceilf(v_float[1]), - v_float[0] > 0 ? floorf(v_float[0]) : ceilf(v_float[0])); -#endif -} - -// Round the lower double-precision (64-bit) floating-point element in b using -// the rounding parameter, store the result as a double-precision floating-point -// element in the lower element of dst, and copy the upper element from a to the -// upper element of dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_round_sd -FORCE_INLINE __m128d _mm_round_sd(__m128d a, __m128d b, int rounding) -{ - return _mm_move_sd(a, _mm_round_pd(b, rounding)); -} - -// Round the lower single-precision (32-bit) floating-point element in b using -// the rounding parameter, store the result as a single-precision floating-point -// element in the lower element of dst, and copy the upper 3 packed elements -// from a to the upper elements of dst. Rounding is done according to the -// rounding[3:0] parameter, which can be one of: -// (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC) // round to nearest, and -// suppress exceptions -// (_MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC) // round down, and -// suppress exceptions -// (_MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC) // round up, and suppress -// exceptions -// (_MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC) // truncate, and suppress -// exceptions _MM_FROUND_CUR_DIRECTION // use MXCSR.RC; see -// _MM_SET_ROUNDING_MODE -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_round_ss -FORCE_INLINE __m128 _mm_round_ss(__m128 a, __m128 b, int rounding) -{ - return _mm_move_ss(a, _mm_round_ps(b, rounding)); -} - -// Load 128-bits of integer data from memory into dst using a non-temporal -// memory hint. mem_addr must be aligned on a 16-byte boundary or a -// general-protection exception may be generated. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_stream_load_si128 -FORCE_INLINE __m128i _mm_stream_load_si128(__m128i *p) -{ -#if __has_builtin(__builtin_nontemporal_store) - return __builtin_nontemporal_load(p); -#else - return vreinterpretq_m128i_s64(vld1q_s64((int64_t *) p)); -#endif -} - -// Compute the bitwise NOT of a and then AND with a 128-bit vector containing -// all 1's, and return 1 if the result is zero, otherwise return 0. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_test_all_ones -FORCE_INLINE int _mm_test_all_ones(__m128i a) -{ - return (uint64_t) (vgetq_lane_s64(a, 0) & vgetq_lane_s64(a, 1)) == - ~(uint64_t) 0; -} - -// Compute the bitwise AND of 128 bits (representing integer data) in a and -// mask, and return 1 if the result is zero, otherwise return 0. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_test_all_zeros -FORCE_INLINE int _mm_test_all_zeros(__m128i a, __m128i mask) -{ - int64x2_t a_and_mask = - vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(mask)); - return !(vgetq_lane_s64(a_and_mask, 0) | vgetq_lane_s64(a_and_mask, 1)); -} - -// Compute the bitwise AND of 128 bits (representing integer data) in a and -// mask, and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute -// the bitwise NOT of a and then AND with mask, and set CF to 1 if the result is -// zero, otherwise set CF to 0. Return 1 if both the ZF and CF values are zero, -// otherwise return 0. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=mm_test_mix_ones_zero -// Note: Argument names may be wrong in the Intel intrinsics guide. -FORCE_INLINE int _mm_test_mix_ones_zeros(__m128i a, __m128i mask) -{ - uint64x2_t v = vreinterpretq_u64_m128i(a); - uint64x2_t m = vreinterpretq_u64_m128i(mask); - - // find ones (set-bits) and zeros (clear-bits) under clip mask - uint64x2_t ones = vandq_u64(m, v); - uint64x2_t zeros = vbicq_u64(m, v); - - // If both 128-bit variables are populated (non-zero) then return 1. - // For comparision purposes, first compact each var down to 32-bits. - uint32x2_t reduced = vpmax_u32(vqmovn_u64(ones), vqmovn_u64(zeros)); - - // if folding minimum is non-zero then both vars must be non-zero - return (vget_lane_u32(vpmin_u32(reduced, reduced), 0) != 0); -} - -// Compute the bitwise AND of 128 bits (representing integer data) in a and b, -// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the -// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, -// otherwise set CF to 0. Return the CF value. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testc_si128 -FORCE_INLINE int _mm_testc_si128(__m128i a, __m128i b) -{ - int64x2_t s64 = - vbicq_s64(vreinterpretq_s64_m128i(b), vreinterpretq_s64_m128i(a)); - return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); -} - -// Compute the bitwise AND of 128 bits (representing integer data) in a and b, -// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the -// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, -// otherwise set CF to 0. Return 1 if both the ZF and CF values are zero, -// otherwise return 0. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testnzc_si128 -#define _mm_testnzc_si128(a, b) _mm_test_mix_ones_zeros(a, b) - -// Compute the bitwise AND of 128 bits (representing integer data) in a and b, -// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the -// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, -// otherwise set CF to 0. Return the ZF value. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testz_si128 -FORCE_INLINE int _mm_testz_si128(__m128i a, __m128i b) -{ - int64x2_t s64 = - vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b)); - return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); -} - -/* SSE4.2 */ - -static const uint16_t ALIGN_STRUCT(16) _sse2neon_cmpestr_mask16b[8] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, -}; -static const uint8_t ALIGN_STRUCT(16) _sse2neon_cmpestr_mask8b[16] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, -}; - -/* specify the source data format */ -#define _SIDD_UBYTE_OPS 0x00 /* unsigned 8-bit characters */ -#define _SIDD_UWORD_OPS 0x01 /* unsigned 16-bit characters */ -#define _SIDD_SBYTE_OPS 0x02 /* signed 8-bit characters */ -#define _SIDD_SWORD_OPS 0x03 /* signed 16-bit characters */ - -/* specify the comparison operation */ -#define _SIDD_CMP_EQUAL_ANY 0x00 /* compare equal any: strchr */ -#define _SIDD_CMP_RANGES 0x04 /* compare ranges */ -#define _SIDD_CMP_EQUAL_EACH 0x08 /* compare equal each: strcmp */ -#define _SIDD_CMP_EQUAL_ORDERED 0x0C /* compare equal ordered */ - -/* specify the polarity */ -#define _SIDD_POSITIVE_POLARITY 0x00 -#define _SIDD_MASKED_POSITIVE_POLARITY 0x20 -#define _SIDD_NEGATIVE_POLARITY 0x10 /* negate results */ -#define _SIDD_MASKED_NEGATIVE_POLARITY \ - 0x30 /* negate results only before end of string */ - -/* specify the output selection in _mm_cmpXstri */ -#define _SIDD_LEAST_SIGNIFICANT 0x00 -#define _SIDD_MOST_SIGNIFICANT 0x40 - -/* specify the output selection in _mm_cmpXstrm */ -#define _SIDD_BIT_MASK 0x00 -#define _SIDD_UNIT_MASK 0x40 - -/* Pattern Matching for C macros. - * https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms - */ - -/* catenate */ -#define SSE2NEON_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ -#define SSE2NEON_CAT(a, b) SSE2NEON_PRIMITIVE_CAT(a, b) - -#define SSE2NEON_IIF(c) SSE2NEON_PRIMITIVE_CAT(SSE2NEON_IIF_, c) -/* run the 2nd parameter */ -#define SSE2NEON_IIF_0(t, ...) __VA_ARGS__ -/* run the 1st parameter */ -#define SSE2NEON_IIF_1(t, ...) t - -#define SSE2NEON_COMPL(b) SSE2NEON_PRIMITIVE_CAT(SSE2NEON_COMPL_, b) -#define SSE2NEON_COMPL_0 1 -#define SSE2NEON_COMPL_1 0 - -#define SSE2NEON_DEC(x) SSE2NEON_PRIMITIVE_CAT(SSE2NEON_DEC_, x) -#define SSE2NEON_DEC_1 0 -#define SSE2NEON_DEC_2 1 -#define SSE2NEON_DEC_3 2 -#define SSE2NEON_DEC_4 3 -#define SSE2NEON_DEC_5 4 -#define SSE2NEON_DEC_6 5 -#define SSE2NEON_DEC_7 6 -#define SSE2NEON_DEC_8 7 -#define SSE2NEON_DEC_9 8 -#define SSE2NEON_DEC_10 9 -#define SSE2NEON_DEC_11 10 -#define SSE2NEON_DEC_12 11 -#define SSE2NEON_DEC_13 12 -#define SSE2NEON_DEC_14 13 -#define SSE2NEON_DEC_15 14 -#define SSE2NEON_DEC_16 15 - -/* detection */ -#define SSE2NEON_CHECK_N(x, n, ...) n -#define SSE2NEON_CHECK(...) SSE2NEON_CHECK_N(__VA_ARGS__, 0, ) -#define SSE2NEON_PROBE(x) x, 1, - -#define SSE2NEON_NOT(x) SSE2NEON_CHECK(SSE2NEON_PRIMITIVE_CAT(SSE2NEON_NOT_, x)) -#define SSE2NEON_NOT_0 SSE2NEON_PROBE(~) - -#define SSE2NEON_BOOL(x) SSE2NEON_COMPL(SSE2NEON_NOT(x)) -#define SSE2NEON_IF(c) SSE2NEON_IIF(SSE2NEON_BOOL(c)) - -#define SSE2NEON_EAT(...) -#define SSE2NEON_EXPAND(...) __VA_ARGS__ -#define SSE2NEON_WHEN(c) SSE2NEON_IF(c)(SSE2NEON_EXPAND, SSE2NEON_EAT) - -/* recursion */ -/* deferred expression */ -#define SSE2NEON_EMPTY() -#define SSE2NEON_DEFER(id) id SSE2NEON_EMPTY() -#define SSE2NEON_OBSTRUCT(...) __VA_ARGS__ SSE2NEON_DEFER(SSE2NEON_EMPTY)() -#define SSE2NEON_EXPAND(...) __VA_ARGS__ - -#define SSE2NEON_EVAL(...) \ - SSE2NEON_EVAL1(SSE2NEON_EVAL1(SSE2NEON_EVAL1(__VA_ARGS__))) -#define SSE2NEON_EVAL1(...) \ - SSE2NEON_EVAL2(SSE2NEON_EVAL2(SSE2NEON_EVAL2(__VA_ARGS__))) -#define SSE2NEON_EVAL2(...) \ - SSE2NEON_EVAL3(SSE2NEON_EVAL3(SSE2NEON_EVAL3(__VA_ARGS__))) -#define SSE2NEON_EVAL3(...) __VA_ARGS__ - -#define SSE2NEON_REPEAT(count, macro, ...) \ - SSE2NEON_WHEN(count) \ - (SSE2NEON_OBSTRUCT(SSE2NEON_REPEAT_INDIRECT)()( \ - SSE2NEON_DEC(count), macro, \ - __VA_ARGS__) SSE2NEON_OBSTRUCT(macro)(SSE2NEON_DEC(count), \ - __VA_ARGS__)) -#define SSE2NEON_REPEAT_INDIRECT() SSE2NEON_REPEAT - -#define SSE2NEON_SIZE_OF_byte 8 -#define SSE2NEON_NUMBER_OF_LANES_byte 16 -#define SSE2NEON_SIZE_OF_word 16 -#define SSE2NEON_NUMBER_OF_LANES_word 8 - -#define SSE2NEON_COMPARE_EQUAL_THEN_FILL_LANE(i, type) \ - mtx[i] = vreinterpretq_m128i_##type(vceqq_##type( \ - vdupq_n_##type(vgetq_lane_##type(vreinterpretq_##type##_m128i(b), i)), \ - vreinterpretq_##type##_m128i(a))); - -#define SSE2NEON_FILL_LANE(i, type) \ - vec_b[i] = \ - vdupq_n_##type(vgetq_lane_##type(vreinterpretq_##type##_m128i(b), i)); - -#define PCMPSTR_RANGES(a, b, mtx, data_type_prefix, type_prefix, size, \ - number_of_lanes, byte_or_word) \ - do { \ - SSE2NEON_CAT( \ - data_type_prefix, \ - SSE2NEON_CAT(size, \ - SSE2NEON_CAT(x, SSE2NEON_CAT(number_of_lanes, _t)))) \ - vec_b[number_of_lanes]; \ - __m128i mask = SSE2NEON_IIF(byte_or_word)( \ - vreinterpretq_m128i_u16(vdupq_n_u16(0xff)), \ - vreinterpretq_m128i_u32(vdupq_n_u32(0xffff))); \ - SSE2NEON_EVAL(SSE2NEON_REPEAT(number_of_lanes, SSE2NEON_FILL_LANE, \ - SSE2NEON_CAT(type_prefix, size))) \ - for (int i = 0; i < number_of_lanes; i++) { \ - mtx[i] = SSE2NEON_CAT(vreinterpretq_m128i_u, \ - size)(SSE2NEON_CAT(vbslq_u, size)( \ - SSE2NEON_CAT(vreinterpretq_u, \ - SSE2NEON_CAT(size, _m128i))(mask), \ - SSE2NEON_CAT(vcgeq_, SSE2NEON_CAT(type_prefix, size))( \ - vec_b[i], \ - SSE2NEON_CAT( \ - vreinterpretq_, \ - SSE2NEON_CAT(type_prefix, \ - SSE2NEON_CAT(size, _m128i(a))))), \ - SSE2NEON_CAT(vcleq_, SSE2NEON_CAT(type_prefix, size))( \ - vec_b[i], \ - SSE2NEON_CAT( \ - vreinterpretq_, \ - SSE2NEON_CAT(type_prefix, \ - SSE2NEON_CAT(size, _m128i(a))))))); \ - } \ - } while (0) - -#define PCMPSTR_EQ(a, b, mtx, size, number_of_lanes) \ - do { \ - SSE2NEON_EVAL(SSE2NEON_REPEAT(number_of_lanes, \ - SSE2NEON_COMPARE_EQUAL_THEN_FILL_LANE, \ - SSE2NEON_CAT(u, size))) \ - } while (0) - -#define SSE2NEON_CMP_EQUAL_ANY_IMPL(type) \ - static int _sse2neon_cmp_##type##_equal_any(__m128i a, int la, __m128i b, \ - int lb) \ - { \ - __m128i mtx[16]; \ - PCMPSTR_EQ(a, b, mtx, SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ - SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type)); \ - return SSE2NEON_CAT( \ - _sse2neon_aggregate_equal_any_, \ - SSE2NEON_CAT( \ - SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ - SSE2NEON_CAT(x, SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, \ - type))))(la, lb, mtx); \ - } - -#define SSE2NEON_CMP_RANGES_IMPL(type, data_type, us, byte_or_word) \ - static int _sse2neon_cmp_##us##type##_ranges(__m128i a, int la, __m128i b, \ - int lb) \ - { \ - __m128i mtx[16]; \ - PCMPSTR_RANGES( \ - a, b, mtx, data_type, us, SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ - SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type), byte_or_word); \ - return SSE2NEON_CAT( \ - _sse2neon_aggregate_ranges_, \ - SSE2NEON_CAT( \ - SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ - SSE2NEON_CAT(x, SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, \ - type))))(la, lb, mtx); \ - } - -#define SSE2NEON_CMP_EQUAL_ORDERED_IMPL(type) \ - static int _sse2neon_cmp_##type##_equal_ordered(__m128i a, int la, \ - __m128i b, int lb) \ - { \ - __m128i mtx[16]; \ - PCMPSTR_EQ(a, b, mtx, SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ - SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type)); \ - return SSE2NEON_CAT( \ - _sse2neon_aggregate_equal_ordered_, \ - SSE2NEON_CAT( \ - SSE2NEON_CAT(SSE2NEON_SIZE_OF_, type), \ - SSE2NEON_CAT(x, \ - SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type))))( \ - SSE2NEON_CAT(SSE2NEON_NUMBER_OF_LANES_, type), la, lb, mtx); \ - } - -static int _sse2neon_aggregate_equal_any_8x16(int la, int lb, __m128i mtx[16]) -{ - int res = 0; - int m = (1 << la) - 1; - uint8x8_t vec_mask = vld1_u8(_sse2neon_cmpestr_mask8b); - uint8x8_t t_lo = vtst_u8(vdup_n_u8(m & 0xff), vec_mask); - uint8x8_t t_hi = vtst_u8(vdup_n_u8(m >> 8), vec_mask); - uint8x16_t vec = vcombine_u8(t_lo, t_hi); - for (int j = 0; j < lb; j++) { - mtx[j] = vreinterpretq_m128i_u8( - vandq_u8(vec, vreinterpretq_u8_m128i(mtx[j]))); - mtx[j] = vreinterpretq_m128i_u8( - vshrq_n_u8(vreinterpretq_u8_m128i(mtx[j]), 7)); - int tmp = _sse2neon_vaddvq_u8(vreinterpretq_u8_m128i(mtx[j])) ? 1 : 0; - res |= (tmp << j); - } - return res; -} - -static int _sse2neon_aggregate_equal_any_16x8(int la, int lb, __m128i mtx[16]) -{ - int res = 0; - int m = (1 << la) - 1; - uint16x8_t vec = - vtstq_u16(vdupq_n_u16(m), vld1q_u16(_sse2neon_cmpestr_mask16b)); - for (int j = 0; j < lb; j++) { - mtx[j] = vreinterpretq_m128i_u16( - vandq_u16(vec, vreinterpretq_u16_m128i(mtx[j]))); - mtx[j] = vreinterpretq_m128i_u16( - vshrq_n_u16(vreinterpretq_u16_m128i(mtx[j]), 15)); - int tmp = _sse2neon_vaddvq_u16(vreinterpretq_u16_m128i(mtx[j])) ? 1 : 0; - res |= (tmp << j); - } - return res; -} - -/* clang-format off */ -#define SSE2NEON_GENERATE_CMP_EQUAL_ANY(prefix) \ - prefix##IMPL(byte) \ - prefix##IMPL(word) -/* clang-format on */ - -SSE2NEON_GENERATE_CMP_EQUAL_ANY(SSE2NEON_CMP_EQUAL_ANY_) - -static int _sse2neon_aggregate_ranges_16x8(int la, int lb, __m128i mtx[16]) -{ - int res = 0; - int m = (1 << la) - 1; - uint16x8_t vec = - vtstq_u16(vdupq_n_u16(m), vld1q_u16(_sse2neon_cmpestr_mask16b)); - for (int j = 0; j < lb; j++) { - mtx[j] = vreinterpretq_m128i_u16( - vandq_u16(vec, vreinterpretq_u16_m128i(mtx[j]))); - mtx[j] = vreinterpretq_m128i_u16( - vshrq_n_u16(vreinterpretq_u16_m128i(mtx[j]), 15)); - __m128i tmp = vreinterpretq_m128i_u32( - vshrq_n_u32(vreinterpretq_u32_m128i(mtx[j]), 16)); - uint32x4_t vec_res = vandq_u32(vreinterpretq_u32_m128i(mtx[j]), - vreinterpretq_u32_m128i(tmp)); -#if defined(__aarch64__) || defined(_M_ARM64) - int t = vaddvq_u32(vec_res) ? 1 : 0; -#else - uint64x2_t sumh = vpaddlq_u32(vec_res); - int t = vgetq_lane_u64(sumh, 0) + vgetq_lane_u64(sumh, 1); -#endif - res |= (t << j); - } - return res; -} - -static int _sse2neon_aggregate_ranges_8x16(int la, int lb, __m128i mtx[16]) -{ - int res = 0; - int m = (1 << la) - 1; - uint8x8_t vec_mask = vld1_u8(_sse2neon_cmpestr_mask8b); - uint8x8_t t_lo = vtst_u8(vdup_n_u8(m & 0xff), vec_mask); - uint8x8_t t_hi = vtst_u8(vdup_n_u8(m >> 8), vec_mask); - uint8x16_t vec = vcombine_u8(t_lo, t_hi); - for (int j = 0; j < lb; j++) { - mtx[j] = vreinterpretq_m128i_u8( - vandq_u8(vec, vreinterpretq_u8_m128i(mtx[j]))); - mtx[j] = vreinterpretq_m128i_u8( - vshrq_n_u8(vreinterpretq_u8_m128i(mtx[j]), 7)); - __m128i tmp = vreinterpretq_m128i_u16( - vshrq_n_u16(vreinterpretq_u16_m128i(mtx[j]), 8)); - uint16x8_t vec_res = vandq_u16(vreinterpretq_u16_m128i(mtx[j]), - vreinterpretq_u16_m128i(tmp)); - int t = _sse2neon_vaddvq_u16(vec_res) ? 1 : 0; - res |= (t << j); - } - return res; -} - -#define SSE2NEON_CMP_RANGES_IS_BYTE 1 -#define SSE2NEON_CMP_RANGES_IS_WORD 0 - -/* clang-format off */ -#define SSE2NEON_GENERATE_CMP_RANGES(prefix) \ - prefix##IMPL(byte, uint, u, prefix##IS_BYTE) \ - prefix##IMPL(byte, int, s, prefix##IS_BYTE) \ - prefix##IMPL(word, uint, u, prefix##IS_WORD) \ - prefix##IMPL(word, int, s, prefix##IS_WORD) -/* clang-format on */ - -SSE2NEON_GENERATE_CMP_RANGES(SSE2NEON_CMP_RANGES_) - -#undef SSE2NEON_CMP_RANGES_IS_BYTE -#undef SSE2NEON_CMP_RANGES_IS_WORD - -static int _sse2neon_cmp_byte_equal_each(__m128i a, int la, __m128i b, int lb) -{ - uint8x16_t mtx = - vceqq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b)); - int m0 = (la < lb) ? 0 : ((1 << la) - (1 << lb)); - int m1 = 0x10000 - (1 << la); - int tb = 0x10000 - (1 << lb); - uint8x8_t vec_mask, vec0_lo, vec0_hi, vec1_lo, vec1_hi; - uint8x8_t tmp_lo, tmp_hi, res_lo, res_hi; - vec_mask = vld1_u8(_sse2neon_cmpestr_mask8b); - vec0_lo = vtst_u8(vdup_n_u8(m0), vec_mask); - vec0_hi = vtst_u8(vdup_n_u8(m0 >> 8), vec_mask); - vec1_lo = vtst_u8(vdup_n_u8(m1), vec_mask); - vec1_hi = vtst_u8(vdup_n_u8(m1 >> 8), vec_mask); - tmp_lo = vtst_u8(vdup_n_u8(tb), vec_mask); - tmp_hi = vtst_u8(vdup_n_u8(tb >> 8), vec_mask); - - res_lo = vbsl_u8(vec0_lo, vdup_n_u8(0), vget_low_u8(mtx)); - res_hi = vbsl_u8(vec0_hi, vdup_n_u8(0), vget_high_u8(mtx)); - res_lo = vbsl_u8(vec1_lo, tmp_lo, res_lo); - res_hi = vbsl_u8(vec1_hi, tmp_hi, res_hi); - res_lo = vand_u8(res_lo, vec_mask); - res_hi = vand_u8(res_hi, vec_mask); - - int res = _sse2neon_vaddv_u8(res_lo) + (_sse2neon_vaddv_u8(res_hi) << 8); - return res; -} - -static int _sse2neon_cmp_word_equal_each(__m128i a, int la, __m128i b, int lb) -{ - uint16x8_t mtx = - vceqq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b)); - int m0 = (la < lb) ? 0 : ((1 << la) - (1 << lb)); - int m1 = 0x100 - (1 << la); - int tb = 0x100 - (1 << lb); - uint16x8_t vec_mask = vld1q_u16(_sse2neon_cmpestr_mask16b); - uint16x8_t vec0 = vtstq_u16(vdupq_n_u16(m0), vec_mask); - uint16x8_t vec1 = vtstq_u16(vdupq_n_u16(m1), vec_mask); - uint16x8_t tmp = vtstq_u16(vdupq_n_u16(tb), vec_mask); - mtx = vbslq_u16(vec0, vdupq_n_u16(0), mtx); - mtx = vbslq_u16(vec1, tmp, mtx); - mtx = vandq_u16(mtx, vec_mask); - return _sse2neon_vaddvq_u16(mtx); -} - -#define SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UBYTE 1 -#define SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UWORD 0 - -#define SSE2NEON_AGGREGATE_EQUAL_ORDER_IMPL(size, number_of_lanes, data_type) \ - static int _sse2neon_aggregate_equal_ordered_##size##x##number_of_lanes( \ - int bound, int la, int lb, __m128i mtx[16]) \ - { \ - int res = 0; \ - int m1 = SSE2NEON_IIF(data_type)(0x10000, 0x100) - (1 << la); \ - uint##size##x8_t vec_mask = SSE2NEON_IIF(data_type)( \ - vld1_u##size(_sse2neon_cmpestr_mask##size##b), \ - vld1q_u##size(_sse2neon_cmpestr_mask##size##b)); \ - uint##size##x##number_of_lanes##_t vec1 = SSE2NEON_IIF(data_type)( \ - vcombine_u##size(vtst_u##size(vdup_n_u##size(m1), vec_mask), \ - vtst_u##size(vdup_n_u##size(m1 >> 8), vec_mask)), \ - vtstq_u##size(vdupq_n_u##size(m1), vec_mask)); \ - uint##size##x##number_of_lanes##_t vec_minusone = vdupq_n_u##size(-1); \ - uint##size##x##number_of_lanes##_t vec_zero = vdupq_n_u##size(0); \ - for (int j = 0; j < lb; j++) { \ - mtx[j] = vreinterpretq_m128i_u##size(vbslq_u##size( \ - vec1, vec_minusone, vreinterpretq_u##size##_m128i(mtx[j]))); \ - } \ - for (int j = lb; j < bound; j++) { \ - mtx[j] = vreinterpretq_m128i_u##size( \ - vbslq_u##size(vec1, vec_minusone, vec_zero)); \ - } \ - unsigned SSE2NEON_IIF(data_type)(char, short) *ptr = \ - (unsigned SSE2NEON_IIF(data_type)(char, short) *) mtx; \ - for (int i = 0; i < bound; i++) { \ - int val = 1; \ - for (int j = 0, k = i; j < bound - i && k < bound; j++, k++) \ - val &= ptr[k * bound + j]; \ - res += val << i; \ - } \ - return res; \ - } - -/* clang-format off */ -#define SSE2NEON_GENERATE_AGGREGATE_EQUAL_ORDER(prefix) \ - prefix##IMPL(8, 16, prefix##IS_UBYTE) \ - prefix##IMPL(16, 8, prefix##IS_UWORD) -/* clang-format on */ - -SSE2NEON_GENERATE_AGGREGATE_EQUAL_ORDER(SSE2NEON_AGGREGATE_EQUAL_ORDER_) - -#undef SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UBYTE -#undef SSE2NEON_AGGREGATE_EQUAL_ORDER_IS_UWORD - -/* clang-format off */ -#define SSE2NEON_GENERATE_CMP_EQUAL_ORDERED(prefix) \ - prefix##IMPL(byte) \ - prefix##IMPL(word) -/* clang-format on */ - -SSE2NEON_GENERATE_CMP_EQUAL_ORDERED(SSE2NEON_CMP_EQUAL_ORDERED_) - -#define SSE2NEON_CMPESTR_LIST \ - _(CMP_UBYTE_EQUAL_ANY, cmp_byte_equal_any) \ - _(CMP_UWORD_EQUAL_ANY, cmp_word_equal_any) \ - _(CMP_SBYTE_EQUAL_ANY, cmp_byte_equal_any) \ - _(CMP_SWORD_EQUAL_ANY, cmp_word_equal_any) \ - _(CMP_UBYTE_RANGES, cmp_ubyte_ranges) \ - _(CMP_UWORD_RANGES, cmp_uword_ranges) \ - _(CMP_SBYTE_RANGES, cmp_sbyte_ranges) \ - _(CMP_SWORD_RANGES, cmp_sword_ranges) \ - _(CMP_UBYTE_EQUAL_EACH, cmp_byte_equal_each) \ - _(CMP_UWORD_EQUAL_EACH, cmp_word_equal_each) \ - _(CMP_SBYTE_EQUAL_EACH, cmp_byte_equal_each) \ - _(CMP_SWORD_EQUAL_EACH, cmp_word_equal_each) \ - _(CMP_UBYTE_EQUAL_ORDERED, cmp_byte_equal_ordered) \ - _(CMP_UWORD_EQUAL_ORDERED, cmp_word_equal_ordered) \ - _(CMP_SBYTE_EQUAL_ORDERED, cmp_byte_equal_ordered) \ - _(CMP_SWORD_EQUAL_ORDERED, cmp_word_equal_ordered) - -enum { -#define _(name, func_suffix) name, - SSE2NEON_CMPESTR_LIST -#undef _ -}; -typedef int (*cmpestr_func_t)(__m128i a, int la, __m128i b, int lb); -static cmpestr_func_t _sse2neon_cmpfunc_table[] = { -#define _(name, func_suffix) _sse2neon_##func_suffix, - SSE2NEON_CMPESTR_LIST -#undef _ -}; - -FORCE_INLINE int _sse2neon_sido_negative(int res, int lb, int imm8, int bound) -{ - switch (imm8 & 0x30) { - case _SIDD_NEGATIVE_POLARITY: - res ^= 0xffffffff; - break; - case _SIDD_MASKED_NEGATIVE_POLARITY: - res ^= (1 << lb) - 1; - break; - default: - break; - } - - return res & ((bound == 8) ? 0xFF : 0xFFFF); -} - -FORCE_INLINE int _sse2neon_clz(unsigned int x) -{ -#ifdef _MSC_VER - unsigned long cnt = 0; - if (_BitScanReverse(&cnt, x)) - return 31 - cnt; - return 32; -#else - return x != 0 ? __builtin_clz(x) : 32; -#endif -} - -FORCE_INLINE int _sse2neon_ctz(unsigned int x) -{ -#ifdef _MSC_VER - unsigned long cnt = 0; - if (_BitScanForward(&cnt, x)) - return cnt; - return 32; -#else - return x != 0 ? __builtin_ctz(x) : 32; -#endif -} - -FORCE_INLINE int _sse2neon_ctzll(unsigned long long x) -{ -#ifdef _MSC_VER - unsigned long cnt; -#if defined(SSE2NEON_HAS_BITSCAN64) - if (_BitScanForward64(&cnt, x)) - return (int) (cnt); -#else - if (_BitScanForward(&cnt, (unsigned long) (x))) - return (int) cnt; - if (_BitScanForward(&cnt, (unsigned long) (x >> 32))) - return (int) (cnt + 32); -#endif /* SSE2NEON_HAS_BITSCAN64 */ - return 64; -#else /* assume GNU compatible compilers */ - return x != 0 ? __builtin_ctzll(x) : 64; -#endif -} - -#define SSE2NEON_MIN(x, y) (x) < (y) ? (x) : (y) - -#define SSE2NEON_CMPSTR_SET_UPPER(var, imm) \ - const int var = (imm & 0x01) ? 8 : 16 - -#define SSE2NEON_CMPESTRX_LEN_PAIR(a, b, la, lb) \ - int tmp1 = la ^ (la >> 31); \ - la = tmp1 - (la >> 31); \ - int tmp2 = lb ^ (lb >> 31); \ - lb = tmp2 - (lb >> 31); \ - la = SSE2NEON_MIN(la, bound); \ - lb = SSE2NEON_MIN(lb, bound) - -// Compare all pairs of character in string a and b, -// then aggregate the result. -// As the only difference of PCMPESTR* and PCMPISTR* is the way to calculate the -// length of string, we use SSE2NEON_CMP{I,E}STRX_GET_LEN to get the length of -// string a and b. -#define SSE2NEON_COMP_AGG(a, b, la, lb, imm8, IE) \ - SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); \ - SSE2NEON_##IE##_LEN_PAIR(a, b, la, lb); \ - int r2 = (_sse2neon_cmpfunc_table[imm8 & 0x0f])(a, la, b, lb); \ - r2 = _sse2neon_sido_negative(r2, lb, imm8, bound) - -#define SSE2NEON_CMPSTR_GENERATE_INDEX(r2, bound, imm8) \ - return (r2 == 0) ? bound \ - : ((imm8 & 0x40) ? (31 - _sse2neon_clz(r2)) \ - : _sse2neon_ctz(r2)) - -#define SSE2NEON_CMPSTR_GENERATE_MASK(dst) \ - __m128i dst = vreinterpretq_m128i_u8(vdupq_n_u8(0)); \ - if (imm8 & 0x40) { \ - if (bound == 8) { \ - uint16x8_t tmp = vtstq_u16(vdupq_n_u16(r2), \ - vld1q_u16(_sse2neon_cmpestr_mask16b)); \ - dst = vreinterpretq_m128i_u16(vbslq_u16( \ - tmp, vdupq_n_u16(-1), vreinterpretq_u16_m128i(dst))); \ - } else { \ - uint8x16_t vec_r2 = \ - vcombine_u8(vdup_n_u8(r2), vdup_n_u8(r2 >> 8)); \ - uint8x16_t tmp = \ - vtstq_u8(vec_r2, vld1q_u8(_sse2neon_cmpestr_mask8b)); \ - dst = vreinterpretq_m128i_u8( \ - vbslq_u8(tmp, vdupq_n_u8(-1), vreinterpretq_u8_m128i(dst))); \ - } \ - } else { \ - if (bound == 16) { \ - dst = vreinterpretq_m128i_u16( \ - vsetq_lane_u16(r2 & 0xffff, vreinterpretq_u16_m128i(dst), 0)); \ - } else { \ - dst = vreinterpretq_m128i_u8( \ - vsetq_lane_u8(r2 & 0xff, vreinterpretq_u8_m128i(dst), 0)); \ - } \ - } \ - return dst - -// Compare packed strings in a and b with lengths la and lb using the control -// in imm8, and returns 1 if b did not contain a null character and the -// resulting mask was zero, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestra -FORCE_INLINE int _mm_cmpestra(__m128i a, - int la, - __m128i b, - int lb, - const int imm8) -{ - int lb_cpy = lb; - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); - return !r2 & (lb_cpy > bound); -} - -// Compare packed strings in a and b with lengths la and lb using the control in -// imm8, and returns 1 if the resulting mask was non-zero, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrc -FORCE_INLINE int _mm_cmpestrc(__m128i a, - int la, - __m128i b, - int lb, - const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); - return r2 != 0; -} - -// Compare packed strings in a and b with lengths la and lb using the control -// in imm8, and store the generated index in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestri -FORCE_INLINE int _mm_cmpestri(__m128i a, - int la, - __m128i b, - int lb, - const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); - SSE2NEON_CMPSTR_GENERATE_INDEX(r2, bound, imm8); -} - -// Compare packed strings in a and b with lengths la and lb using the control -// in imm8, and store the generated mask in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrm -FORCE_INLINE __m128i -_mm_cmpestrm(__m128i a, int la, __m128i b, int lb, const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); - SSE2NEON_CMPSTR_GENERATE_MASK(dst); -} - -// Compare packed strings in a and b with lengths la and lb using the control in -// imm8, and returns bit 0 of the resulting bit mask. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestro -FORCE_INLINE int _mm_cmpestro(__m128i a, - int la, - __m128i b, - int lb, - const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPESTRX); - return r2 & 1; -} - -// Compare packed strings in a and b with lengths la and lb using the control in -// imm8, and returns 1 if any character in a was null, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrs -FORCE_INLINE int _mm_cmpestrs(__m128i a, - int la, - __m128i b, - int lb, - const int imm8) -{ - (void) a; - (void) b; - (void) lb; - SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); - return la <= (bound - 1); -} - -// Compare packed strings in a and b with lengths la and lb using the control in -// imm8, and returns 1 if any character in b was null, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpestrz -FORCE_INLINE int _mm_cmpestrz(__m128i a, - int la, - __m128i b, - int lb, - const int imm8) -{ - (void) a; - (void) b; - (void) la; - SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); - return lb <= (bound - 1); -} - -#define SSE2NEON_CMPISTRX_LENGTH(str, len, imm8) \ - do { \ - if (imm8 & 0x01) { \ - uint16x8_t equal_mask_##str = \ - vceqq_u16(vreinterpretq_u16_m128i(str), vdupq_n_u16(0)); \ - uint8x8_t res_##str = vshrn_n_u16(equal_mask_##str, 4); \ - uint64_t matches_##str = \ - vget_lane_u64(vreinterpret_u64_u8(res_##str), 0); \ - len = _sse2neon_ctzll(matches_##str) >> 3; \ - } else { \ - uint16x8_t equal_mask_##str = vreinterpretq_u16_u8( \ - vceqq_u8(vreinterpretq_u8_m128i(str), vdupq_n_u8(0))); \ - uint8x8_t res_##str = vshrn_n_u16(equal_mask_##str, 4); \ - uint64_t matches_##str = \ - vget_lane_u64(vreinterpret_u64_u8(res_##str), 0); \ - len = _sse2neon_ctzll(matches_##str) >> 2; \ - } \ - } while (0) - -#define SSE2NEON_CMPISTRX_LEN_PAIR(a, b, la, lb) \ - int la, lb; \ - do { \ - SSE2NEON_CMPISTRX_LENGTH(a, la, imm8); \ - SSE2NEON_CMPISTRX_LENGTH(b, lb, imm8); \ - } while (0) - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and returns 1 if b did not contain a null character and the resulting -// mask was zero, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistra -FORCE_INLINE int _mm_cmpistra(__m128i a, __m128i b, const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); - return !r2 & (lb >= bound); -} - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and returns 1 if the resulting mask was non-zero, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrc -FORCE_INLINE int _mm_cmpistrc(__m128i a, __m128i b, const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); - return r2 != 0; -} - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and store the generated index in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistri -FORCE_INLINE int _mm_cmpistri(__m128i a, __m128i b, const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); - SSE2NEON_CMPSTR_GENERATE_INDEX(r2, bound, imm8); -} - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and store the generated mask in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrm -FORCE_INLINE __m128i _mm_cmpistrm(__m128i a, __m128i b, const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); - SSE2NEON_CMPSTR_GENERATE_MASK(dst); -} - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and returns bit 0 of the resulting bit mask. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistro -FORCE_INLINE int _mm_cmpistro(__m128i a, __m128i b, const int imm8) -{ - SSE2NEON_COMP_AGG(a, b, la, lb, imm8, CMPISTRX); - return r2 & 1; -} - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and returns 1 if any character in a was null, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrs -FORCE_INLINE int _mm_cmpistrs(__m128i a, __m128i b, const int imm8) -{ - (void) b; - SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); - int la; - SSE2NEON_CMPISTRX_LENGTH(a, la, imm8); - return la <= (bound - 1); -} - -// Compare packed strings with implicit lengths in a and b using the control in -// imm8, and returns 1 if any character in b was null, and 0 otherwise. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmpistrz -FORCE_INLINE int _mm_cmpistrz(__m128i a, __m128i b, const int imm8) -{ - (void) a; - SSE2NEON_CMPSTR_SET_UPPER(bound, imm8); - int lb; - SSE2NEON_CMPISTRX_LENGTH(b, lb, imm8); - return lb <= (bound - 1); -} - -// Compares the 2 signed 64-bit integers in a and the 2 signed 64-bit integers -// in b for greater than. -FORCE_INLINE __m128i _mm_cmpgt_epi64(__m128i a, __m128i b) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - return vreinterpretq_m128i_u64( - vcgtq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); -#else - return vreinterpretq_m128i_s64(vshrq_n_s64( - vqsubq_s64(vreinterpretq_s64_m128i(b), vreinterpretq_s64_m128i(a)), - 63)); -#endif -} - -// Starting with the initial value in crc, accumulates a CRC32 value for -// unsigned 16-bit integer v, and stores the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u16 -FORCE_INLINE uint32_t _mm_crc32_u16(uint32_t crc, uint16_t v) -{ -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) - __asm__ __volatile__("crc32ch %w[c], %w[c], %w[v]\n\t" - : [c] "+r"(crc) - : [v] "r"(v)); -#elif ((__ARM_ARCH == 8) && defined(__ARM_FEATURE_CRC32)) || \ - (defined(_M_ARM64) && !defined(__clang__)) - crc = __crc32ch(crc, v); -#else - crc = _mm_crc32_u8(crc, v & 0xff); - crc = _mm_crc32_u8(crc, (v >> 8) & 0xff); -#endif - return crc; -} - -// Starting with the initial value in crc, accumulates a CRC32 value for -// unsigned 32-bit integer v, and stores the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u32 -FORCE_INLINE uint32_t _mm_crc32_u32(uint32_t crc, uint32_t v) -{ -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) - __asm__ __volatile__("crc32cw %w[c], %w[c], %w[v]\n\t" - : [c] "+r"(crc) - : [v] "r"(v)); -#elif ((__ARM_ARCH == 8) && defined(__ARM_FEATURE_CRC32)) || \ - (defined(_M_ARM64) && !defined(__clang__)) - crc = __crc32cw(crc, v); -#else - crc = _mm_crc32_u16(crc, v & 0xffff); - crc = _mm_crc32_u16(crc, (v >> 16) & 0xffff); -#endif - return crc; -} - -// Starting with the initial value in crc, accumulates a CRC32 value for -// unsigned 64-bit integer v, and stores the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u64 -FORCE_INLINE uint64_t _mm_crc32_u64(uint64_t crc, uint64_t v) -{ -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) - __asm__ __volatile__("crc32cx %w[c], %w[c], %x[v]\n\t" - : [c] "+r"(crc) - : [v] "r"(v)); -#elif (defined(_M_ARM64) && !defined(__clang__)) - crc = __crc32cd((uint32_t) crc, v); -#else - crc = _mm_crc32_u32((uint32_t) (crc), v & 0xffffffff); - crc = _mm_crc32_u32((uint32_t) (crc), (v >> 32) & 0xffffffff); -#endif - return crc; -} - -// Starting with the initial value in crc, accumulates a CRC32 value for -// unsigned 8-bit integer v, and stores the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_crc32_u8 -FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t crc, uint8_t v) -{ -#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) - __asm__ __volatile__("crc32cb %w[c], %w[c], %w[v]\n\t" - : [c] "+r"(crc) - : [v] "r"(v)); -#elif ((__ARM_ARCH == 8) && defined(__ARM_FEATURE_CRC32)) || \ - (defined(_M_ARM64) && !defined(__clang__)) - crc = __crc32cb(crc, v); -#else - crc ^= v; - for (int bit = 0; bit < 8; bit++) { - if (crc & 1) - crc = (crc >> 1) ^ UINT32_C(0x82f63b78); - else - crc = (crc >> 1); - } -#endif - return crc; -} - -/* AES */ - -#if !defined(__ARM_FEATURE_CRYPTO) && (!defined(_M_ARM64) || defined(__clang__)) -/* clang-format off */ -#define SSE2NEON_AES_SBOX(w) \ - { \ - w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), \ - w(0xc5), w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), \ - w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), \ - w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \ - w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), \ - w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), \ - w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), w(0x04), \ - w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), \ - w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), \ - w(0x75), w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), \ - w(0x5a), w(0xa0), w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), \ - w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), \ - w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \ - w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), \ - w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), \ - w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), \ - w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), \ - w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), \ - w(0xf3), w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), \ - w(0x97), w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), \ - w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), w(0x81), w(0x4f), \ - w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \ - w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), \ - w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), \ - w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), \ - w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), \ - w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), \ - w(0x7a), w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), \ - w(0x1c), w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), \ - w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), \ - w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \ - w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), \ - w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), \ - w(0x94), w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), \ - w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), \ - w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), \ - w(0xb0), w(0x54), w(0xbb), w(0x16) \ - } -#define SSE2NEON_AES_RSBOX(w) \ - { \ - w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), \ - w(0x38), w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), \ - w(0xd7), w(0xfb), w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), \ - w(0x2f), w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), \ - w(0xc4), w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), \ - w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), \ - w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e), w(0x08), \ - w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2), \ - w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), \ - w(0x25), w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), \ - w(0x98), w(0x16), w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), \ - w(0x65), w(0xb6), w(0x92), w(0x6c), w(0x70), w(0x48), w(0x50), \ - w(0xfd), w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), \ - w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), \ - w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), \ - w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06), \ - w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), \ - w(0x02), w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), \ - w(0x8a), w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), \ - w(0x67), w(0xdc), w(0xea), w(0x97), w(0xf2), w(0xcf), w(0xce), \ - w(0xf0), w(0xb4), w(0xe6), w(0x73), w(0x96), w(0xac), w(0x74), \ - w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), \ - w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), \ - w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), \ - w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), \ - w(0x1b), w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), \ - w(0x79), w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), \ - w(0xcd), w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), \ - w(0x88), w(0x07), w(0xc7), w(0x31), w(0xb1), w(0x12), w(0x10), \ - w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f), w(0x60), w(0x51), \ - w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), \ - w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), \ - w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), \ - w(0xb0), w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), \ - w(0x99), w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), \ - w(0x77), w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), \ - w(0x55), w(0x21), w(0x0c), w(0x7d) \ - } -/* clang-format on */ - -/* X Macro trick. See https://en.wikipedia.org/wiki/X_Macro */ -#define SSE2NEON_AES_H0(x) (x) -static const uint8_t _sse2neon_sbox[256] = SSE2NEON_AES_SBOX(SSE2NEON_AES_H0); -static const uint8_t _sse2neon_rsbox[256] = SSE2NEON_AES_RSBOX(SSE2NEON_AES_H0); -#undef SSE2NEON_AES_H0 - -/* x_time function and matrix multiply function */ -#if !defined(__aarch64__) && !defined(_M_ARM64) -#define SSE2NEON_XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) -#define SSE2NEON_MULTIPLY(x, y) \ - (((y & 1) * x) ^ ((y >> 1 & 1) * SSE2NEON_XT(x)) ^ \ - ((y >> 2 & 1) * SSE2NEON_XT(SSE2NEON_XT(x))) ^ \ - ((y >> 3 & 1) * SSE2NEON_XT(SSE2NEON_XT(SSE2NEON_XT(x)))) ^ \ - ((y >> 4 & 1) * SSE2NEON_XT(SSE2NEON_XT(SSE2NEON_XT(SSE2NEON_XT(x)))))) -#endif - -// In the absence of crypto extensions, implement aesenc using regular NEON -// intrinsics instead. See: -// https://www.workofard.com/2017/01/accelerated-aes-for-the-arm64-linux-kernel/ -// https://www.workofard.com/2017/07/ghash-for-low-end-cores/ and -// for more information. -FORCE_INLINE __m128i _mm_aesenc_si128(__m128i a, __m128i RoundKey) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - static const uint8_t shift_rows[] = { - 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3, - 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb, - }; - static const uint8_t ror32by8[] = { - 0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, - 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc, - }; - - uint8x16_t v; - uint8x16_t w = vreinterpretq_u8_m128i(a); - - /* shift rows */ - w = vqtbl1q_u8(w, vld1q_u8(shift_rows)); - - /* sub bytes */ - // Here, we separate the whole 256-bytes table into 4 64-bytes tables, and - // look up each of the table. After each lookup, we load the next table - // which locates at the next 64-bytes. In the meantime, the index in the - // table would be smaller than it was, so the index parameters of - // `vqtbx4q_u8()` need to be added the same constant as the loaded tables. - v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_sbox), w); - // 'w-0x40' equals to 'vsubq_u8(w, vdupq_n_u8(0x40))' - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x40), w - 0x40); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x80), w - 0x80); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0xc0), w - 0xc0); - - /* mix columns */ - w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); - w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); - w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); - - /* add round key */ - return vreinterpretq_m128i_u8(w) ^ RoundKey; - -#else /* ARMv7-A implementation for a table-based AES */ -#define SSE2NEON_AES_B2W(b0, b1, b2, b3) \ - (((uint32_t) (b3) << 24) | ((uint32_t) (b2) << 16) | \ - ((uint32_t) (b1) << 8) | (uint32_t) (b0)) -// muliplying 'x' by 2 in GF(2^8) -#define SSE2NEON_AES_F2(x) ((x << 1) ^ (((x >> 7) & 1) * 0x011b /* WPOLY */)) -// muliplying 'x' by 3 in GF(2^8) -#define SSE2NEON_AES_F3(x) (SSE2NEON_AES_F2(x) ^ x) -#define SSE2NEON_AES_U0(p) \ - SSE2NEON_AES_B2W(SSE2NEON_AES_F2(p), p, p, SSE2NEON_AES_F3(p)) -#define SSE2NEON_AES_U1(p) \ - SSE2NEON_AES_B2W(SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p, p) -#define SSE2NEON_AES_U2(p) \ - SSE2NEON_AES_B2W(p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p) -#define SSE2NEON_AES_U3(p) \ - SSE2NEON_AES_B2W(p, p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p)) - - // this generates a table containing every possible permutation of - // shift_rows() and sub_bytes() with mix_columns(). - static const uint32_t ALIGN_STRUCT(16) aes_table[4][256] = { - SSE2NEON_AES_SBOX(SSE2NEON_AES_U0), - SSE2NEON_AES_SBOX(SSE2NEON_AES_U1), - SSE2NEON_AES_SBOX(SSE2NEON_AES_U2), - SSE2NEON_AES_SBOX(SSE2NEON_AES_U3), - }; -#undef SSE2NEON_AES_B2W -#undef SSE2NEON_AES_F2 -#undef SSE2NEON_AES_F3 -#undef SSE2NEON_AES_U0 -#undef SSE2NEON_AES_U1 -#undef SSE2NEON_AES_U2 -#undef SSE2NEON_AES_U3 - - uint32_t x0 = _mm_cvtsi128_si32(a); // get a[31:0] - uint32_t x1 = - _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0x55)); // get a[63:32] - uint32_t x2 = - _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xAA)); // get a[95:64] - uint32_t x3 = - _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xFF)); // get a[127:96] - - // finish the modulo addition step in mix_columns() - __m128i out = _mm_set_epi32( - (aes_table[0][x3 & 0xff] ^ aes_table[1][(x0 >> 8) & 0xff] ^ - aes_table[2][(x1 >> 16) & 0xff] ^ aes_table[3][x2 >> 24]), - (aes_table[0][x2 & 0xff] ^ aes_table[1][(x3 >> 8) & 0xff] ^ - aes_table[2][(x0 >> 16) & 0xff] ^ aes_table[3][x1 >> 24]), - (aes_table[0][x1 & 0xff] ^ aes_table[1][(x2 >> 8) & 0xff] ^ - aes_table[2][(x3 >> 16) & 0xff] ^ aes_table[3][x0 >> 24]), - (aes_table[0][x0 & 0xff] ^ aes_table[1][(x1 >> 8) & 0xff] ^ - aes_table[2][(x2 >> 16) & 0xff] ^ aes_table[3][x3 >> 24])); - - return _mm_xor_si128(out, RoundKey); -#endif -} - -// Perform one round of an AES decryption flow on data (state) in a using the -// round key in RoundKey, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128 -FORCE_INLINE __m128i _mm_aesdec_si128(__m128i a, __m128i RoundKey) -{ -#if defined(__aarch64__) - static const uint8_t inv_shift_rows[] = { - 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb, - 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3, - }; - static const uint8_t ror32by8[] = { - 0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, - 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc, - }; - - uint8x16_t v; - uint8x16_t w = vreinterpretq_u8_m128i(a); - - // inverse shift rows - w = vqtbl1q_u8(w, vld1q_u8(inv_shift_rows)); - - // inverse sub bytes - v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_rsbox), w); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x40), w - 0x40); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x80), w - 0x80); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0xc0), w - 0xc0); - - // inverse mix columns - // multiplying 'v' by 4 in GF(2^8) - w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); - w = (w << 1) ^ (uint8x16_t) (((int8x16_t) w >> 7) & 0x1b); - v ^= w; - v ^= (uint8x16_t) vrev32q_u16((uint16x8_t) w); - - w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & - 0x1b); // muliplying 'v' by 2 in GF(2^8) - w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); - w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); - - // add round key - return vreinterpretq_m128i_u8(w) ^ RoundKey; - -#else /* ARMv7-A NEON implementation */ - /* FIXME: optimized for NEON */ - uint8_t i, e, f, g, h, v[4][4]; - uint8_t *_a = (uint8_t *) &a; - for (i = 0; i < 16; ++i) { - v[((i / 4) + (i % 4)) % 4][i % 4] = _sse2neon_rsbox[_a[i]]; - } - - // inverse mix columns - for (i = 0; i < 4; ++i) { - e = v[i][0]; - f = v[i][1]; - g = v[i][2]; - h = v[i][3]; - - v[i][0] = SSE2NEON_MULTIPLY(e, 0x0e) ^ SSE2NEON_MULTIPLY(f, 0x0b) ^ - SSE2NEON_MULTIPLY(g, 0x0d) ^ SSE2NEON_MULTIPLY(h, 0x09); - v[i][1] = SSE2NEON_MULTIPLY(e, 0x09) ^ SSE2NEON_MULTIPLY(f, 0x0e) ^ - SSE2NEON_MULTIPLY(g, 0x0b) ^ SSE2NEON_MULTIPLY(h, 0x0d); - v[i][2] = SSE2NEON_MULTIPLY(e, 0x0d) ^ SSE2NEON_MULTIPLY(f, 0x09) ^ - SSE2NEON_MULTIPLY(g, 0x0e) ^ SSE2NEON_MULTIPLY(h, 0x0b); - v[i][3] = SSE2NEON_MULTIPLY(e, 0x0b) ^ SSE2NEON_MULTIPLY(f, 0x0d) ^ - SSE2NEON_MULTIPLY(g, 0x09) ^ SSE2NEON_MULTIPLY(h, 0x0e); - } - - return vreinterpretq_m128i_u8(vld1q_u8((uint8_t *) v)) ^ RoundKey; -#endif -} - -// Perform the last round of an AES encryption flow on data (state) in a using -// the round key in RoundKey, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128 -FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) -{ -#if defined(__aarch64__) - static const uint8_t shift_rows[] = { - 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3, - 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb, - }; - - uint8x16_t v; - uint8x16_t w = vreinterpretq_u8_m128i(a); - - // shift rows - w = vqtbl1q_u8(w, vld1q_u8(shift_rows)); - - // sub bytes - v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_sbox), w); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x40), w - 0x40); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x80), w - 0x80); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0xc0), w - 0xc0); - - // add round key - return vreinterpretq_m128i_u8(v) ^ RoundKey; - -#else /* ARMv7-A implementation */ - uint8_t v[16] = { - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 0)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 5)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 10)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 15)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 4)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 9)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 14)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 3)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 8)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 13)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 2)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 7)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 12)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 1)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 6)], - _sse2neon_sbox[vgetq_lane_u8(vreinterpretq_u8_m128i(a), 11)], - }; - - return vreinterpretq_m128i_u8(vld1q_u8(v)) ^ RoundKey; -#endif -} - -// Perform the last round of an AES decryption flow on data (state) in a using -// the round key in RoundKey, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128 -FORCE_INLINE __m128i _mm_aesdeclast_si128(__m128i a, __m128i RoundKey) -{ -#if defined(__aarch64__) - static const uint8_t inv_shift_rows[] = { - 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb, - 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3, - }; - - uint8x16_t v; - uint8x16_t w = vreinterpretq_u8_m128i(a); - - // inverse shift rows - w = vqtbl1q_u8(w, vld1q_u8(inv_shift_rows)); - - // inverse sub bytes - v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_rsbox), w); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x40), w - 0x40); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0x80), w - 0x80); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_rsbox + 0xc0), w - 0xc0); - - // add round key - return vreinterpretq_m128i_u8(v) ^ RoundKey; - -#else /* ARMv7-A NEON implementation */ - /* FIXME: optimized for NEON */ - uint8_t v[4][4]; - uint8_t *_a = (uint8_t *) &a; - for (int i = 0; i < 16; ++i) { - v[((i / 4) + (i % 4)) % 4][i % 4] = _sse2neon_rsbox[_a[i]]; - } - - return vreinterpretq_m128i_u8(vld1q_u8((uint8_t *) v)) ^ RoundKey; -#endif -} - -// Perform the InvMixColumns transformation on a and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesimc_si128 -FORCE_INLINE __m128i _mm_aesimc_si128(__m128i a) -{ -#if defined(__aarch64__) - static const uint8_t ror32by8[] = { - 0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, - 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc, - }; - uint8x16_t v = vreinterpretq_u8_m128i(a); - uint8x16_t w; - - // multiplying 'v' by 4 in GF(2^8) - w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); - w = (w << 1) ^ (uint8x16_t) (((int8x16_t) w >> 7) & 0x1b); - v ^= w; - v ^= (uint8x16_t) vrev32q_u16((uint16x8_t) w); - - // multiplying 'v' by 2 in GF(2^8) - w = (v << 1) ^ (uint8x16_t) (((int8x16_t) v >> 7) & 0x1b); - w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); - w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); - return vreinterpretq_m128i_u8(w); - -#else /* ARMv7-A NEON implementation */ - uint8_t i, e, f, g, h, v[4][4]; - vst1q_u8((uint8_t *) v, vreinterpretq_u8_m128i(a)); - for (i = 0; i < 4; ++i) { - e = v[i][0]; - f = v[i][1]; - g = v[i][2]; - h = v[i][3]; - - v[i][0] = SSE2NEON_MULTIPLY(e, 0x0e) ^ SSE2NEON_MULTIPLY(f, 0x0b) ^ - SSE2NEON_MULTIPLY(g, 0x0d) ^ SSE2NEON_MULTIPLY(h, 0x09); - v[i][1] = SSE2NEON_MULTIPLY(e, 0x09) ^ SSE2NEON_MULTIPLY(f, 0x0e) ^ - SSE2NEON_MULTIPLY(g, 0x0b) ^ SSE2NEON_MULTIPLY(h, 0x0d); - v[i][2] = SSE2NEON_MULTIPLY(e, 0x0d) ^ SSE2NEON_MULTIPLY(f, 0x09) ^ - SSE2NEON_MULTIPLY(g, 0x0e) ^ SSE2NEON_MULTIPLY(h, 0x0b); - v[i][3] = SSE2NEON_MULTIPLY(e, 0x0b) ^ SSE2NEON_MULTIPLY(f, 0x0d) ^ - SSE2NEON_MULTIPLY(g, 0x09) ^ SSE2NEON_MULTIPLY(h, 0x0e); - } - - return vreinterpretq_m128i_u8(vld1q_u8((uint8_t *) v)); -#endif -} - -// Assist in expanding the AES cipher key by computing steps towards generating -// a round key for encryption cipher using data from a and an 8-bit round -// constant specified in imm8, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aeskeygenassist_si128 -// -// Emits the Advanced Encryption Standard (AES) instruction aeskeygenassist. -// This instruction generates a round key for AES encryption. See -// https://kazakov.life/2017/11/01/cryptocurrency-mining-on-ios-devices/ -// for details. -FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon) -{ -#if defined(__aarch64__) - uint8x16_t _a = vreinterpretq_u8_m128i(a); - uint8x16_t v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(_sse2neon_sbox), _a); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x40), _a - 0x40); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0x80), _a - 0x80); - v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(_sse2neon_sbox + 0xc0), _a - 0xc0); - - uint32x4_t v_u32 = vreinterpretq_u32_u8(v); - uint32x4_t ror_v = vorrq_u32(vshrq_n_u32(v_u32, 8), vshlq_n_u32(v_u32, 24)); - uint32x4_t ror_xor_v = veorq_u32(ror_v, vdupq_n_u32(rcon)); - - return vreinterpretq_m128i_u32(vtrn2q_u32(v_u32, ror_xor_v)); - -#else /* ARMv7-A NEON implementation */ - uint32_t X1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0x55)); - uint32_t X3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xFF)); - for (int i = 0; i < 4; ++i) { - ((uint8_t *) &X1)[i] = _sse2neon_sbox[((uint8_t *) &X1)[i]]; - ((uint8_t *) &X3)[i] = _sse2neon_sbox[((uint8_t *) &X3)[i]]; - } - return _mm_set_epi32(((X3 >> 8) | (X3 << 24)) ^ rcon, X3, - ((X1 >> 8) | (X1 << 24)) ^ rcon, X1); -#endif -} -#undef SSE2NEON_AES_SBOX -#undef SSE2NEON_AES_RSBOX - -#if defined(__aarch64__) -#undef SSE2NEON_XT -#undef SSE2NEON_MULTIPLY -#endif - -#else /* __ARM_FEATURE_CRYPTO */ -// Implements equivalent of 'aesenc' by combining AESE (with an empty key) and -// AESMC and then manually applying the real key as an xor operation. This -// unfortunately means an additional xor op; the compiler should be able to -// optimize this away for repeated calls however. See -// https://blog.michaelbrase.com/2018/05/08/emulating-x86-aes-intrinsics-on-armv8-a -// for more details. -FORCE_INLINE __m128i _mm_aesenc_si128(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u8(veorq_u8( - vaesmcq_u8(vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), - vreinterpretq_u8_m128i(b))); -} - -// Perform one round of an AES decryption flow on data (state) in a using the -// round key in RoundKey, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128 -FORCE_INLINE __m128i _mm_aesdec_si128(__m128i a, __m128i RoundKey) -{ - return vreinterpretq_m128i_u8(veorq_u8( - vaesimcq_u8(vaesdq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), - vreinterpretq_u8_m128i(RoundKey))); -} - -// Perform the last round of an AES encryption flow on data (state) in a using -// the round key in RoundKey, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128 -FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) -{ - return _mm_xor_si128(vreinterpretq_m128i_u8(vaeseq_u8( - vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), - RoundKey); -} - -// Perform the last round of an AES decryption flow on data (state) in a using -// the round key in RoundKey, and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128 -FORCE_INLINE __m128i _mm_aesdeclast_si128(__m128i a, __m128i RoundKey) -{ - return vreinterpretq_m128i_u8( - veorq_u8(vaesdq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0)), - vreinterpretq_u8_m128i(RoundKey))); -} - -// Perform the InvMixColumns transformation on a and store the result in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesimc_si128 -FORCE_INLINE __m128i _mm_aesimc_si128(__m128i a) -{ - return vreinterpretq_m128i_u8(vaesimcq_u8(vreinterpretq_u8_m128i(a))); -} - -// Assist in expanding the AES cipher key by computing steps towards generating -// a round key for encryption cipher using data from a and an 8-bit round -// constant specified in imm8, and store the result in dst." -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aeskeygenassist_si128 -FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon) -{ - // AESE does ShiftRows and SubBytes on A - uint8x16_t u8 = vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0)); - -#ifndef _MSC_VER - uint8x16_t dest = { - // Undo ShiftRows step from AESE and extract X1 and X3 - u8[0x4], u8[0x1], u8[0xE], u8[0xB], // SubBytes(X1) - u8[0x1], u8[0xE], u8[0xB], u8[0x4], // ROT(SubBytes(X1)) - u8[0xC], u8[0x9], u8[0x6], u8[0x3], // SubBytes(X3) - u8[0x9], u8[0x6], u8[0x3], u8[0xC], // ROT(SubBytes(X3)) - }; - uint32x4_t r = {0, (unsigned) rcon, 0, (unsigned) rcon}; - return vreinterpretq_m128i_u8(dest) ^ vreinterpretq_m128i_u32(r); -#else - // We have to do this hack because MSVC is strictly adhering to the CPP - // standard, in particular C++03 8.5.1 sub-section 15, which states that - // unions must be initialized by their first member type. - - // As per the Windows ARM64 ABI, it is always little endian, so this works - __n128 dest{ - ((uint64_t) u8.n128_u8[0x4] << 0) | ((uint64_t) u8.n128_u8[0x1] << 8) | - ((uint64_t) u8.n128_u8[0xE] << 16) | - ((uint64_t) u8.n128_u8[0xB] << 24) | - ((uint64_t) u8.n128_u8[0x1] << 32) | - ((uint64_t) u8.n128_u8[0xE] << 40) | - ((uint64_t) u8.n128_u8[0xB] << 48) | - ((uint64_t) u8.n128_u8[0x4] << 56), - ((uint64_t) u8.n128_u8[0xC] << 0) | ((uint64_t) u8.n128_u8[0x9] << 8) | - ((uint64_t) u8.n128_u8[0x6] << 16) | - ((uint64_t) u8.n128_u8[0x3] << 24) | - ((uint64_t) u8.n128_u8[0x9] << 32) | - ((uint64_t) u8.n128_u8[0x6] << 40) | - ((uint64_t) u8.n128_u8[0x3] << 48) | - ((uint64_t) u8.n128_u8[0xC] << 56)}; - - dest.n128_u32[1] = dest.n128_u32[1] ^ rcon; - dest.n128_u32[3] = dest.n128_u32[3] ^ rcon; - - return dest; -#endif -} -#endif - -/* Others */ - -// Perform a carry-less multiplication of two 64-bit integers, selected from a -// and b according to imm8, and store the results in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128 -FORCE_INLINE __m128i _mm_clmulepi64_si128(__m128i _a, __m128i _b, const int imm) -{ - uint64x2_t a = vreinterpretq_u64_m128i(_a); - uint64x2_t b = vreinterpretq_u64_m128i(_b); - switch (imm & 0x11) { - case 0x00: - return vreinterpretq_m128i_u64( - _sse2neon_vmull_p64(vget_low_u64(a), vget_low_u64(b))); - case 0x01: - return vreinterpretq_m128i_u64( - _sse2neon_vmull_p64(vget_high_u64(a), vget_low_u64(b))); - case 0x10: - return vreinterpretq_m128i_u64( - _sse2neon_vmull_p64(vget_low_u64(a), vget_high_u64(b))); - case 0x11: - return vreinterpretq_m128i_u64( - _sse2neon_vmull_p64(vget_high_u64(a), vget_high_u64(b))); - default: - abort(); - } -} - -FORCE_INLINE unsigned int _sse2neon_mm_get_denormals_zero_mode(void) -{ - union { - fpcr_bitfield field; -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t value; -#else - uint32_t value; -#endif - } r; - -#if defined(__aarch64__) || defined(_M_ARM64) - r.value = _sse2neon_get_fpcr(); -#else - __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ -#endif - - return r.field.bit24 ? _MM_DENORMALS_ZERO_ON : _MM_DENORMALS_ZERO_OFF; -} - -// Count the number of bits set to 1 in unsigned 32-bit integer a, and -// return that count in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_popcnt_u32 -FORCE_INLINE int _mm_popcnt_u32(unsigned int a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) -#if __has_builtin(__builtin_popcount) - return __builtin_popcount(a); -#elif defined(_MSC_VER) - return _CountOneBits(a); -#else - return (int) vaddlv_u8(vcnt_u8(vcreate_u8((uint64_t) a))); -#endif -#else - uint32_t count = 0; - uint8x8_t input_val, count8x8_val; - uint16x4_t count16x4_val; - uint32x2_t count32x2_val; - - input_val = vld1_u8((uint8_t *) &a); - count8x8_val = vcnt_u8(input_val); - count16x4_val = vpaddl_u8(count8x8_val); - count32x2_val = vpaddl_u16(count16x4_val); - - vst1_u32(&count, count32x2_val); - return count; -#endif -} - -// Count the number of bits set to 1 in unsigned 64-bit integer a, and -// return that count in dst. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_popcnt_u64 -FORCE_INLINE int64_t _mm_popcnt_u64(uint64_t a) -{ -#if defined(__aarch64__) || defined(_M_ARM64) -#if __has_builtin(__builtin_popcountll) - return __builtin_popcountll(a); -#elif defined(_MSC_VER) - return _CountOneBits64(a); -#else - return (int64_t) vaddlv_u8(vcnt_u8(vcreate_u8(a))); -#endif -#else - uint64_t count = 0; - uint8x8_t input_val, count8x8_val; - uint16x4_t count16x4_val; - uint32x2_t count32x2_val; - uint64x1_t count64x1_val; - - input_val = vld1_u8((uint8_t *) &a); - count8x8_val = vcnt_u8(input_val); - count16x4_val = vpaddl_u8(count8x8_val); - count32x2_val = vpaddl_u16(count16x4_val); - count64x1_val = vpaddl_u32(count32x2_val); - vst1_u64(&count, count64x1_val); - return count; -#endif -} - -FORCE_INLINE void _sse2neon_mm_set_denormals_zero_mode(unsigned int flag) -{ - // AArch32 Advanced SIMD arithmetic always uses the Flush-to-zero setting, - // regardless of the value of the FZ bit. - union { - fpcr_bitfield field; -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t value; -#else - uint32_t value; -#endif - } r; - -#if defined(__aarch64__) || defined(_M_ARM64) - r.value = _sse2neon_get_fpcr(); -#else - __asm__ __volatile__("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ -#endif - - r.field.bit24 = (flag & _MM_DENORMALS_ZERO_MASK) == _MM_DENORMALS_ZERO_ON; - -#if defined(__aarch64__) || defined(_M_ARM64) - _sse2neon_set_fpcr(r.value); -#else - __asm__ __volatile__("vmsr FPSCR, %0" ::"r"(r)); /* write */ -#endif -} - -// Return the current 64-bit value of the processor's time-stamp counter. -// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=rdtsc -FORCE_INLINE uint64_t __rdtsc(void) -{ -#if defined(__aarch64__) || defined(_M_ARM64) - uint64_t val; - - /* According to ARM DDI 0487F.c, from Armv8.0 to Armv8.5 inclusive, the - * system counter is at least 56 bits wide; from Armv8.6, the counter - * must be 64 bits wide. So the system counter could be less than 64 - * bits wide and it is attributed with the flag 'cap_user_time_short' - * is true. - */ -#if defined(_MSC_VER) - val = _ReadStatusReg(ARM64_SYSREG(3, 3, 14, 0, 2)); -#else - __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(val)); -#endif - - return val; -#else - uint32_t pmccntr, pmuseren, pmcntenset; - // Read the user mode Performance Monitoring Unit (PMU) - // User Enable Register (PMUSERENR) access permissions. - __asm__ __volatile__("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren)); - if (pmuseren & 1) { // Allows reading PMUSERENR for user mode code. - __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset)); - if (pmcntenset & 0x80000000UL) { // Is it counting? - __asm__ __volatile__("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); - // The counter is set up to count every 64th cycle - return (uint64_t) (pmccntr) << 6; - } - } - - // Fallback to syscall as we can't enable PMUSERENR in user mode. - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64_t) (tv.tv_sec) * 1000000 + tv.tv_usec; -#endif -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma pop_macro("ALIGN_STRUCT") -#pragma pop_macro("FORCE_INLINE") -#endif - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC pop_options -#endif - -#endif diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 32dbc8db..56633755 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -5,7 +5,7 @@ plugins { android { namespace 'info.cemu.Cemu' compileSdk 34 - ndkVersion '26.1.10909125' + ndkVersion '25.2.9519653' defaultConfig { applicationId "info.cemu.Cemu" minSdk 30 @@ -47,7 +47,6 @@ android { '-DBUNDLE_SPEEX=ON', '-DENABLE_DISCORD_RPC=OFF', '-DENABLE_NSYSHID_LIBUSB=OFF', - '-DENABLE_HIDAPI=OFF', '-DENABLE_WAYLAND=OFF', ) // abiFilters("x86_64", "arm64-v8a") diff --git a/src/android/app/src/main/cpp/EmulationState.h b/src/android/app/src/main/cpp/EmulationState.h index 59dbeb5b..8640b900 100644 --- a/src/android/app/src/main/cpp/EmulationState.h +++ b/src/android/app/src/main/cpp/EmulationState.h @@ -118,7 +118,9 @@ class EmulationState int wpadCount = 0; for (int i = 0; i < InputManager::kMaxController; i++) { - auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(i).getEmulatedController(); + auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController( + i) + .getEmulatedController(); if (!emulatedController) continue; if (emulatedController->type() != EmulatedController::Type::VPAD) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 528e5b6b..599db860 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -74,7 +74,11 @@ if(WIN32) target_sources(CemuUtil PRIVATE MemMapper/MemMapperWin.cpp) target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoWin.cpp) elseif(UNIX) - target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp) + if(ANDROID) + target_sources(CemuUtil PRIVATE Fiber/FiberBoost.cpp) + else() + target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp) + endif() target_sources(CemuUtil PRIVATE MemMapper/MemMapperUnix.cpp) target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoUnix.cpp) if(NOT APPLE) @@ -91,7 +95,7 @@ set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$ -using _ucontext_t = libucontext_ucontext_t; -constexpr auto& swapcontext = libucontext_swapcontext; -constexpr auto& getcontext = libucontext_getcontext; -constexpr auto& makecontext = libucontext_makecontext; -#else #include -using _ucontext_t = ucontext_t; -#endif #include thread_local Fiber* sCurrentFiber{}; Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* privateData) : m_privateData(privateData) { - _ucontext_t* ctx = (_ucontext_t*)malloc(sizeof(_ucontext_t)); + ucontext_t* ctx = (ucontext_t*)malloc(sizeof(ucontext_t)); const size_t stackSize = 2 * 1024 * 1024; m_stackPtr = malloc(stackSize); @@ -30,7 +21,7 @@ Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* pri Fiber::Fiber(void* privateData) : m_privateData(privateData) { - _ucontext_t* ctx = (_ucontext_t*)malloc(sizeof(_ucontext_t)); + ucontext_t* ctx = (ucontext_t*)malloc(sizeof(ucontext_t)); getcontext(ctx); this->m_implData = (void*)ctx; m_stackPtr = nullptr; @@ -55,7 +46,7 @@ void Fiber::Switch(Fiber& targetFiber) Fiber* leavingFiber = sCurrentFiber; sCurrentFiber = &targetFiber; std::atomic_thread_fence(std::memory_order_seq_cst); - swapcontext((_ucontext_t*)(leavingFiber->m_implData), (_ucontext_t*)(targetFiber.m_implData)); + swapcontext((ucontext_t*)(leavingFiber->m_implData), (ucontext_t*)(targetFiber.m_implData)); std::atomic_thread_fence(std::memory_order_seq_cst); } diff --git a/src/util/crypto/aes128.cpp b/src/util/crypto/aes128.cpp index 345a0dfb..e11b4b44 100644 --- a/src/util/crypto/aes128.cpp +++ b/src/util/crypto/aes128.cpp @@ -600,6 +600,7 @@ void AES128_CBC_decrypt_updateIV(uint8* output, uint8* input, uint32 length, con memcpy(iv, newIv, KEYLEN); } +#if defined(ARCH_X86_64) ATTRIBUTE_AESNI inline __m128i AESNI128_ASSIST( __m128i temp1, __m128i temp2) @@ -791,6 +792,7 @@ ATTRIBUTE_AESNI void __aesni__AES128_ECB_encrypt(uint8* input, const uint8* key, feedback = _mm_aesenclast_si128(feedback, ((__m128i*)expandedKey)[10]); _mm_storeu_si128(&((__m128i*)output)[0], feedback); } +#endif void(*AES128_ECB_encrypt)(uint8* input, const uint8* key, uint8* output); void (*AES128_CBC_decrypt)(uint8* output, uint8* input, uint32 length, const uint8* key, const uint8* iv) = nullptr; @@ -835,6 +837,7 @@ void AES128_init() lookupTable_multiply[i] = (vE << 0) | (v9 << 8) | (vD << 16) | (vB << 24); } // check if AES-NI is available + #if defined(ARCH_X86_64) if (g_CPUFeatures.x86.aesni) { // AES-NI implementation @@ -847,4 +850,8 @@ void AES128_init() AES128_CBC_decrypt = __soft__AES128_CBC_decrypt; AES128_ECB_encrypt = __soft__AES128_ECB_encrypt; } + #else + AES128_CBC_decrypt = __soft__AES128_CBC_decrypt; + AES128_ECB_encrypt = __soft__AES128_ECB_encrypt; + #endif } diff --git a/vcpkg.json b/vcpkg.json index d794b76e..18f28c30 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -41,10 +41,7 @@ }, "boost-random", "fmt", - { - "name": "hidapi", - "platform": "!android" - }, + "hidapi", "libpng", "glm", { From 47f4c6b79e182c856737687e5bbe4cd31c38b7b0 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 20 Jan 2024 21:47:42 +0200 Subject: [PATCH 100/101] Added debug build type appIdsuffix --- src/Cafe/CafeSystem.cpp | 4 +++- src/android/app/build.gradle | 12 ++++++++++++ src/android/app/src/debug/res/values/strings.xml | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/android/app/src/debug/res/values/strings.xml diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 4bc6e8a0..40a4d047 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -515,7 +515,9 @@ namespace CafeSystem { std::string buffer; const char* platform = NULL; - #if BOOST_OS_WINDOWS + #if __ANDROID__ + platform = "Android"; + #elif BOOST_OS_WINDOWS uint32 buildNumber; std::string windowsVersionName = GetWindowsNamedVersion(buildNumber); buffer = fmt::format("{} (Build {})", windowsVersionName, buildNumber); diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 56633755..a4a796a6 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -18,12 +18,24 @@ android { } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + signingConfigs { + release { + storeFile file(RELEASE_STORE_FILE) + storePassword RELEASE_STORE_PASSWORD + keyAlias RELEASE_KEY_ALIAS + keyPassword RELEASE_KEY_PASSWORD + } + } buildTypes { release { + signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } + debug { + applicationIdSuffix ".debug" + } } compileOptions { sourceCompatibility JavaVersion.VERSION_17 diff --git a/src/android/app/src/debug/res/values/strings.xml b/src/android/app/src/debug/res/values/strings.xml new file mode 100644 index 00000000..5aa522b3 --- /dev/null +++ b/src/android/app/src/debug/res/values/strings.xml @@ -0,0 +1,3 @@ + + Cemu debug + \ No newline at end of file From f5cf6b7ca67a21d47625027e6b1cacdf24b52be7 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:59:52 +0200 Subject: [PATCH 101/101] Updated ndkVersion & build without HIDAPI on android --- src/android/app/build.gradle | 3 ++- vcpkg.json | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index a4a796a6..dd4ca3d6 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -5,7 +5,7 @@ plugins { android { namespace 'info.cemu.Cemu' compileSdk 34 - ndkVersion '25.2.9519653' + ndkVersion '26.1.10909125' defaultConfig { applicationId "info.cemu.Cemu" minSdk 30 @@ -60,6 +60,7 @@ android { '-DENABLE_DISCORD_RPC=OFF', '-DENABLE_NSYSHID_LIBUSB=OFF', '-DENABLE_WAYLAND=OFF', + '-DENABLE_HIDAPI=OFF', ) // abiFilters("x86_64", "arm64-v8a") abiFilters("arm64-v8a") diff --git a/vcpkg.json b/vcpkg.json index 18f28c30..d794b76e 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -41,7 +41,10 @@ }, "boost-random", "fmt", - "hidapi", + { + "name": "hidapi", + "platform": "!android" + }, "libpng", "glm", {