From f5be6bef2df0e1c0dbeb01c5fbfb683fbf3fb492 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:55:41 +0100 Subject: [PATCH 01/10] GameList: Save config after updating favorites --- src/gui/wxgui/components/wxGameList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/wxgui/components/wxGameList.cpp b/src/gui/wxgui/components/wxGameList.cpp index 4ad30d6e..ee366ce8 100644 --- a/src/gui/wxgui/components/wxGameList.cpp +++ b/src/gui/wxgui/components/wxGameList.cpp @@ -712,7 +712,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event) GetConfig().SetGameListFavorite(title_id, !GetConfig().IsGameListFavorite(title_id)); SortEntries(); UpdateItemColors(); - //SaveConfig(); + SaveConfig(true); break; case kContextMenuEditName: { From 556fc2489617a041b9d187f93c36eacce146e8fb Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:48:05 +0100 Subject: [PATCH 02/10] GameList: Reduce time it takes for the list to be populated --- src/gui/wxgui/components/wxGameList.cpp | 219 ++++++++++++++---------- src/gui/wxgui/components/wxGameList.h | 11 +- 2 files changed, 133 insertions(+), 97 deletions(-) diff --git a/src/gui/wxgui/components/wxGameList.cpp b/src/gui/wxgui/components/wxGameList.cpp index ee366ce8..cf77119a 100644 --- a/src/gui/wxgui/components/wxGameList.cpp +++ b/src/gui/wxgui/components/wxGameList.cpp @@ -190,6 +190,7 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id) Bind(wxEVT_LIST_COL_END_DRAG, &wxGameList::OnColumnResize, this); Bind(wxEVT_LIST_COL_RIGHT_CLICK, &wxGameList::OnColumnRightClick, this); Bind(wxEVT_SIZE, &wxGameList::OnGameListSize, this); + m_bulkUpdateTimer.Bind(wxEVT_TIMER, &wxGameList::OnTimerBulkAddEntriesToGameList, this); m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxGameList*)ctx)->HandleTitleListCallback(evt); }, this); @@ -1079,112 +1080,142 @@ void wxGameList::OnClose(wxCloseEvent& event) m_exit = true; } -int wxGameList::FindInsertPosition(TitleId titleId) +int wxGameList::FindInsertPosition(TitleId titleId, bool& entryAlreadyExists) { + entryAlreadyExists = false; SortData data{this, ItemColumns(GetSortIndicator()), IsAscendingSortIndicator()}; const auto itemCount = GetItemCount(); + if (itemCount == 0) return 0; - // todo - optimize this with binary search - - for (int i = 0; i < itemCount; i++) + sint32 low = 0; + sint32 high = itemCount; + while (low < high) { - if (SortComparator(titleId, (uint64)GetItemData(i), &data) <= 0) - return i; + sint32 mid = low + (high - low) / 2; + auto cmp = SortComparator(titleId, (uint64)GetItemData(mid), &data); + if (cmp <= 0) + { + if (cmp == 0) + { + entryAlreadyExists = true; + return mid; + } + high = mid; + } + else + { + low = mid + 1; + } } - return itemCount; + return low; +} + +void wxGameList::OnTimerBulkAddEntriesToGameList(wxTimerEvent& event) +{ + std::vector titleIdsToUpdate; + std::swap(titleIdsToUpdate, m_bulkTitlesToAdd); + + wxWindowUpdateLocker lock(this); + bool hasAnyNewEntry = false; + for (auto& titleId : titleIdsToUpdate) + { + GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId); + if (!gameInfo.IsValid() || gameInfo.IsSystemDataTitle()) + { + // entry no longer exists or is not a valid game + // we dont need to remove list entries here because all delete operations should trigger a full list refresh + continue; + } + TitleId baseTitleId = gameInfo.GetBaseTitleId(); + bool isNewEntry = false; + + int icon = -1; /* 0 is the default empty icon */ + int icon_small = -1; /* 0 is the default empty icon */ + QueryIconForTitle(baseTitleId, icon, icon_small); + + bool entryAlreadyExists = false; + auto index = FindInsertPosition(baseTitleId, entryAlreadyExists); + if(!entryAlreadyExists) + { + // entry doesn't exist + index = InsertItem(index, wxString::FromUTF8(GetNameByTitleId(baseTitleId))); + SetItemPtrData(index, baseTitleId); + isNewEntry = true; + hasAnyNewEntry = true; + } + + if (m_style == Style::kList) + { + SetItemColumnImage(index, ColumnIcon, icon_small); + + SetItem(index, ColumnName, wxString::FromUTF8(GetNameByTitleId(baseTitleId))); + + SetItem(index, ColumnVersion, fmt::format("{}", gameInfo.GetVersion())); + + if(gameInfo.HasAOC()) + SetItem(index, ColumnDLC, fmt::format("{}", gameInfo.GetAOCVersion())); + else + SetItem(index, ColumnDLC, wxString()); + + if (isNewEntry) + { + iosu::pdm::GameListStat playTimeStat; + if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat)) + { + // time played + uint32 minutesPlayed = playTimeStat.numMinutesPlayed; + if (minutesPlayed == 0) + SetItem(index, ColumnGameTime, wxEmptyString); + else if (minutesPlayed < 60) + SetItem(index, ColumnGameTime, formatWxString(wxPLURAL("{} minute", "{} minutes", minutesPlayed), minutesPlayed)); + else + { + 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) + { + 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.FormatDate()); + } + else + SetItem(index, ColumnGameStarted, _("never")); + } + else + { + SetItem(index, ColumnGameTime, wxEmptyString); + SetItem(index, ColumnGameStarted, _("never")); + } + } + const auto region_text = fmt::format("{}", gameInfo.GetRegion()); + SetItem(index, ColumnRegion, wxGetTranslation(region_text)); + SetItem(index, ColumnTitleID, fmt::format("{:016x}", baseTitleId)); + } + else if (m_style == Style::kIcons) + { + SetItemImage(index, icon); + } + else if (m_style == Style::kSmallIcons) + { + SetItemImage(index, icon_small); + } + } + if (hasAnyNewEntry) + UpdateItemColors(); } void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) { + if (m_bulkTitlesToAdd.size() < 100) + m_bulkUpdateTimer.StartOnce(100); // if timer is started already this will delay it const auto titleId = event.GetTitleId(); - GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId); - if (!gameInfo.IsValid() || gameInfo.IsSystemDataTitle()) - { - // entry no longer exists or is not a valid game - // we dont need to remove list entries here because all delete operations should trigger a full list refresh - return; - } - TitleId baseTitleId = gameInfo.GetBaseTitleId(); - bool isNewEntry = false; - - int icon = -1; /* 0 is the default empty icon */ - int icon_small = -1; /* 0 is the default empty icon */ - QueryIconForTitle(baseTitleId, icon, icon_small); - - auto index = FindListItemByTitleId(baseTitleId); - if(index == wxNOT_FOUND) - { - // entry doesn't exist - index = InsertItem(FindInsertPosition(baseTitleId), wxString::FromUTF8(GetNameByTitleId(baseTitleId))); - SetItemPtrData(index, baseTitleId); - isNewEntry = true; - } - - if (m_style == Style::kList) - { - SetItemColumnImage(index, ColumnIcon, icon_small); - - SetItem(index, ColumnName, wxString::FromUTF8(GetNameByTitleId(baseTitleId))); - - SetItem(index, ColumnVersion, fmt::format("{}", gameInfo.GetVersion())); - - if(gameInfo.HasAOC()) - SetItem(index, ColumnDLC, fmt::format("{}", gameInfo.GetAOCVersion())); - else - SetItem(index, ColumnDLC, wxString()); - - if (isNewEntry) - { - iosu::pdm::GameListStat playTimeStat; - if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat)) - { - // time played - uint32 minutesPlayed = playTimeStat.numMinutesPlayed; - if (minutesPlayed == 0) - SetItem(index, ColumnGameTime, wxEmptyString); - else if (minutesPlayed < 60) - SetItem(index, ColumnGameTime, formatWxString(wxPLURAL("{} minute", "{} minutes", minutesPlayed), minutesPlayed)); - else - { - 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) - { - 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.FormatDate()); - } - else - SetItem(index, ColumnGameStarted, _("never")); - } - else - { - SetItem(index, ColumnGameTime, wxEmptyString); - SetItem(index, ColumnGameStarted, _("never")); - } - } - - - const auto region_text = fmt::format("{}", gameInfo.GetRegion()); - SetItem(index, ColumnRegion, wxGetTranslation(region_text)); - SetItem(index, ColumnTitleID, fmt::format("{:016x}", baseTitleId)); - } - else if (m_style == Style::kIcons) - { - SetItemImage(index, icon); - } - else if (m_style == Style::kSmallIcons) - { - SetItemImage(index, icon_small); - } - if (isNewEntry) - UpdateItemColors(index); + m_bulkTitlesToAdd.emplace_back(titleId); } void wxGameList::OnItemActivated(wxListEvent& event) diff --git a/src/gui/wxgui/components/wxGameList.h b/src/gui/wxgui/components/wxGameList.h index 3c5fdedd..388a9df6 100644 --- a/src/gui/wxgui/components/wxGameList.h +++ b/src/gui/wxgui/components/wxGameList.h @@ -3,8 +3,6 @@ #include "config/CemuConfig.h" #include "Cafe/TitleList/TitleId.h" -#include - #include #include #include @@ -57,6 +55,7 @@ public: void CreateShortcut(GameInfo2& gameInfo); long FindListItemByTitleId(uint64 title_id) const; + void OnClose(wxCloseEvent& event); private: @@ -93,7 +92,7 @@ private: int dir; }; - int FindInsertPosition(TitleId titleId); + int FindInsertPosition(TitleId titleId, bool& entryAlreadyExists); std::weak_ordering SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData); static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData); @@ -132,6 +131,10 @@ private: std::map m_name_cache; + // bulk update handling + std::vector m_bulkTitlesToAdd; + wxTimer m_bulkUpdateTimer; + // list mode void CreateListColumns(); @@ -156,6 +159,8 @@ private: void AdjustLastColumnWidth(); int GetColumnDefaultWidth(int column); + void OnTimerBulkAddEntriesToGameList(wxTimerEvent& event); + static inline std::once_flag s_launch_file_once; }; From 18dd75c1f5abe270062e83e5458c61841a53dedb Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:26:11 +0100 Subject: [PATCH 03/10] Latte: Force height of 1D textures to 1 --- src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp index 25c9f54b..4c11e57c 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp @@ -140,6 +140,8 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u depth = 1; } uint32 height = word1.get_HEIGHT() + 1; + if (dim == Latte::E_DIM::DIM_1D || dim == Latte::E_DIM::DIM_1D_ARRAY) + height = 1; if (Latte::IsCompressedFormat(word1.get_DATA_FORMAT())) pitch /= 4; // view slice From 2096c434de63227d8850585e0a9340bf2a43bbe8 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:19:44 +0100 Subject: [PATCH 04/10] Latte: Cleanup some endian handling code --- src/Cafe/OS/libs/gx2/GX2.cpp | 8 ++--- src/Cafe/OS/libs/gx2/GX2_Blit.cpp | 16 +++++----- src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp | 30 ++++++++----------- src/Cafe/OS/libs/gx2/GX2_Surface.h | 36 +++++++++++------------ src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp | 8 ++--- 5 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/Cafe/OS/libs/gx2/GX2.cpp b/src/Cafe/OS/libs/gx2/GX2.cpp index 532ce21e..b5e75b90 100644 --- a/src/Cafe/OS/libs/gx2/GX2.cpp +++ b/src/Cafe/OS/libs/gx2/GX2.cpp @@ -105,7 +105,7 @@ void gx2Export_GX2CopyColorBufferToScanBuffer(PPCInterpreter_t* hCPU) gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.pitch); gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.tileMode.value()); gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.swizzle); - gx2WriteGather_submitU32AsBE(_swapEndianU32(colorBuffer->viewFirstSlice)); + gx2WriteGather_submitU32AsBE((uint32)colorBuffer->viewFirstSlice); gx2WriteGather_submitU32AsBE((uint32)colorBuffer->surface.format.value()); gx2WriteGather_submitU32AsBE(hCPU->gpr[4]); @@ -161,9 +161,9 @@ void _GX2InitScanBuffer(GX2ColorBuffer* colorBuffer, sint32 width, sint32 height colorBuffer->surface.resFlag = GX2_RESFLAG_USAGE_TEXTURE | GX2_RESFLAG_USAGE_COLOR_BUFFER; colorBuffer->surface.width = width; colorBuffer->surface.height = height; - colorBuffer->viewFirstSlice = _swapEndianU32(0); - colorBuffer->viewNumSlices = _swapEndianU32(1); - colorBuffer->viewMip = _swapEndianU32(0); + colorBuffer->viewFirstSlice = 0; + colorBuffer->viewNumSlices = 1; + colorBuffer->viewMip = 0; colorBuffer->surface.numLevels = 1; colorBuffer->surface.dim = Latte::E_DIM::DIM_2D; colorBuffer->surface.swizzle = 0; diff --git a/src/Cafe/OS/libs/gx2/GX2_Blit.cpp b/src/Cafe/OS/libs/gx2/GX2_Blit.cpp index 6e0db6aa..af7c1e7e 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Blit.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Blit.cpp @@ -13,8 +13,8 @@ namespace GX2 void GX2SetClearDepthStencil(GX2DepthBuffer* depthBuffer, float depthClearValue, uint8 stencilClearValue) { GX2ReserveCmdSpace(4); - *(uint32*)&depthBuffer->clearDepth = _swapEndianU32(*(uint32*)&depthClearValue); - depthBuffer->clearStencil = _swapEndianU32(stencilClearValue); + depthBuffer->clearDepth = depthClearValue; + depthBuffer->clearStencil = stencilClearValue; Latte::LATTE_DB_STENCIL_CLEAR stencilClearReg; stencilClearReg.set_clearValue(stencilClearValue); Latte::LATTE_DB_DEPTH_CLEAR depthClearReg; @@ -28,7 +28,7 @@ namespace GX2 void GX2SetClearDepth(GX2DepthBuffer* depthBuffer, float depthClearValue) { GX2ReserveCmdSpace(3); - *(uint32*)&depthBuffer->clearDepth = _swapEndianU32(*(uint32*)&depthClearValue); + depthBuffer->clearDepth = depthClearValue; Latte::LATTE_DB_DEPTH_CLEAR depthClearReg; depthClearReg.set_clearValue(depthClearValue); gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1), @@ -40,7 +40,7 @@ namespace GX2 void GX2SetClearStencil(GX2DepthBuffer* depthBuffer, uint8 stencilClearValue) { GX2ReserveCmdSpace(3); - depthBuffer->clearStencil = _swapEndianU32(stencilClearValue); + depthBuffer->clearStencil = stencilClearValue; Latte::LATTE_DB_STENCIL_CLEAR stencilClearReg; stencilClearReg.set_clearValue(stencilClearValue); gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1), @@ -109,8 +109,8 @@ namespace GX2 colorWidth = colorBuffer->surface.width; colorHeight = colorBuffer->surface.height; colorPitch = colorBuffer->surface.pitch; - colorFirstSlice = _swapEndianU32(colorBuffer->viewFirstSlice); - colorNumSlices = _swapEndianU32(colorBuffer->viewNumSlices); + colorFirstSlice = colorBuffer->viewFirstSlice; + colorNumSlices = colorBuffer->viewNumSlices; } // depth buffer MPTR depthPhysAddr = MPTR_NULL; @@ -129,8 +129,8 @@ namespace GX2 depthWidth = depthBuffer->surface.width; depthHeight = depthBuffer->surface.height; depthPitch = depthBuffer->surface.pitch; - depthFirstSlice = _swapEndianU32(depthBuffer->viewFirstSlice); - depthNumSlices = _swapEndianU32(depthBuffer->viewNumSlices); + depthFirstSlice = depthBuffer->viewFirstSlice; + depthNumSlices = depthBuffer->viewNumSlices; } gx2WriteGather_submit(pm4HeaderType3(IT_HLE_CLEAR_COLOR_DEPTH_STENCIL, 23), hleClearFlags, diff --git a/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp b/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp index 8abc3613..4ef37b18 100644 --- a/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp @@ -17,12 +17,12 @@ void gx2Export_GX2InitColorBufferRegs(PPCInterpreter_t* hCPU) ppcDefineParamStructPtr(colorBuffer, GX2ColorBuffer, 0); LatteAddrLib::AddrSurfaceInfo_OUT surfaceInfo; - LatteAddrLib::GX2CalculateSurfaceInfo(colorBuffer->surface.format, colorBuffer->surface.width, colorBuffer->surface.height, colorBuffer->surface.depth, colorBuffer->surface.dim, colorBuffer->surface.tileMode, colorBuffer->surface.aa, _swapEndianU32(colorBuffer->viewMip), &surfaceInfo); + LatteAddrLib::GX2CalculateSurfaceInfo(colorBuffer->surface.format, colorBuffer->surface.width, colorBuffer->surface.height, colorBuffer->surface.depth, colorBuffer->surface.dim, colorBuffer->surface.tileMode, colorBuffer->surface.aa, colorBuffer->viewMip, &surfaceInfo); uint32 pitchHeight = (surfaceInfo.height * surfaceInfo.pitch) >> 6; #ifdef CEMU_DEBUG_ASSERT - if (colorBuffer->viewNumSlices != _swapEndianU32(1)) - cemuLog_logDebug(LogType::Force, "GX2InitColorBufferRegs(): With unsupported slice count {}", _swapEndianU32(colorBuffer->viewNumSlices)); + if (colorBuffer->viewNumSlices != 1) + cemuLog_logDebug(LogType::Force, "GX2InitColorBufferRegs(): With unsupported slice count {}", (uint32)colorBuffer->viewNumSlices); if (surfaceInfo.pitch < 7) cemuLog_logDebug(LogType::Force, "GX2InitColorBufferRegs(): Pitch too small (pitch = {})", surfaceInfo.pitch); if ((surfaceInfo.pitch & 7) != 0) @@ -101,8 +101,8 @@ void gx2Export_GX2InitColorBufferRegs(PPCInterpreter_t* hCPU) uint32 regView = 0; if (colorBuffer->surface.tileMode != Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL) { - regView |= (_swapEndianU32(colorBuffer->viewFirstSlice) & 0x7FF); - regView |= (((_swapEndianU32(colorBuffer->viewNumSlices) + _swapEndianU32(colorBuffer->viewFirstSlice) - 1) & 0x7FF) << 13); + regView |= ((uint32)colorBuffer->viewFirstSlice & 0x7FF); + regView |= ((((uint32)colorBuffer->viewNumSlices + (uint32)colorBuffer->viewFirstSlice - 1) & 0x7FF) << 13); } colorBuffer->reg_view = regView; colorBuffer->reg_mask = 0; @@ -118,7 +118,7 @@ void gx2Export_GX2InitDepthBufferRegs(PPCInterpreter_t* hCPU) ppcDefineParamStructPtr(depthBuffer, GX2DepthBuffer, 0); LatteAddrLib::AddrSurfaceInfo_OUT surfaceInfo; - LatteAddrLib::GX2CalculateSurfaceInfo(depthBuffer->surface.format, depthBuffer->surface.width, depthBuffer->surface.height, depthBuffer->surface.depth, depthBuffer->surface.dim, depthBuffer->surface.tileMode, depthBuffer->surface.aa, _swapEndianU32(depthBuffer->viewMip), &surfaceInfo); + LatteAddrLib::GX2CalculateSurfaceInfo(depthBuffer->surface.format, depthBuffer->surface.width, depthBuffer->surface.height, depthBuffer->surface.depth, depthBuffer->surface.dim, depthBuffer->surface.tileMode, depthBuffer->surface.aa, depthBuffer->viewMip, &surfaceInfo); cemu_assert_debug(depthBuffer->viewNumSlices != 0); @@ -140,18 +140,12 @@ void gx2Export_GX2SetColorBuffer(PPCInterpreter_t* hCPU) GX2ColorBuffer* colorBufferBE = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); #ifdef CEMU_DEBUG_ASSERT - cemuLog_log(LogType::GX2, "ColorBuffer tileMode {:01x} PhysAddr {:08x} fmt {:04x} res {}x{} Mip {} Slice {}", (uint32)colorBufferBE->surface.tileMode.value(), (uint32)colorBufferBE->surface.imagePtr, (uint32)colorBufferBE->surface.format.value(), (uint32)colorBufferBE->surface.width, (uint32)colorBufferBE->surface.height, _swapEndianU32(colorBufferBE->viewMip), _swapEndianU32(colorBufferBE->viewFirstSlice)); + cemuLog_log(LogType::GX2, "ColorBuffer tileMode {:01x} PhysAddr {:08x} fmt {:04x} res {}x{} Mip {} Slice {}", (uint32)colorBufferBE->surface.tileMode.value(), (uint32)colorBufferBE->surface.imagePtr, (uint32)colorBufferBE->surface.format.value(), (uint32)colorBufferBE->surface.width, (uint32)colorBufferBE->surface.height, (uint32)colorBufferBE->viewMip, (uint32)colorBufferBE->viewFirstSlice); #endif - // regs[0] = mmCB_COLOR0_SIZE - // regs[1] = mmCB_COLOR0_INFO - // regs[2] = mmCB_COLOR0_VIEW - // regs[3] = mmCB_COLOR0_MASK - // regs[4] = mmCB_COLOR0_TILE - uint32 targetIndex = hCPU->gpr[4]; - uint32 viewMip = _swapEndianU32(colorBufferBE->viewMip); + uint32 viewMip = colorBufferBE->viewMip; uint32 colorBufferBase = memory_virtualToPhysical(colorBufferBE->surface.imagePtr); if( viewMip != 0 ) @@ -165,7 +159,7 @@ void gx2Export_GX2SetColorBuffer(PPCInterpreter_t* hCPU) } Latte::E_GX2TILEMODE tileMode = colorBufferBE->surface.tileMode; - uint32 viewMipIndex = _swapEndianU32(colorBufferBE->viewMip); + uint32 viewMipIndex = colorBufferBE->viewMip; uint32 swizzle = colorBufferBE->surface.swizzle; if (Latte::TM_IsMacroTiled(tileMode) && viewMipIndex < ((swizzle >> 16) & 0xFF)) @@ -210,7 +204,7 @@ void gx2Export_GX2SetDepthBuffer(PPCInterpreter_t* hCPU) cemuLog_log(LogType::GX2, "DepthBuffer tileMode {:01x} PhysAddr {:08x} fmt {:04x} res {}x{}", (uint32)depthBufferBE->surface.tileMode.value(), (uint32)depthBufferBE->surface.imagePtr, (uint32)depthBufferBE->surface.format.value(), (uint32)depthBufferBE->surface.width, (uint32)depthBufferBE->surface.height); - uint32 viewMip = _swapEndianU32(depthBufferBE->viewMip); + uint32 viewMip = depthBufferBE->viewMip; // todo: current code for the PM4 packets is a hack, replace with proper implementation @@ -256,8 +250,8 @@ void gx2Export_GX2SetDepthBuffer(PPCInterpreter_t* hCPU) // set DB_DEPTH_VIEW uint32 db_view = 0; - db_view |= (_swapEndianU32(depthBufferBE->viewFirstSlice)&0x7FF); - db_view |= (((_swapEndianU32(depthBufferBE->viewNumSlices)+_swapEndianU32(depthBufferBE->viewFirstSlice)-1)&0x7FF)<<13); + db_view |= ((uint32)depthBufferBE->viewFirstSlice&0x7FF); + db_view |= ((((uint32)depthBufferBE->viewNumSlices+(uint32)depthBufferBE->viewFirstSlice-1)&0x7FF)<<13); gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SET_CONTEXT_REG, 2)); gx2WriteGather_submitU32AsBE(mmDB_DEPTH_VIEW - 0xA000); gx2WriteGather_submitU32AsBE(db_view); diff --git a/src/Cafe/OS/libs/gx2/GX2_Surface.h b/src/Cafe/OS/libs/gx2/GX2_Surface.h index 2586503a..a8981e0e 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Surface.h +++ b/src/Cafe/OS/libs/gx2/GX2_Surface.h @@ -33,17 +33,17 @@ static_assert(sizeof(GX2Surface) == 0x74); struct GX2ColorBuffer { - /* +0x00 */ GX2Surface surface; - /* +0x74 */ uint32 viewMip; - /* +0x78 */ uint32 viewFirstSlice; - /* +0x7C */ uint32 viewNumSlices; - /* +0x80 */ MPTR auxData; - /* +0x84 */ uint32 auxSize; - /* +0x88 */ uint32be reg_size; // CB_COLOR*_SIZE - /* +0x8C */ uint32be reg_info; // CB_COLOR*_INFO - /* +0x90 */ uint32be reg_view; // CB_COLOR*_VIEW - /* +0x94 */ uint32be reg_mask; // CB_COLOR*_MASK - /* +0x98 */ uint32be reg4; // ? + /* +0x00 */ GX2Surface surface; + /* +0x74 */ uint32be viewMip; + /* +0x78 */ uint32be viewFirstSlice; + /* +0x7C */ uint32be viewNumSlices; + /* +0x80 */ MEMPTR auxData; + /* +0x84 */ uint32be auxSize2; + /* +0x88 */ uint32be reg_size; // CB_COLOR*_SIZE + /* +0x8C */ uint32be reg_info; // CB_COLOR*_INFO + /* +0x90 */ uint32be reg_view; // CB_COLOR*_VIEW + /* +0x94 */ uint32be reg_mask; // CB_COLOR*_MASK + /* +0x98 */ uint32be reg4; // ? }; static_assert(sizeof(GX2ColorBuffer) == 0x9C); @@ -51,13 +51,13 @@ static_assert(sizeof(GX2ColorBuffer) == 0x9C); struct GX2DepthBuffer { /* +0x00 */ GX2Surface surface; - /* +0x74 */ uint32 viewMip; - /* +0x78 */ uint32 viewFirstSlice; - /* +0x7C */ uint32 viewNumSlices; - /* +0x80 */ MPTR hiZPtr; - /* +0x84 */ uint32 hiZSize; - /* +0x88 */ float clearDepth; - /* +0x8C */ uint32 clearStencil; + /* +0x74 */ uint32be viewMip; + /* +0x78 */ uint32be viewFirstSlice; + /* +0x7C */ uint32be viewNumSlices; + /* +0x80 */ MEMPTR hiZPtr; + /* +0x84 */ uint32be hiZSize; + /* +0x88 */ float32be clearDepth; + /* +0x8C */ uint32be clearStencil; /* +0x90 */ uint32be reg_size; /* +0x94 */ uint32be reg_view; /* +0x98 */ uint32be reg_base; diff --git a/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp b/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp index 681889dc..f52f38e7 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp @@ -435,13 +435,13 @@ void gx2Export_GX2ResolveAAColorBuffer(PPCInterpreter_t* hCPU) GX2ColorBuffer* srcColorBuffer = (GX2ColorBuffer*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); GX2Surface* srcSurface = &srcColorBuffer->surface; GX2Surface* dstSurface = (GX2Surface*)memory_getPointerFromVirtualOffset(hCPU->gpr[4]); - uint32 srcMip = _swapEndianU32(srcColorBuffer->viewMip); + uint32 srcMip = srcColorBuffer->viewMip; uint32 dstMip = hCPU->gpr[5]; - uint32 srcSlice = _swapEndianU32(srcColorBuffer->viewFirstSlice); + uint32 srcSlice = srcColorBuffer->viewFirstSlice; uint32 dstSlice = hCPU->gpr[6]; #ifdef CEMU_DEBUG_ASSERT - if( _swapEndianU32(srcColorBuffer->viewMip) != 0 || _swapEndianU32(srcColorBuffer->viewFirstSlice) != 0 ) + if( srcColorBuffer->viewMip != 0 || srcColorBuffer->viewFirstSlice != 0 ) assert_dbg(); #endif @@ -618,7 +618,7 @@ void gx2Export_GX2ConvertDepthBufferToTextureSurface(PPCInterpreter_t* hCPU) sint32 srcMip = 0; - uint32 numSlices = std::max(_swapEndianU32(depthBuffer->viewNumSlices), 1); + uint32 numSlices = std::max(depthBuffer->viewNumSlices, 1); GX2::GX2ReserveCmdSpace((1 + 13 * 2) * numSlices); for (uint32 subSliceIndex = 0; subSliceIndex < numSlices; subSliceIndex++) { From 4ca4d1caedf799599aab4227be5507dba0942730 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 20 Feb 2026 21:00:22 +0100 Subject: [PATCH 05/10] config: Retain Vulkan device selection from previous config format --- src/config/CemuConfig.cpp | 7 ++++++- src/config/CemuConfig.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 92542fa9..51f18a57 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -127,7 +127,11 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser) // graphics auto graphic = parser.get("Graphic"); graphic_api = graphic.get("api", kOpenGL); - graphic.get("vkDevice", vk_graphic_device_uuid); + graphic.get("device", legacy_graphic_device_uuid); + if (graphic.get("vkDevice").valid()) + graphic.get("vkDevice", vk_graphic_device_uuid); + else + vk_graphic_device_uuid = legacy_graphic_device_uuid; mtl_graphic_device_uuid = graphic.get("mtlDevice", 0); vsync = graphic.get("VSync", 0); overrideAppGammaPreference = graphic.get("OverrideAppGammaPreference", false); @@ -359,6 +363,7 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser) // graphics auto graphic = config.set("Graphic"); graphic.set("api", graphic_api); + graphic.set("device", legacy_graphic_device_uuid); graphic.set("vkDevice", vk_graphic_device_uuid); graphic.set("mtlDevice", mtl_graphic_device_uuid); graphic.set("VSync", vsync); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index 1134ab96..1f230093 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -432,6 +432,7 @@ struct CemuConfig // graphics ConfigValue graphic_api{ kVulkan }; + std::array legacy_graphic_device_uuid{}; // placeholder option for backwards compatibility with settings from 2.6 and before (renamed to "vkDevice") std::array vk_graphic_device_uuid; uint64 mtl_graphic_device_uuid{ 0 }; ConfigValue vsync{ 0 }; // 0 = off, 1+ = depending on render backend From ceb9771a5c10a75198baede1fc0f8a14cff5668d Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 23 Feb 2026 03:21:24 +0100 Subject: [PATCH 06/10] Vulkan: Fixes for object refcounting --- src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h | 12 ++---------- .../Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp | 2 -- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h index 7dcd3ebc..7d2cb653 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h @@ -45,7 +45,7 @@ public: VKRMoveableRefCounter(VKRMoveableRefCounter&& rhs) noexcept { this->refs = std::move(rhs.refs); - this->m_refCount = rhs.m_refCount; + this->m_refCount.store(rhs.m_refCount); rhs.m_refCount = 0; this->selfRef = rhs.selfRef; rhs.selfRef = nullptr; @@ -88,21 +88,13 @@ protected: // does nothing by default } - int m_refCount{}; + std::atomic_int_least32_t m_refCount{}; private: VKRMoveableRefCounterRef* selfRef; std::vector refs; #ifdef CEMU_DEBUG_ASSERT std::vector reverseRefs; #endif - - void moveObj(VKRMoveableRefCounter&& rhs) - { - this->refs = std::move(rhs.refs); - this->m_refCount = rhs.m_refCount; - this->selfRef = rhs.selfRef; - this->selfRef->ref = this; - } }; class VKRDestructibleObject : public VKRMoveableRefCounter diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp index 795d11c3..55b2330e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp @@ -1177,8 +1177,6 @@ void PipelineCompiler::CompileThreadPool_Stop() while (!s_pipelineCompileRequests.empty()) { PipelineCompiler* pipelineCompiler = s_pipelineCompileRequests.pop(); - if (!pipelineCompiler) - break; if (pipelineCompiler) delete pipelineCompiler; } From 3ee166101a1459da47d0078a869ff6023ba0ff64 Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:06:59 +0100 Subject: [PATCH 07/10] debugger: fix breakpoints window right-click being offset --- src/gui/wxgui/debugger/BreakpointWindow.cpp | 39 ++++++++++----------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/gui/wxgui/debugger/BreakpointWindow.cpp b/src/gui/wxgui/debugger/BreakpointWindow.cpp index 13ce86c8..64e82c59 100644 --- a/src/gui/wxgui/debugger/BreakpointWindow.cpp +++ b/src/gui/wxgui/debugger/BreakpointWindow.cpp @@ -32,7 +32,7 @@ BreakpointWindow::BreakpointWindow(DebuggerWindow2& parent, const wxPoint& main_ wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - m_breakpoints = new wxListView(this, wxID_ANY); + m_breakpoints = new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL); m_breakpoints->EnableCheckBoxes(true); wxListItem col0; @@ -162,10 +162,12 @@ void BreakpointWindow::OnBreakpointToggled(wxListEvent& event) void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) { const auto position = event.GetPosition(); - const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2; - if (index < 0 || index >= m_breakpoints->GetItemCount()) + int flags = 0; + const long index = m_breakpoints->HitTest(position, flags); + + if (index == wxNOT_FOUND) return; - + sint32 x = position.x; const auto enabled_width = m_breakpoints->GetColumnWidth(ColumnEnabled); if (x <= enabled_width) @@ -173,7 +175,7 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) x -= enabled_width; const auto address_width = m_breakpoints->GetColumnWidth(ColumnAddress); - if(x <= address_width) + if (x <= address_width) { const auto item = m_breakpoints->GetItemText(index, ColumnAddress); const auto address = std::stoul(item.ToStdString(), nullptr, 16); @@ -183,13 +185,13 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) } x -= address_width; - const auto type_width = m_breakpoints->GetColumnWidth(ColumnType); + const auto type_width = m_breakpoints->GetColumnWidth(ColumnType); if (x <= type_width) return; x -= type_width; const auto comment_width = m_breakpoints->GetColumnWidth(ColumnComment); - if(x <= comment_width) + if (x <= comment_width) { if (index >= debuggerState.breakpoints.size()) return; @@ -213,9 +215,9 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) void BreakpointWindow::OnRightDown(wxMouseEvent& event) { - const auto position = event.GetPosition(); - const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2; - if (index < 0 || index >= m_breakpoints->GetItemCount()) + int flags = 0; + const long index = m_breakpoints->HitTest(event.GetPosition(), flags); + if (index == wxNOT_FOUND || index < 0 || index >= m_breakpoints->GetItemCount()) { wxMenu menu; menu.Append(MENU_ID_CREATE_CODE_BP_EXECUTION, _("Create execution breakpoint")); @@ -244,19 +246,16 @@ void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt) if (evt.GetId() == MENU_ID_DELETE_BP) { long sel = m_breakpoints->GetFirstSelected(); - if (sel != wxNOT_FOUND) - { - if (sel >= debuggerState.breakpoints.size()) - return; + if (sel == wxNOT_FOUND || sel < 0 || sel >= m_breakpoints->GetItemCount()) + return; - auto it = debuggerState.breakpoints.begin(); - std::advance(it, sel); + auto it = debuggerState.breakpoints.begin(); + std::advance(it, sel); - debugger_deleteBreakpoint(*it); + debugger_deleteBreakpoint(*it); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); - wxPostEvent(this->m_parent, evt); - } + wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); + wxPostEvent(this->m_parent, evt); } } From a04eb53822d306435f4efaba36225bc3bd297f5d Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Thu, 12 Mar 2026 21:58:22 +0100 Subject: [PATCH 08/10] vcpkg: Update wxWidgets to 3.3.2 (#1824) --- .../wxwidgets/fix-listctrl-layout.patch | 40 +++++++++++++++++++ .../wxwidgets/portfile.cmake | 9 +++-- .../vcpkg_overlay_ports/wxwidgets/vcpkg.json | 2 +- src/gui/wxgui/components/wxGameList.cpp | 4 +- 4 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 dependencies/vcpkg_overlay_ports/wxwidgets/fix-listctrl-layout.patch diff --git a/dependencies/vcpkg_overlay_ports/wxwidgets/fix-listctrl-layout.patch b/dependencies/vcpkg_overlay_ports/wxwidgets/fix-listctrl-layout.patch new file mode 100644 index 00000000..4da35fb3 --- /dev/null +++ b/dependencies/vcpkg_overlay_ports/wxwidgets/fix-listctrl-layout.patch @@ -0,0 +1,40 @@ +diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp +index 5e1cd98a8b..169ab7847d 100644 +--- a/src/msw/listctrl.cpp ++++ b/src/msw/listctrl.cpp +@@ -1385,20 +1385,29 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) + + wxCopyRECTToRect(rectWin, rect); + +- // We can't use wxGetListCtrlSubItemRect() for the 0th subitem as 0 means +- // the entire row for this function, so we need to calculate it ourselves. +- if ( subItem == 0 && code == wxLIST_RECT_BOUNDS ) ++ // We can't trust the native LVM_GETSUBITEMRECT for LVIR_BOUNDS because: ++ // - subitem 0 returns the entire row bounds, not just column 0 ++ // - when column 0 is narrower than the icon, the native control clamps ++ // all subitems' left edge to at least the icon width, misaligning them ++ // ++ // So for all subitems we calculate x and width from GetColumnWidth() in ++ // visual (column order) order, which always reflects the real column sizes. ++ if ( subItem != wxLIST_GETSUBITEMRECT_WHOLEITEM && code == wxLIST_RECT_BOUNDS ) + { +- // Because the columns can be reordered, we need to sum the widths of +- // all preceding columns to get the correct x position. ++ // Start from the row's left edge (which accounts for scrolling). ++ RECT rowRect; ++ wxGetListCtrlItemRect(GetHwnd(), item, LVIR_BOUNDS, rowRect); ++ int x = rowRect.left; ++ + for ( auto col : GetColumnsOrder() ) + { + if ( col == subItem ) + break; + +- rect.x += GetColumnWidth(col); ++ x += GetColumnWidth(col); + } + ++ rect.x = x; + rect.width = GetColumnWidth(subItem); + } + diff --git a/dependencies/vcpkg_overlay_ports/wxwidgets/portfile.cmake b/dependencies/vcpkg_overlay_ports/wxwidgets/portfile.cmake index d9962958..a0c801df 100644 --- a/dependencies/vcpkg_overlay_ports/wxwidgets/portfile.cmake +++ b/dependencies/vcpkg_overlay_ports/wxwidgets/portfile.cmake @@ -1,8 +1,8 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO wxWidgets/wxWidgets - REF "bfd436b" - SHA512 bd3fd6d0d0db3b6fa34eceae1119e21ffd2f62221dcd249f8b8b82a6e65d83a05101e4e1e4ca9b9c4d7937add73b113bb029b03b05d2c3d87d17c1922d800a24 + REF "v${VERSION}" + SHA512 c47460c8bc150445e7774a287a79bd70b6d96b91ea46b51f238a317f068e466d570c5a94e2204d112e42da781a5cef98236718efff4b7f539d61a12ceaf65eb7 HEAD_REF master PATCHES install-layout.patch @@ -12,14 +12,15 @@ vcpkg_from_github( fix-pcre2.patch gtk3-link-libraries.patch sdl2.patch + fix-listctrl-layout.patch ) # Submodule dependencies vcpkg_from_github( OUT_SOURCE_PATH lexilla_SOURCE_PATH REPO wxWidgets/lexilla - REF "27c20a6ae5eebf418debeac0166052ed6fb653bc" - SHA512 7e5de7f664509473b691af8261fca34c2687772faca7260eeba5f2984516e6f8edf88c27192e056c9dda996e2ad2c20f6d1dff1c4bd2f3c0d74852cb50ca424a + REF "0dbce0b418b8b3d2ef30304d0bf53ff58c07ed84" + SHA512 61f7b0217f4518121ecad32f015b53600e565bdd499d4020468cf6c8a533d516c6881115aa5e640afca5ea428535f47680cde426fa3dbabe9e4423b4526853fd HEAD_REF wx ) file(COPY "${lexilla_SOURCE_PATH}/" DESTINATION "${SOURCE_PATH}/src/stc/lexilla") diff --git a/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json b/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json index 132f4fe3..542027c8 100644 --- a/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json +++ b/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json @@ -1,6 +1,6 @@ { "name": "wxwidgets", - "version": "3.3.1", + "version": "3.3.2", "description": [ "Widget toolkit and tools library for creating graphical user interfaces (GUIs) for cross-platform applications. ", "Set WXWIDGETS_USE_STL in a custom triplet to build with the wxUSE_STL build option.", diff --git a/src/gui/wxgui/components/wxGameList.cpp b/src/gui/wxgui/components/wxGameList.cpp index cf77119a..a1e5142d 100644 --- a/src/gui/wxgui/components/wxGameList.cpp +++ b/src/gui/wxgui/components/wxGameList.cpp @@ -304,7 +304,7 @@ int wxGameList::GetColumnDefaultWidth(int column) switch (column) { case ColumnIcon: - return kListIconWidth; + return kListIconWidth+2; case ColumnName: return DefaultColumnSize::name; case ColumnVersion: @@ -994,7 +994,7 @@ void wxGameList::ApplyGameListColumnWidths() const auto& config = GetWxGUIConfig(); wxWindowUpdateLocker lock(this); if(config.show_icon_column) - SetColumnWidth(ColumnIcon, kListIconWidth); + SetColumnWidth(ColumnIcon, kListIconWidth+2); else SetColumnWidth(ColumnIcon, 0); SetColumnWidth(ColumnName, config.column_width.name); From 0ff7d6ef0c0a4e13217f7b35abe226785735c604 Mon Sep 17 00:00:00 2001 From: Squall Leonhart Date: Sat, 14 Mar 2026 07:38:33 +1100 Subject: [PATCH 09/10] input: Fix logging crash in WPADGetInfoAsync due to mismatching type (#1832) --- src/Cafe/OS/libs/padscore/padscore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cafe/OS/libs/padscore/padscore.cpp b/src/Cafe/OS/libs/padscore/padscore.cpp index 646ea17f..cad9193a 100644 --- a/src/Cafe/OS/libs/padscore/padscore.cpp +++ b/src/Cafe/OS/libs/padscore/padscore.cpp @@ -134,7 +134,7 @@ void padscoreExport_WPADGetInfoAsync(PPCInterpreter_t* hCPU) ppcDefineParamU32(channel, 0); ppcDefineParamStructPtr(wpadInfo, WPADInfo_t, 1); ppcDefineParamMPTR(callbackFunc, 2); - cemuLog_log(LogType::InputAPI, "WPADGetInfoAsync({}, 0x{:08x}, 0x{:08x})", channel, fmt::ptr(wpadInfo), callbackFunc); + cemuLog_log(LogType::InputAPI, "WPADGetInfoAsync({}, 0x{:08x}, 0x{:08x})", channel, memory_getVirtualOffsetFromPointer(wpadInfo), callbackFunc); if (channel < InputManager::kMaxWPADControllers) { From 6312fb936cbec3ed62f85dba7206313638aafe4c Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sat, 14 Mar 2026 16:24:20 +0000 Subject: [PATCH 10/10] ci: Update github actions (#1836) --- .github/workflows/build.yml | 20 +++++++++---------- .github/workflows/deploy_release.yml | 12 +++++------ .../workflows/determine_release_version.yml | 2 +- .github/workflows/generate_pot.yml | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af560d21..31b47e55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: "Checkout repo" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: "recursive" fetch-depth: 0 @@ -83,7 +83,7 @@ jobs: run: mv bin/Cemu_release bin/Cemu - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-bin-linux-${{ matrix.arch }} path: ./bin/Cemu @@ -103,9 +103,9 @@ jobs: needs: build-ubuntu steps: - name: Checkout Upstream Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-bin-linux-${{ matrix.arch }} path: bin @@ -122,7 +122,7 @@ jobs: dist/linux/appimage.sh ${{ runner.arch }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-appimage-${{ matrix.arch }} path: artifacts @@ -131,7 +131,7 @@ jobs: runs-on: windows-2022 steps: - name: "Checkout repo" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: "recursive" @@ -193,13 +193,13 @@ jobs: makensis /DPRODUCT_VERSION=${{ inputs.next_version_major }}.${{ inputs.next_version_minor }} installer.nsi - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-bin-windows-x64 path: ./bin/Cemu.exe - name: Upload NSIS Installer - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-installer-windows-x64 path: ./src/resource/cemu-${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}-windows-x64-installer.exe @@ -211,7 +211,7 @@ jobs: arch: [x86_64, arm64] steps: - name: "Checkout repo" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: "recursive" @@ -289,7 +289,7 @@ jobs: rm bin/tmp.dmg - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-bin-macos-${{ matrix.arch }} path: ./bin/Cemu.dmg diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index dfcce040..caa4a57a 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-22.04 needs: [call-release-build, calculate-version] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -78,27 +78,27 @@ jobs: if [ -n "${{ github.event.inputs.changelog9 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog9 }}\n"; fi echo -e "$CHANGELOG" echo "RELEASE_BODY=$CHANGELOG" >> $GITHUB_ENV - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-bin-linux-x64 path: cemu-bin-linux-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-appimage-x64 path: cemu-appimage-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-bin-windows-x64 path: cemu-bin-windows-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-installer-windows-x64 path: cemu-installer-windows-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: pattern: cemu-bin-macos* path: cemu-macos diff --git a/.github/workflows/determine_release_version.yml b/.github/workflows/determine_release_version.yml index be606941..a2efcd17 100644 --- a/.github/workflows/determine_release_version.yml +++ b/.github/workflows/determine_release_version.yml @@ -23,7 +23,7 @@ jobs: next_version_minor: ${{ steps.calculate_next_version.outputs.next_version_minor }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: Get all releases id: get_all_releases diff --git a/.github/workflows/generate_pot.yml b/.github/workflows/generate_pot.yml index bd42de46..f8f1864c 100644 --- a/.github/workflows/generate_pot.yml +++ b/.github/workflows/generate_pot.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout repo" - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: "Install gettext" run: | @@ -36,7 +36,7 @@ jobs: -o cemu.pot - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: POT file path: ./cemu.pot