Merge branch 'main' into metal

This commit is contained in:
SamoZ256 2025-12-05 14:10:35 +01:00 committed by GitHub
commit bdecf3c00a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
99 changed files with 3886 additions and 2698 deletions

View file

@ -66,9 +66,9 @@ void _stripPathFilename(fs::path& path)
path = path.parent_path();
}
std::list<fs::path> _getCachesPaths(const TitleId& titleId)
std::vector<fs::path> _getCachesPaths(const TitleId& titleId)
{
std::list<fs::path> cachePaths{
std::vector<fs::path> cachePaths{
ActiveSettings::GetCachePath(L"shaderCache/driver/vk/{:016x}.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_spirv.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_gl.bin", titleId),
@ -78,12 +78,12 @@ std::list<fs::path> _getCachesPaths(const TitleId& titleId)
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_vkpipeline.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_mtlpipeline.bin", titleId)};
cachePaths.remove_if(
[](const fs::path& cachePath)
{
std::error_code ec;
return !fs::exists(cachePath, ec);
});
cachePaths.erase(std::remove_if(cachePaths.begin(), cachePaths.end(),
[](const fs::path& cachePath) {
std::error_code ec;
return !fs::exists(cachePath, ec);
}),
cachePaths.end());
return cachePaths;
}
@ -144,17 +144,15 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
const auto& config = GetWxGUIConfig();
char transparent_bitmap[kIconWidth * kIconWidth * 4] = {};
memset((void*)transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap));
memset(transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap));
wxBitmap blank(transparent_bitmap, kIconWidth, kIconWidth);
m_image_list_data = {};
m_image_list_data.emplace_back(wxBitmapBundle::FromBitmap(blank));
wxListCtrl::SetNormalImages(m_image_list_data);
m_image_list_data.Add(blank);
wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL);
m_image_list_small_data = {};
wxBitmap::Rescale(blank, {kListIconWidth, kListIconWidth});
m_image_list_small_data.emplace_back(wxBitmapBundle::FromBitmap(blank));
wxListCtrl::SetSmallImages(m_image_list_small_data);
m_image_list_small_data.Add(blank);
wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL);
InsertColumn(ColumnHiddenName, "", wxLIST_FORMAT_LEFT, 0);
if(config.show_icon_column)
@ -305,12 +303,6 @@ int wxGameList::GetColumnDefaultWidth(int column)
switch (column)
{
case ColumnIcon:
#if __WXMSW__
// note: this is another workaround that could be used to fix the icon offset, but instead of this there's a vcpkg patch for wxWidgets that fixes the icon offset
// wxWidgets offsets the icon in the REPORT view so it's cut off if the column width is set to 64px, see https://github.com/wxWidgets/wxWidgets/blob/09f433faf39aab3f25b3c564b82448bb845fae56/src/msw/listctrl.cpp#L3091
// so add 6px to the column width to compensate, and add another 6px to the right side so that it has equal whitespace on both sides
// return kListIconWidth + 6 + 6;
#endif
return kListIconWidth;
case ColumnName:
return DefaultColumnSize::name;
@ -353,7 +345,7 @@ bool wxGameList::IsVisible(long item) const
return visible;
}
void wxGameList::ReloadGameEntries(bool cached)
void wxGameList::ReloadGameEntries()
{
wxWindowUpdateLocker windowlock(this);
DeleteAllItems();
@ -413,11 +405,13 @@ void wxGameList::SetStyle(Style style, bool save)
switch(style)
{
case Style::kIcons:
wxListCtrl::SetNormalImages(m_image_list_data);
wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL);
break;
case Style::kSmallIcons:
wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_NORMAL);
break;
case Style::kList:
wxListCtrl::SetSmallImages(m_image_list_small_data);
wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL);
break;
}
@ -446,7 +440,7 @@ long wxGameList::GetStyleFlags(Style style) const
switch (style)
{
case Style::kList:
return (wxLC_SINGLE_SEL | wxLC_REPORT);
return (wxLC_SINGLE_SEL | wxLC_VRULES | wxLC_REPORT);
case Style::kIcons:
return (wxLC_SINGLE_SEL | wxLC_ICON);
case Style::kSmallIcons:
@ -461,25 +455,22 @@ void wxGameList::UpdateItemColors(sint32 startIndex)
{
wxWindowUpdateLocker lock(this);
wxColour bgColourPrimary = GetBackgroundColour();
wxColour bgColourSecondary = wxHelper::CalculateAccentColour(bgColourPrimary);
for (int i = startIndex; i < GetItemCount(); ++i)
{
const auto titleId = (uint64)GetItemData(i);
const uint64 titleId = GetItemData(i);
if (GetConfig().IsGameListFavorite(titleId))
{
SetItemBackgroundColour(i, kFavoriteColor);
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
else if ((i&1) != 0)
else if ((i % 2) != 0)
{
SetItemBackgroundColour(i, bgColourPrimary);
SetItemBackgroundColour(i, kPrimaryColor);
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
else
{
SetItemBackgroundColour(i, bgColourSecondary);
SetItemBackgroundColour(i, kAlternateColor);
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
}
@ -487,7 +478,7 @@ void wxGameList::UpdateItemColors(sint32 startIndex)
static inline int order_to_int(const std::weak_ordering &wo)
{
// no easy conversion seems to exists in C++20
// no easy conversion seems to exist in C++20
if (wo == std::weak_ordering::less)
return -1;
else if (wo == std::weak_ordering::greater)
@ -499,27 +490,24 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2,
{
auto titleLastPlayed = [](uint64_t id)
{
iosu::pdm::GameListStat playTimeStat{};
iosu::pdm::GetStatForGamelist(id, playTimeStat);
return playTimeStat;
iosu::pdm::GameListStat playTimeStat{};
iosu::pdm::GetStatForGamelist(id, playTimeStat);
return playTimeStat;
};
auto titlePlayMinutes = [](uint64_t id)
{
iosu::pdm::GameListStat playTimeStat;
if (!iosu::pdm::GetStatForGamelist(id, playTimeStat))
return 0u;
return playTimeStat.numMinutesPlayed;
iosu::pdm::GameListStat playTimeStat;
if (!iosu::pdm::GetStatForGamelist(id, playTimeStat))
return 0u;
return playTimeStat.numMinutesPlayed;
};
auto titleRegion = [](uint64_t id)
{
return CafeTitleList::GetGameInfo(id).GetRegion();
return CafeTitleList::GetGameInfo(id).GetRegion();
};
if (!sortData->asc)
std::swap(titleId1, titleId2);
switch(sortData->column)
{
default:
@ -548,7 +536,7 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2,
int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
{
const auto sort_data = (SortData*)sortData;
return order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data));
return sort_data->dir * order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data));
}
void wxGameList::SortEntries(int column)
@ -572,7 +560,7 @@ void wxGameList::SortEntries(int column)
case ColumnRegion:
case ColumnTitleID:
{
SortData data{this, ItemColumns{column}, ascending};
SortData data{this, ItemColumns{column}, ascending ? 1 : -1};
SortItems(SortFunction, (wxIntPtr)&data);
ShowSortIndicator(column, ascending);
break;
@ -843,8 +831,8 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
int icon_small;
if (!QueryIconForTitle(title_id, icon_large, icon_small))
break;
auto icon = m_image_list_data[icon_large];
auto newClipboardData = wxBitmapDataObject(icon.GetBitmap(icon.GetDefaultSize()));
auto icon = m_image_list_data.GetIcon(icon_large);
auto newClipboardData = wxBitmapDataObject(icon);
wxClipboard::Get()->SetData(&newClipboardData);
wxClipboard::Get()->Close();
}
@ -857,7 +845,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
switch (event.GetId())
{
case kContextMenuRefreshGames:
ReloadGameEntries(false);
ReloadGameEntries();
break;
case kContextMenuStyleList:
SetStyle(Style::kList);
@ -1005,7 +993,7 @@ void wxGameList::ApplyGameListColumnWidths()
const auto& config = GetWxGUIConfig();
wxWindowUpdateLocker lock(this);
if(config.show_icon_column)
SetColumnWidth(ColumnIcon, GetColumnDefaultWidth(ColumnIcon));
SetColumnWidth(ColumnIcon, kListIconWidth);
else
SetColumnWidth(ColumnIcon, 0);
SetColumnWidth(ColumnName, config.column_width.name);
@ -1120,19 +1108,6 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event)
TitleId baseTitleId = gameInfo.GetBaseTitleId();
bool isNewEntry = false;
if (m_style == Style::kIcons)
{
wxListCtrl::SetNormalImages(m_image_list_data);
}
else if (m_style == Style::kSmallIcons)
{
wxListCtrl::SetSmallImages(m_image_list_small_data);
}
else if (m_style == Style::kList)
{
wxListCtrl::SetSmallImages(m_image_list_small_data);
}
int icon = -1; /* 0 is the default empty icon */
int icon_small = -1; /* 0 is the default empty icon */
QueryIconForTitle(baseTitleId, icon, icon_small);
@ -1283,7 +1258,7 @@ void wxGameList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt)
}
}
void wxGameList::RemoveCache(const std::list<fs::path>& cachePaths, const std::string& titleName)
void wxGameList::RemoveCache(const std::vector<fs::path>& cachePaths, const std::string& titleName)
{
wxMessageDialog dialog(this, formatWxString(_("Remove the shader caches for {}?"), titleName), _("Remove shader caches"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION);
dialog.SetYesNoLabels(_("Yes"), _("No"));
@ -1291,7 +1266,7 @@ void wxGameList::RemoveCache(const std::list<fs::path>& cachePaths, const std::s
const auto dialogResult = dialog.ShowModal();
if (dialogResult != wxID_YES)
return;
std::list<std::string> errs;
std::vector<std::string> errs;
for (const fs::path& cachePath : cachePaths)
{
if (std::error_code ec; !fs::remove(cachePath, ec))
@ -1348,18 +1323,17 @@ void wxGameList::AsyncWorkerThread()
wxMemoryInputStream tmp_stream(tgaData->data(), tgaData->size());
const wxImage image(tmp_stream);
// todo - is wxImageList thread safe?
m_image_list_data.emplace_back(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC));
m_image_list_small_data.emplace_back(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC));
int icon = m_image_list_data.Add(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC));
int icon_small = m_image_list_small_data.Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC));
// store in cache
m_icon_cache_mtx.lock();
m_icon_cache.try_emplace(titleId, m_image_list_data.size() - 1, m_image_list_small_data.size() - 1);
m_icon_cache.try_emplace(titleId, icon, icon_small);
m_icon_cache_mtx.unlock();
iconSuccessfullyLoaded = true;
}
else
{
cemuLog_log(LogType::Force, "Failed to load icon for title {:016x}", titleId);
cemu_assert_debug(false);
}
titleInfo.Unmount(tempMountPath);
// notify UI about loaded icon
@ -1418,9 +1392,9 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
// Obtain and convert icon
[&]()
{
int iconIndex, smallIconIndex;
int iconIdx, smallIconIdx;
if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex))
if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx))
{
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
@ -1436,8 +1410,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId());
wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value()));
const auto icon = m_image_list_data[iconIndex];
wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)};
const auto icon = m_image_list_data.GetIcon(iconIdx);
wxBitmap bitmap{icon};
wxImage image = bitmap.ConvertToImage();
wxPNGHandler pngHandler;
if (!pngHandler.SaveFile(&image, pngFileStream, false))
@ -1511,9 +1485,9 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
// Obtain and convert icon
[&]()
{
int iconIndex, smallIconIndex;
int iconIdx, smallIconIdx;
if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex))
if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx))
{
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
@ -1529,8 +1503,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId());
wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value()));
const auto icon = m_image_list_data[iconIndex];
wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)};
const auto icon = m_image_list_data.GetIcon(iconIdx);
wxBitmap bitmap{icon};
wxImage image = bitmap.ConvertToImage();
wxPNGHandler pngHandler;
if (!pngHandler.SaveFile(&image, pngFileStream, false))
@ -1638,14 +1612,14 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
}
const auto icon = m_image_list_data[iconIdx];
const auto icon = m_image_list_data.GetIcon(iconIdx);
const auto folder = ActiveSettings::GetUserDataPath("icons");
if (!fs::exists(folder) && !fs::create_directories(folder))
{
cemuLog_log(LogType::Force, "Failed to create icon directory");
return;
}
wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)};
wxBitmap bitmap{icon};
icon_path = folder / fmt::format("{:016x}.ico", titleId);
auto stream = wxFileOutputStream(icon_path->wstring());