mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 08:22:32 +00:00
Merge branch 'ime' of https://github.com/LNDF/shadPS4 into ime
This commit is contained in:
commit
633f99e62a
6
.github/linux-appimage-qt.sh
vendored
6
.github/linux-appimage-qt.sh
vendored
@ -14,12 +14,10 @@ export PATH="$Qt6_DIR/bin:$PATH"
|
|||||||
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||||
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||||
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
|
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
|
||||||
wget -q https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh
|
|
||||||
|
|
||||||
chmod a+x linuxdeploy-x86_64.AppImage
|
chmod a+x linuxdeploy-x86_64.AppImage
|
||||||
chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
|
chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
|
||||||
chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh
|
chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh
|
||||||
chmod a+x linuxdeploy-plugin-gstreamer.sh
|
|
||||||
|
|
||||||
# Build AppImage
|
# Build AppImage
|
||||||
./linuxdeploy-x86_64.AppImage --appdir AppDir
|
./linuxdeploy-x86_64.AppImage --appdir AppDir
|
||||||
@ -27,5 +25,7 @@ chmod a+x linuxdeploy-plugin-gstreamer.sh
|
|||||||
|
|
||||||
cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin
|
cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin
|
||||||
|
|
||||||
./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt --plugin gstreamer --output appimage
|
./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt
|
||||||
|
rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so
|
||||||
|
./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage
|
||||||
mv Shadps4-x86_64.AppImage Shadps4-qt.AppImage
|
mv Shadps4-x86_64.AppImage Shadps4-qt.AppImage
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
namespace Libraries::NpTrophy {
|
namespace Libraries::NpTrophy {
|
||||||
|
|
||||||
static TrophyUI g_trophy_ui;
|
|
||||||
|
|
||||||
std::string game_serial;
|
std::string game_serial;
|
||||||
|
|
||||||
static constexpr auto MaxTrophyHandles = 4u;
|
static constexpr auto MaxTrophyHandles = 4u;
|
||||||
@ -223,6 +221,14 @@ int PS4_SYSV_ABI sceNpTrophyGetGameIcon(OrbisNpTrophyContext context, OrbisNpTro
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GameTrophyInfo {
|
||||||
|
uint32_t num_groups;
|
||||||
|
uint32_t num_trophies;
|
||||||
|
uint32_t num_trophies_by_rarity[5];
|
||||||
|
uint32_t unlocked_trophies;
|
||||||
|
uint32_t unlocked_trophies_by_rarity[5];
|
||||||
|
};
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
|
int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
|
||||||
OrbisNpTrophyGameDetails* details,
|
OrbisNpTrophyGameDetails* details,
|
||||||
OrbisNpTrophyGameData* data) {
|
OrbisNpTrophyGameData* data) {
|
||||||
@ -240,79 +246,66 @@ int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTro
|
|||||||
if (details->size != 0x4A0 || data->size != 0x20)
|
if (details->size != 0x4A0 || data->size != 0x20)
|
||||||
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
const auto trophyDir =
|
const auto trophy_dir =
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
||||||
|
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
|
||||||
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
|
|
||||||
|
|
||||||
if (result) {
|
ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
|
||||||
|
|
||||||
uint32_t numGroups = 0;
|
GameTrophyInfo game_info{};
|
||||||
uint32_t numTrophies = 0;
|
|
||||||
uint32_t numTrophiesByRarity[5];
|
|
||||||
numTrophiesByRarity[1] = 0;
|
|
||||||
numTrophiesByRarity[2] = 0;
|
|
||||||
numTrophiesByRarity[3] = 0;
|
|
||||||
numTrophiesByRarity[4] = 0;
|
|
||||||
uint32_t unlockedTrophies = 0;
|
|
||||||
uint32_t unlockedTrophiesByRarity[5];
|
|
||||||
unlockedTrophiesByRarity[1] = 0;
|
|
||||||
unlockedTrophiesByRarity[2] = 0;
|
|
||||||
unlockedTrophiesByRarity[3] = 0;
|
|
||||||
unlockedTrophiesByRarity[4] = 0;
|
|
||||||
|
|
||||||
auto trophyconf = doc.child("trophyconf");
|
auto trophyconf = doc.child("trophyconf");
|
||||||
for (pugi::xml_node_iterator it = trophyconf.children().begin();
|
for (const pugi::xml_node& node : trophyconf.children()) {
|
||||||
it != trophyconf.children().end(); ++it) {
|
std::string_view node_name = node.name();
|
||||||
|
|
||||||
if (std::string(it->name()) == "title-name") {
|
if (node_name == "title-name") {
|
||||||
strncpy(details->title, it->text().as_string(),
|
strncpy(details->title, node.text().as_string(), ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE);
|
||||||
ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::string(it->name()) == "title-detail") {
|
if (node_name == "title-detail") {
|
||||||
strncpy(details->description, it->text().as_string(),
|
strncpy(details->description, node.text().as_string(),
|
||||||
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
|
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::string(it->name()) == "group")
|
if (node_name == "group")
|
||||||
numGroups++;
|
game_info.num_groups++;
|
||||||
|
|
||||||
if (std::string(it->name()) == "trophy") {
|
if (node_name == "trophy") {
|
||||||
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
|
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
|
||||||
std::string currentTrophyGrade = it->attribute("ttype").value();
|
std::string_view current_trophy_grade = node.attribute("ttype").value();
|
||||||
|
|
||||||
numTrophies++;
|
if (current_trophy_grade.empty()) {
|
||||||
if (!currentTrophyGrade.empty()) {
|
continue;
|
||||||
int trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
|
|
||||||
numTrophiesByRarity[trophyGrade]++;
|
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
|
||||||
unlockedTrophies++;
|
|
||||||
unlockedTrophiesByRarity[trophyGrade]++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game_info.num_trophies++;
|
||||||
|
int trophy_grade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
|
||||||
|
game_info.num_trophies_by_rarity[trophy_grade]++;
|
||||||
|
|
||||||
|
if (current_trophy_unlockstate) {
|
||||||
|
game_info.unlocked_trophies++;
|
||||||
|
game_info.unlocked_trophies_by_rarity[trophy_grade]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
details->numGroups = numGroups;
|
details->num_groups = game_info.num_groups;
|
||||||
details->numTrophies = numTrophies;
|
details->num_trophies = game_info.num_trophies;
|
||||||
details->numPlatinum = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
details->num_platinum = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
||||||
details->numGold = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
details->num_gold = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
||||||
details->numSilver = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
details->num_silver = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
||||||
details->numBronze = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
details->num_bronze = game_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
||||||
data->unlockedTrophies = unlockedTrophies;
|
data->unlocked_trophies = game_info.unlocked_trophies;
|
||||||
data->unlockedPlatinum = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
data->unlocked_platinum = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
||||||
data->unlockedGold = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
data->unlocked_gold = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
||||||
data->unlockedSilver = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
data->unlocked_silver = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
||||||
data->unlockedBronze = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
data->unlocked_bronze = game_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
||||||
|
|
||||||
// maybe this should be 1 instead of 100?
|
// maybe this should be 1 instead of 100?
|
||||||
data->progressPercentage = 100;
|
data->progress_percentage = 100;
|
||||||
|
|
||||||
} else
|
|
||||||
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
|
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
@ -323,6 +316,13 @@ int PS4_SYSV_ABI sceNpTrophyGetGroupIcon(OrbisNpTrophyContext context, OrbisNpTr
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GroupTrophyInfo {
|
||||||
|
uint32_t num_trophies;
|
||||||
|
uint32_t num_trophies_by_rarity[5];
|
||||||
|
uint32_t unlocked_trophies;
|
||||||
|
uint32_t unlocked_trophies_by_rarity[5];
|
||||||
|
};
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
|
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
|
||||||
OrbisNpTrophyGroupId groupId,
|
OrbisNpTrophyGroupId groupId,
|
||||||
OrbisNpTrophyGroupDetails* details,
|
OrbisNpTrophyGroupDetails* details,
|
||||||
@ -341,89 +341,75 @@ int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTr
|
|||||||
if (details->size != 0x4A0 || data->size != 0x28)
|
if (details->size != 0x4A0 || data->size != 0x28)
|
||||||
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
const auto trophyDir =
|
const auto trophy_dir =
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
||||||
|
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
|
||||||
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
|
|
||||||
|
|
||||||
if (result) {
|
ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
|
||||||
|
|
||||||
uint32_t numGroups = 0;
|
GroupTrophyInfo group_info{};
|
||||||
uint32_t numTrophies = 0;
|
|
||||||
uint32_t numTrophiesByRarity[5];
|
|
||||||
numTrophiesByRarity[1] = 0;
|
|
||||||
numTrophiesByRarity[2] = 0;
|
|
||||||
numTrophiesByRarity[3] = 0;
|
|
||||||
numTrophiesByRarity[4] = 0;
|
|
||||||
uint32_t unlockedTrophies = 0;
|
|
||||||
uint32_t unlockedTrophiesByRarity[5];
|
|
||||||
unlockedTrophiesByRarity[1] = 0;
|
|
||||||
unlockedTrophiesByRarity[2] = 0;
|
|
||||||
unlockedTrophiesByRarity[3] = 0;
|
|
||||||
unlockedTrophiesByRarity[4] = 0;
|
|
||||||
|
|
||||||
auto trophyconf = doc.child("trophyconf");
|
auto trophyconf = doc.child("trophyconf");
|
||||||
for (pugi::xml_node_iterator it = trophyconf.children().begin();
|
for (const pugi::xml_node& node : trophyconf.children()) {
|
||||||
it != trophyconf.children().end(); ++it) {
|
std::string_view node_name = node.name();
|
||||||
|
|
||||||
if (std::string(it->name()) == "group") {
|
if (node_name == "group") {
|
||||||
numGroups++;
|
int current_group_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_GROUP_ID);
|
||||||
std::string currentGroupId = it->attribute("id").value();
|
if (current_group_id != ORBIS_NP_TROPHY_INVALID_GROUP_ID) {
|
||||||
if (!currentGroupId.empty()) {
|
if (current_group_id == groupId) {
|
||||||
if (std::stoi(currentGroupId) == groupId) {
|
std::string_view current_group_name = node.child("name").text().as_string();
|
||||||
std::string currentGroupName = it->child("name").text().as_string();
|
std::string_view current_group_description =
|
||||||
std::string currentGroupDescription =
|
node.child("detail").text().as_string();
|
||||||
it->child("detail").text().as_string();
|
|
||||||
|
|
||||||
strncpy(details->title, currentGroupName.c_str(),
|
strncpy(details->title, current_group_name.data(),
|
||||||
ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE);
|
ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE);
|
||||||
strncpy(details->description, currentGroupDescription.c_str(),
|
strncpy(details->description, current_group_description.data(),
|
||||||
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
|
ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->groupId = groupId;
|
details->group_id = groupId;
|
||||||
|
data->group_id = groupId;
|
||||||
|
|
||||||
if (std::string(it->name()) == "trophy") {
|
if (node_name == "trophy") {
|
||||||
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
|
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
|
||||||
std::string currentTrophyGrade = it->attribute("ttype").value();
|
std::string_view current_trophy_grade = node.attribute("ttype").value();
|
||||||
std::string currentTrophyGroupID = it->attribute("gid").value();
|
int current_trophy_group_id = node.attribute("gid").as_int(-1);
|
||||||
|
|
||||||
if (!currentTrophyGroupID.empty()) {
|
if (current_trophy_grade.empty()) {
|
||||||
if (std::stoi(currentTrophyGroupID) == groupId) {
|
continue;
|
||||||
numTrophies++;
|
|
||||||
if (!currentTrophyGrade.empty()) {
|
|
||||||
int trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
|
|
||||||
numTrophiesByRarity[trophyGrade]++;
|
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
|
||||||
unlockedTrophies++;
|
|
||||||
unlockedTrophiesByRarity[trophyGrade]++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current_trophy_group_id == groupId) {
|
||||||
|
group_info.num_trophies++;
|
||||||
|
int trophyGrade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
|
||||||
|
group_info.num_trophies_by_rarity[trophyGrade]++;
|
||||||
|
if (current_trophy_unlockstate) {
|
||||||
|
group_info.unlocked_trophies++;
|
||||||
|
group_info.unlocked_trophies_by_rarity[trophyGrade]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
details->numTrophies = numTrophies;
|
details->num_trophies = group_info.num_trophies;
|
||||||
details->numPlatinum = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
details->num_platinum = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
||||||
details->numGold = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
details->num_gold = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
||||||
details->numSilver = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
details->num_silver = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
||||||
details->numBronze = numTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
details->num_bronze = group_info.num_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
||||||
data->unlockedTrophies = unlockedTrophies;
|
data->unlocked_trophies = group_info.unlocked_trophies;
|
||||||
data->unlockedPlatinum = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
data->unlocked_platinum =
|
||||||
data->unlockedGold = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_PLATINUM];
|
||||||
data->unlockedSilver = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
data->unlocked_gold = group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_GOLD];
|
||||||
data->unlockedBronze = unlockedTrophiesByRarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
data->unlocked_silver = group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_SILVER];
|
||||||
|
data->unlocked_bronze = group_info.unlocked_trophies_by_rarity[ORBIS_NP_TROPHY_GRADE_BRONZE];
|
||||||
|
|
||||||
// maybe this should be 1 instead of 100?
|
// maybe this should be 1 instead of 100?
|
||||||
data->progressPercentage = 100;
|
data->progress_percentage = 100;
|
||||||
|
|
||||||
} else
|
|
||||||
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
|
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
@ -454,87 +440,48 @@ int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpT
|
|||||||
if (details->size != 0x498 || data->size != 0x18)
|
if (details->size != 0x498 || data->size != 0x18)
|
||||||
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
const auto trophyDir =
|
const auto trophy_dir =
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
||||||
|
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
|
||||||
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
|
|
||||||
|
ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
|
||||||
|
|
||||||
if (result) {
|
|
||||||
auto trophyconf = doc.child("trophyconf");
|
auto trophyconf = doc.child("trophyconf");
|
||||||
for (pugi::xml_node_iterator it = trophyconf.children().begin();
|
|
||||||
it != trophyconf.children().end(); ++it) {
|
|
||||||
|
|
||||||
if (std::string(it->name()) == "trophy") {
|
for (const pugi::xml_node& node : trophyconf.children()) {
|
||||||
std::string currentTrophyId = it->attribute("id").value();
|
std::string_view node_name = node.name();
|
||||||
if (std::stoi(currentTrophyId) == trophyId) {
|
|
||||||
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
|
|
||||||
std::string currentTrophyTimestamp = it->attribute("timestamp").value();
|
|
||||||
std::string currentTrophyGrade = it->attribute("ttype").value();
|
|
||||||
std::string currentTrophyGroupID = it->attribute("gid").value();
|
|
||||||
std::string currentTrophyHidden = it->attribute("hidden").value();
|
|
||||||
std::string currentTrophyName = it->child("name").text().as_string();
|
|
||||||
std::string currentTrophyDescription = it->child("detail").text().as_string();
|
|
||||||
|
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
if (node_name == "trophy") {
|
||||||
details->trophyId = trophyId;
|
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
|
||||||
if (currentTrophyGrade.empty()) {
|
if (current_trophy_id == trophyId) {
|
||||||
details->trophyGrade = ORBIS_NP_TROPHY_GRADE_UNKNOWN;
|
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
|
||||||
} else {
|
std::string_view current_trophy_grade = node.attribute("ttype").value();
|
||||||
details->trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
|
std::string_view current_trophy_name = node.child("name").text().as_string();
|
||||||
}
|
std::string_view current_trophy_description =
|
||||||
if (currentTrophyGroupID.empty()) {
|
node.child("detail").text().as_string();
|
||||||
details->groupId = ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID;
|
|
||||||
} else {
|
|
||||||
details->groupId = std::stoi(currentTrophyGroupID);
|
|
||||||
}
|
|
||||||
if (currentTrophyHidden == "yes") {
|
|
||||||
details->hidden = true;
|
|
||||||
} else {
|
|
||||||
details->hidden = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(details->name, currentTrophyName.c_str(),
|
uint64_t current_trophy_timestamp = node.attribute("timestamp").as_ullong();
|
||||||
ORBIS_NP_TROPHY_NAME_MAX_SIZE);
|
int current_trophy_groupid = node.attribute("gid").as_int(-1);
|
||||||
strncpy(details->description, currentTrophyDescription.c_str(),
|
bool current_trophy_hidden = node.attribute("hidden").as_bool();
|
||||||
|
|
||||||
|
details->trophy_id = trophyId;
|
||||||
|
details->trophy_grade = GetTrophyGradeFromChar(current_trophy_grade.at(0));
|
||||||
|
details->group_id = current_trophy_groupid;
|
||||||
|
details->hidden = current_trophy_hidden;
|
||||||
|
|
||||||
|
strncpy(details->name, current_trophy_name.data(), ORBIS_NP_TROPHY_NAME_MAX_SIZE);
|
||||||
|
strncpy(details->description, current_trophy_description.data(),
|
||||||
ORBIS_NP_TROPHY_DESCR_MAX_SIZE);
|
ORBIS_NP_TROPHY_DESCR_MAX_SIZE);
|
||||||
|
|
||||||
data->trophyId = trophyId;
|
data->trophy_id = trophyId;
|
||||||
data->unlocked = true;
|
data->unlocked = current_trophy_unlockstate;
|
||||||
data->timestamp.tick = std::stoull(currentTrophyTimestamp);
|
data->timestamp.tick = current_trophy_timestamp;
|
||||||
} else {
|
|
||||||
details->trophyId = trophyId;
|
|
||||||
if (currentTrophyGrade.empty()) {
|
|
||||||
details->trophyGrade = ORBIS_NP_TROPHY_GRADE_UNKNOWN;
|
|
||||||
} else {
|
|
||||||
details->trophyGrade = GetTrophyGradeFromChar(currentTrophyGrade.at(0));
|
|
||||||
}
|
|
||||||
if (currentTrophyGroupID.empty()) {
|
|
||||||
details->groupId = ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID;
|
|
||||||
} else {
|
|
||||||
details->groupId = std::stoi(currentTrophyGroupID);
|
|
||||||
}
|
|
||||||
if (currentTrophyHidden == "yes") {
|
|
||||||
details->hidden = true;
|
|
||||||
} else {
|
|
||||||
details->hidden = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(details->name, currentTrophyName.c_str(),
|
|
||||||
ORBIS_NP_TROPHY_NAME_MAX_SIZE);
|
|
||||||
strncpy(details->description, currentTrophyDescription.c_str(),
|
|
||||||
ORBIS_NP_TROPHY_DESCR_MAX_SIZE);
|
|
||||||
|
|
||||||
data->trophyId = trophyId;
|
|
||||||
data->unlocked = false;
|
|
||||||
data->timestamp.tick = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else
|
|
||||||
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
|
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
@ -555,35 +502,33 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
|
|||||||
|
|
||||||
ORBIS_NP_TROPHY_FLAG_ZERO(flags);
|
ORBIS_NP_TROPHY_FLAG_ZERO(flags);
|
||||||
|
|
||||||
const auto trophyDir =
|
const auto trophy_dir =
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
||||||
auto trophy_file = trophyDir / "trophy00" / "Xml" / "TROP.XML";
|
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
|
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
|
||||||
|
|
||||||
int numTrophies = 0;
|
ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
|
||||||
|
|
||||||
if (result) {
|
int num_trophies = 0;
|
||||||
auto trophyconf = doc.child("trophyconf");
|
auto trophyconf = doc.child("trophyconf");
|
||||||
for (pugi::xml_node_iterator it = trophyconf.children().begin();
|
|
||||||
it != trophyconf.children().end(); ++it) {
|
|
||||||
|
|
||||||
std::string currentTrophyId = it->attribute("id").value();
|
for (const pugi::xml_node& node : trophyconf.children()) {
|
||||||
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
|
std::string_view node_name = node.name();
|
||||||
|
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
|
||||||
|
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
|
||||||
|
|
||||||
if (std::string(it->name()) == "trophy") {
|
if (node_name == "trophy") {
|
||||||
numTrophies++;
|
num_trophies++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
if (current_trophy_unlockstate) {
|
||||||
ORBIS_NP_TROPHY_FLAG_SET(std::stoi(currentTrophyId), flags);
|
ORBIS_NP_TROPHY_FLAG_SET(current_trophy_id, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
|
|
||||||
|
|
||||||
*count = numTrophies;
|
*count = num_trophies;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,148 +857,116 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr
|
|||||||
if (platinumId == nullptr)
|
if (platinumId == nullptr)
|
||||||
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
const auto trophyDir =
|
const auto trophy_dir =
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
|
||||||
|
auto trophy_file = trophy_dir / "trophy00" / "Xml" / "TROP.XML";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
|
||||||
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
|
|
||||||
|
ASSERT_MSG(result, "Couldnt parse trophy XML : {}", result.description());
|
||||||
|
|
||||||
*platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID;
|
*platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID;
|
||||||
|
|
||||||
int numTrophies = 0;
|
int num_trophies = 0;
|
||||||
int numTrophiesUnlocked = 0;
|
int num_trophies_unlocked = 0;
|
||||||
|
pugi::xml_node platinum_node;
|
||||||
|
|
||||||
pugi::xml_node_iterator platinumIt;
|
|
||||||
int platinumTrophyGroup = -1;
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
auto trophyconf = doc.child("trophyconf");
|
auto trophyconf = doc.child("trophyconf");
|
||||||
for (pugi::xml_node_iterator it = trophyconf.children().begin();
|
|
||||||
it != trophyconf.children().end(); ++it) {
|
|
||||||
|
|
||||||
std::string currentTrophyId = it->attribute("id").value();
|
for (pugi::xml_node& node : trophyconf.children()) {
|
||||||
std::string currentTrophyName = it->child("name").text().as_string();
|
int current_trophy_id = node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
|
||||||
std::string currentTrophyDescription = it->child("detail").text().as_string();
|
bool current_trophy_unlockstate = node.attribute("unlockstate").as_bool();
|
||||||
std::string currentTrophyType = it->attribute("ttype").value();
|
const char* current_trophy_name = node.child("name").text().as_string();
|
||||||
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
|
std::string_view current_trophy_description = node.child("detail").text().as_string();
|
||||||
|
std::string_view current_trophy_type = node.attribute("ttype").value();
|
||||||
|
|
||||||
if (currentTrophyType == "P") {
|
if (current_trophy_type == "P") {
|
||||||
platinumIt = it;
|
platinum_node = node;
|
||||||
|
if (trophyId == current_trophy_id) {
|
||||||
if (std::string(platinumIt->attribute("gid").value()).empty()) {
|
|
||||||
platinumTrophyGroup = -1;
|
|
||||||
} else {
|
|
||||||
platinumTrophyGroup =
|
|
||||||
std::stoi(std::string(platinumIt->attribute("gid").value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trophyId == std::stoi(currentTrophyId)) {
|
|
||||||
return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK;
|
return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::string(it->name()) == "trophy") {
|
if (std::string_view(node.name()) == "trophy") {
|
||||||
if (platinumTrophyGroup == -1) {
|
if (node.attribute("pid").as_int(-1) != ORBIS_NP_TROPHY_INVALID_TROPHY_ID) {
|
||||||
if (std::string(it->attribute("gid").value()).empty()) {
|
num_trophies++;
|
||||||
numTrophies++;
|
if (current_trophy_unlockstate) {
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
num_trophies_unlocked++;
|
||||||
numTrophiesUnlocked++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!std::string(it->attribute("gid").value()).empty()) {
|
|
||||||
if (std::stoi(std::string(it->attribute("gid").value())) ==
|
|
||||||
platinumTrophyGroup) {
|
|
||||||
numTrophies++;
|
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
|
||||||
numTrophiesUnlocked++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::stoi(currentTrophyId) == trophyId) {
|
if (current_trophy_id == trophyId) {
|
||||||
LOG_INFO(Lib_NpTrophy, "Found trophy to unlock {} : {}",
|
if (current_trophy_unlockstate) {
|
||||||
it->child("name").text().as_string(),
|
|
||||||
it->child("detail").text().as_string());
|
|
||||||
if (currentTrophyUnlockState == "unlocked") {
|
|
||||||
LOG_INFO(Lib_NpTrophy, "Trophy already unlocked");
|
LOG_INFO(Lib_NpTrophy, "Trophy already unlocked");
|
||||||
return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
|
return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
|
||||||
} else {
|
} else {
|
||||||
if (std::string(it->attribute("unlockstate").value()).empty()) {
|
if (node.attribute("unlockstate").empty()) {
|
||||||
it->append_attribute("unlockstate") = "unlocked";
|
node.append_attribute("unlockstate") = "true";
|
||||||
} else {
|
} else {
|
||||||
it->attribute("unlockstate").set_value("unlocked");
|
node.attribute("unlockstate").set_value("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
Rtc::OrbisRtcTick trophyTimestamp;
|
Rtc::OrbisRtcTick trophyTimestamp;
|
||||||
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
||||||
|
|
||||||
if (std::string(it->attribute("timestamp").value()).empty()) {
|
if (node.attribute("timestamp").empty()) {
|
||||||
it->append_attribute("timestamp") =
|
node.append_attribute("timestamp") =
|
||||||
std::to_string(trophyTimestamp.tick).c_str();
|
std::to_string(trophyTimestamp.tick).c_str();
|
||||||
} else {
|
} else {
|
||||||
it->attribute("timestamp")
|
node.attribute("timestamp")
|
||||||
.set_value(std::to_string(trophyTimestamp.tick).c_str());
|
.set_value(std::to_string(trophyTimestamp.tick).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
g_trophy_ui.AddTrophyToQueue(trophyId, currentTrophyName);
|
std::string trophy_icon_file = "TROP";
|
||||||
|
trophy_icon_file.append(node.attribute("id").value());
|
||||||
|
trophy_icon_file.append(".PNG");
|
||||||
|
|
||||||
|
std::filesystem::path current_icon_path =
|
||||||
|
trophy_dir / "trophy00" / "Icons" / trophy_icon_file;
|
||||||
|
|
||||||
|
AddTrophyToQueue(current_icon_path, current_trophy_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::string(platinumIt->attribute("unlockstate").value()).empty()) {
|
if (!platinum_node.attribute("unlockstate").as_bool()) {
|
||||||
if ((numTrophies - 2) == numTrophiesUnlocked) {
|
if ((num_trophies - 1) == num_trophies_unlocked) {
|
||||||
|
if (platinum_node.attribute("unlockstate").empty()) {
|
||||||
platinumIt->append_attribute("unlockstate") = "unlocked";
|
platinum_node.append_attribute("unlockstate") = "true";
|
||||||
|
} else {
|
||||||
|
platinum_node.attribute("unlockstate").set_value("true");
|
||||||
|
}
|
||||||
|
|
||||||
Rtc::OrbisRtcTick trophyTimestamp;
|
Rtc::OrbisRtcTick trophyTimestamp;
|
||||||
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
||||||
|
|
||||||
if (std::string(platinumIt->attribute("timestamp").value()).empty()) {
|
if (platinum_node.attribute("timestamp").empty()) {
|
||||||
platinumIt->append_attribute("timestamp") =
|
platinum_node.append_attribute("timestamp") =
|
||||||
std::to_string(trophyTimestamp.tick).c_str();
|
std::to_string(trophyTimestamp.tick).c_str();
|
||||||
} else {
|
} else {
|
||||||
platinumIt->attribute("timestamp")
|
platinum_node.attribute("timestamp")
|
||||||
.set_value(std::to_string(trophyTimestamp.tick).c_str());
|
.set_value(std::to_string(trophyTimestamp.tick).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string platinumTrophyId = platinumIt->attribute("id").value();
|
int platinum_trophy_id =
|
||||||
std::string platinumTrophyName = platinumIt->child("name").text().as_string();
|
platinum_node.attribute("id").as_int(ORBIS_NP_TROPHY_INVALID_TROPHY_ID);
|
||||||
|
const char* platinum_trophy_name = platinum_node.child("name").text().as_string();
|
||||||
|
|
||||||
*platinumId = std::stoi(platinumTrophyId);
|
std::string platinum_icon_file = "TROP";
|
||||||
g_trophy_ui.AddTrophyToQueue(*platinumId, platinumTrophyName);
|
platinum_icon_file.append(platinum_node.attribute("id").value());
|
||||||
}
|
platinum_icon_file.append(".PNG");
|
||||||
} else if (std::string(platinumIt->attribute("unlockstate").value()) == "locked") {
|
|
||||||
if ((numTrophies - 2) == numTrophiesUnlocked) {
|
|
||||||
|
|
||||||
platinumIt->attribute("unlockstate").set_value("unlocked");
|
std::filesystem::path platinum_icon_path =
|
||||||
|
trophy_dir / "trophy00" / "Icons" / platinum_icon_file;
|
||||||
|
|
||||||
Rtc::OrbisRtcTick trophyTimestamp;
|
*platinumId = platinum_trophy_id;
|
||||||
Rtc::sceRtcGetCurrentTick(&trophyTimestamp);
|
AddTrophyToQueue(platinum_icon_path, platinum_trophy_name);
|
||||||
|
|
||||||
if (std::string(platinumIt->attribute("timestamp").value()).empty()) {
|
|
||||||
platinumIt->append_attribute("timestamp") =
|
|
||||||
std::to_string(trophyTimestamp.tick).c_str();
|
|
||||||
} else {
|
|
||||||
platinumIt->attribute("timestamp")
|
|
||||||
.set_value(std::to_string(trophyTimestamp.tick).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string platinumTrophyId = platinumIt->attribute("id").value();
|
|
||||||
std::string platinumTrophyName = platinumIt->child("name").text().as_string();
|
|
||||||
|
|
||||||
*platinumId = std::stoi(platinumTrophyId);
|
|
||||||
g_trophy_ui.AddTrophyToQueue(*platinumId, platinumTrophyName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.save_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
|
doc.save_file((trophy_dir.string() + "/trophy00/Xml/TROP.XML").c_str());
|
||||||
|
|
||||||
} else
|
|
||||||
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
|
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ bool ORBIS_NP_TROPHY_FLAG_ISSET(int32_t trophyId, OrbisNpTrophyFlagArray* p);
|
|||||||
|
|
||||||
struct OrbisNpTrophyData {
|
struct OrbisNpTrophyData {
|
||||||
size_t size;
|
size_t size;
|
||||||
OrbisNpTrophyId trophyId;
|
OrbisNpTrophyId trophy_id;
|
||||||
bool unlocked;
|
bool unlocked;
|
||||||
uint8_t reserved[3];
|
uint8_t reserved[3];
|
||||||
Rtc::OrbisRtcTick timestamp;
|
Rtc::OrbisRtcTick timestamp;
|
||||||
@ -66,9 +66,9 @@ constexpr int ORBIS_NP_TROPHY_INVALID_GROUP_ID = -2;
|
|||||||
|
|
||||||
struct OrbisNpTrophyDetails {
|
struct OrbisNpTrophyDetails {
|
||||||
size_t size;
|
size_t size;
|
||||||
OrbisNpTrophyId trophyId;
|
OrbisNpTrophyId trophy_id;
|
||||||
OrbisNpTrophyGrade trophyGrade;
|
OrbisNpTrophyGrade trophy_grade;
|
||||||
OrbisNpTrophyGroupId groupId;
|
OrbisNpTrophyGroupId group_id;
|
||||||
bool hidden;
|
bool hidden;
|
||||||
uint8_t reserved[3];
|
uint8_t reserved[3];
|
||||||
char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE];
|
char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE];
|
||||||
@ -77,46 +77,46 @@ struct OrbisNpTrophyDetails {
|
|||||||
|
|
||||||
struct OrbisNpTrophyGameData {
|
struct OrbisNpTrophyGameData {
|
||||||
size_t size;
|
size_t size;
|
||||||
uint32_t unlockedTrophies;
|
uint32_t unlocked_trophies;
|
||||||
uint32_t unlockedPlatinum;
|
uint32_t unlocked_platinum;
|
||||||
uint32_t unlockedGold;
|
uint32_t unlocked_gold;
|
||||||
uint32_t unlockedSilver;
|
uint32_t unlocked_silver;
|
||||||
uint32_t unlockedBronze;
|
uint32_t unlocked_bronze;
|
||||||
uint32_t progressPercentage;
|
uint32_t progress_percentage;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OrbisNpTrophyGameDetails {
|
struct OrbisNpTrophyGameDetails {
|
||||||
size_t size;
|
size_t size;
|
||||||
uint32_t numGroups;
|
uint32_t num_groups;
|
||||||
uint32_t numTrophies;
|
uint32_t num_trophies;
|
||||||
uint32_t numPlatinum;
|
uint32_t num_platinum;
|
||||||
uint32_t numGold;
|
uint32_t num_gold;
|
||||||
uint32_t numSilver;
|
uint32_t num_silver;
|
||||||
uint32_t numBronze;
|
uint32_t num_bronze;
|
||||||
char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE];
|
char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE];
|
||||||
char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE];
|
char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OrbisNpTrophyGroupData {
|
struct OrbisNpTrophyGroupData {
|
||||||
size_t size;
|
size_t size;
|
||||||
OrbisNpTrophyGroupId groupId;
|
OrbisNpTrophyGroupId group_id;
|
||||||
uint32_t unlockedTrophies;
|
uint32_t unlocked_trophies;
|
||||||
uint32_t unlockedPlatinum;
|
uint32_t unlocked_platinum;
|
||||||
uint32_t unlockedGold;
|
uint32_t unlocked_gold;
|
||||||
uint32_t unlockedSilver;
|
uint32_t unlocked_silver;
|
||||||
uint32_t unlockedBronze;
|
uint32_t unlocked_bronze;
|
||||||
uint32_t progressPercentage;
|
uint32_t progress_percentage;
|
||||||
uint8_t reserved[4];
|
uint8_t reserved[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OrbisNpTrophyGroupDetails {
|
struct OrbisNpTrophyGroupDetails {
|
||||||
size_t size;
|
size_t size;
|
||||||
OrbisNpTrophyGroupId groupId;
|
OrbisNpTrophyGroupId group_id;
|
||||||
uint32_t numTrophies;
|
uint32_t num_trophies;
|
||||||
uint32_t numPlatinum;
|
uint32_t num_platinum;
|
||||||
uint32_t numGold;
|
uint32_t num_gold;
|
||||||
uint32_t numSilver;
|
uint32_t num_silver;
|
||||||
uint32_t numBronze;
|
uint32_t num_bronze;
|
||||||
char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE];
|
char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE];
|
||||||
char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE];
|
char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE];
|
||||||
};
|
};
|
||||||
|
@ -2,15 +2,27 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/singleton.h"
|
||||||
#include "imgui/imgui_std.h"
|
#include "imgui/imgui_std.h"
|
||||||
#include "trophy_ui.h"
|
#include "trophy_ui.h"
|
||||||
|
|
||||||
using namespace ImGui;
|
using namespace ImGui;
|
||||||
using namespace Libraries::NpTrophy;
|
namespace Libraries::NpTrophy {
|
||||||
|
|
||||||
TrophyUI::TrophyUI() {
|
std::optional<TrophyUI> current_trophy_ui;
|
||||||
|
std::queue<TrophyInfo> trophy_queue;
|
||||||
|
std::mutex queueMtx;
|
||||||
|
|
||||||
|
TrophyUI::TrophyUI(std::filesystem::path trophyIconPath, std::string trophyName)
|
||||||
|
: trophy_name(trophyName) {
|
||||||
|
if (std::filesystem::exists(trophyIconPath)) {
|
||||||
|
trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Lib_NpTrophy, "Couldnt load trophy icon at {}", trophyIconPath.string());
|
||||||
|
}
|
||||||
AddLayer(this);
|
AddLayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,57 +30,65 @@ TrophyUI::~TrophyUI() {
|
|||||||
Finish();
|
Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Libraries::NpTrophy::TrophyUI::AddTrophyToQueue(int trophyId, std::string trophyName) {
|
|
||||||
TrophyInfo newInfo;
|
|
||||||
newInfo.trophyId = trophyId;
|
|
||||||
newInfo.trophyName = trophyName;
|
|
||||||
trophyQueue.push_back(newInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrophyUI::Finish() {
|
void TrophyUI::Finish() {
|
||||||
RemoveLayer(this);
|
RemoveLayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool displayingTrophy;
|
|
||||||
std::chrono::steady_clock::time_point trophyStartedTime;
|
|
||||||
|
|
||||||
void TrophyUI::Draw() {
|
void TrophyUI::Draw() {
|
||||||
const auto& io = GetIO();
|
const auto& io = GetIO();
|
||||||
|
|
||||||
const ImVec2 window_size{
|
const ImVec2 window_size{
|
||||||
std::min(io.DisplaySize.x, 200.f),
|
std::min(io.DisplaySize.x, 250.f),
|
||||||
std::min(io.DisplaySize.y, 75.f),
|
std::min(io.DisplaySize.y, 70.f),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (trophyQueue.size() != 0) {
|
|
||||||
if (!displayingTrophy) {
|
|
||||||
displayingTrophy = true;
|
|
||||||
trophyStartedTime = std::chrono::steady_clock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point timeNow = std::chrono::steady_clock::now();
|
|
||||||
std::chrono::seconds duration =
|
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(timeNow - trophyStartedTime);
|
|
||||||
|
|
||||||
if (duration.count() >= 5) {
|
|
||||||
trophyQueue.erase(trophyQueue.begin());
|
|
||||||
displayingTrophy = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trophyQueue.size() != 0) {
|
|
||||||
SetNextWindowSize(window_size);
|
SetNextWindowSize(window_size);
|
||||||
SetNextWindowCollapsed(false);
|
SetNextWindowCollapsed(false);
|
||||||
SetNextWindowPos(ImVec2(io.DisplaySize.x - 200, 50));
|
SetNextWindowPos(ImVec2(io.DisplaySize.x - 250, 50));
|
||||||
KeepNavHighlight();
|
KeepNavHighlight();
|
||||||
|
|
||||||
TrophyInfo currentTrophyInfo = trophyQueue[0];
|
|
||||||
if (Begin("Trophy Window", nullptr,
|
if (Begin("Trophy Window", nullptr,
|
||||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
||||||
ImGuiWindowFlags_NoInputs)) {
|
ImGuiWindowFlags_NoInputs)) {
|
||||||
Text("Trophy earned!");
|
if (trophy_icon) {
|
||||||
TextWrapped("%s", currentTrophyInfo.trophyName.c_str());
|
Image(trophy_icon.GetTexture().im_id, ImVec2(50, 50));
|
||||||
|
ImGui::SameLine();
|
||||||
|
} else {
|
||||||
|
// placeholder
|
||||||
|
const auto pos = GetCursorScreenPos();
|
||||||
|
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f},
|
||||||
|
GetColorU32(ImVec4{0.7f}));
|
||||||
|
ImGui::Indent(60);
|
||||||
|
}
|
||||||
|
TextWrapped("Trophy earned!\n%s", trophy_name.c_str());
|
||||||
}
|
}
|
||||||
End();
|
End();
|
||||||
|
|
||||||
|
trophy_timer -= io.DeltaTime;
|
||||||
|
if (trophy_timer <= 0) {
|
||||||
|
queueMtx.lock();
|
||||||
|
if (!trophy_queue.empty()) {
|
||||||
|
TrophyInfo next_trophy = trophy_queue.front();
|
||||||
|
trophy_queue.pop();
|
||||||
|
current_trophy_ui.emplace(next_trophy.trophy_icon_path, next_trophy.trophy_name);
|
||||||
|
} else {
|
||||||
|
current_trophy_ui.reset();
|
||||||
|
}
|
||||||
|
queueMtx.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddTrophyToQueue(std::filesystem::path trophyIconPath, std::string trophyName) {
|
||||||
|
queueMtx.lock();
|
||||||
|
if (current_trophy_ui.has_value()) {
|
||||||
|
TrophyInfo new_trophy;
|
||||||
|
new_trophy.trophy_icon_path = trophyIconPath;
|
||||||
|
new_trophy.trophy_name = trophyName;
|
||||||
|
trophy_queue.push(new_trophy);
|
||||||
|
} else {
|
||||||
|
current_trophy_ui.emplace(trophyIconPath, trophyName);
|
||||||
}
|
}
|
||||||
|
queueMtx.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Libraries::NpTrophy
|
@ -5,32 +5,36 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <queue>
|
||||||
|
|
||||||
#include "common/fixed_value.h"
|
#include "common/fixed_value.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/libraries/np_trophy/np_trophy.h"
|
#include "core/libraries/np_trophy/np_trophy.h"
|
||||||
#include "imgui/imgui_layer.h"
|
#include "imgui/imgui_layer.h"
|
||||||
|
#include "imgui/imgui_texture.h"
|
||||||
|
|
||||||
namespace Libraries::NpTrophy {
|
namespace Libraries::NpTrophy {
|
||||||
|
|
||||||
struct TrophyInfo {
|
|
||||||
int trophyId = -1;
|
|
||||||
std::string trophyName;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TrophyUI final : public ImGui::Layer {
|
class TrophyUI final : public ImGui::Layer {
|
||||||
std::vector<TrophyInfo> trophyQueue;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TrophyUI();
|
TrophyUI(std::filesystem::path trophyIconPath, std::string trophyName);
|
||||||
~TrophyUI() override;
|
~TrophyUI() override;
|
||||||
|
|
||||||
void AddTrophyToQueue(int trophyId, std::string trophyName);
|
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
void Draw() override;
|
void Draw() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string trophy_name;
|
||||||
|
float trophy_timer = 5.0f;
|
||||||
|
ImGui::RefCountedTexture trophy_icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TrophyInfo {
|
||||||
|
std::filesystem::path trophy_icon_path;
|
||||||
|
std::string trophy_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddTrophyToQueue(std::filesystem::path trophyIconPath, std::string trophyName);
|
||||||
|
|
||||||
}; // namespace Libraries::NpTrophy
|
}; // namespace Libraries::NpTrophy
|
@ -76,7 +76,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
|
|||||||
trpType.append(reader.attributes().value("ttype").toString());
|
trpType.append(reader.attributes().value("ttype").toString());
|
||||||
trpPid.append(reader.attributes().value("pid").toString());
|
trpPid.append(reader.attributes().value("pid").toString());
|
||||||
if (reader.attributes().hasAttribute("unlockstate")) {
|
if (reader.attributes().hasAttribute("unlockstate")) {
|
||||||
if (reader.attributes().value("unlockstate").toString() == "unlocked") {
|
if (reader.attributes().value("unlockstate").toString() == "true") {
|
||||||
trpUnlocked.append("unlocked");
|
trpUnlocked.append("unlocked");
|
||||||
} else {
|
} else {
|
||||||
trpUnlocked.append("locked");
|
trpUnlocked.append("locked");
|
||||||
|
@ -233,6 +233,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
|
ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
|
||||||
}
|
}
|
||||||
if (info.has_discard) {
|
if (info.has_discard) {
|
||||||
|
ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
|
||||||
ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
|
ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
|
||||||
}
|
}
|
||||||
if (info.stores.Get(IR::Attribute::Depth)) {
|
if (info.stores.Get(IR::Attribute::Depth)) {
|
||||||
|
@ -49,12 +49,13 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) {
|
|||||||
if (info.num_components == 1) {
|
if (info.num_components == 1) {
|
||||||
return info.id;
|
return info.id;
|
||||||
} else {
|
} else {
|
||||||
return ctx.OpAccessChain(ctx.output_f32, info.id, ctx.ConstU32(element));
|
return ctx.OpAccessChain(info.pointer_type, info.id, ctx.ConstU32(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case IR::Attribute::Position0: {
|
case IR::Attribute::Position0: {
|
||||||
return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, ctx.ConstU32(element));
|
return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, ctx.ConstU32(element));
|
||||||
|
}
|
||||||
case IR::Attribute::Position1:
|
case IR::Attribute::Position1:
|
||||||
case IR::Attribute::Position2:
|
case IR::Attribute::Position2:
|
||||||
case IR::Attribute::Position3: {
|
case IR::Attribute::Position3: {
|
||||||
@ -70,18 +71,48 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) {
|
|||||||
case IR::Attribute::RenderTarget6:
|
case IR::Attribute::RenderTarget6:
|
||||||
case IR::Attribute::RenderTarget7: {
|
case IR::Attribute::RenderTarget7: {
|
||||||
const u32 index = u32(attr) - u32(IR::Attribute::RenderTarget0);
|
const u32 index = u32(attr) - u32(IR::Attribute::RenderTarget0);
|
||||||
if (ctx.frag_num_comp[index] > 1) {
|
const auto& info{ctx.frag_outputs.at(index)};
|
||||||
return ctx.OpAccessChain(ctx.output_f32, ctx.frag_color[index], ctx.ConstU32(element));
|
if (info.num_components > 1) {
|
||||||
|
return ctx.OpAccessChain(info.pointer_type, info.id, ctx.ConstU32(element));
|
||||||
} else {
|
} else {
|
||||||
return ctx.frag_color[index];
|
return info.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case IR::Attribute::Depth:
|
case IR::Attribute::Depth:
|
||||||
return ctx.frag_depth;
|
return ctx.frag_depth;
|
||||||
default:
|
default:
|
||||||
throw NotImplementedException("Read attribute {}", attr);
|
throw NotImplementedException("Write attribute {}", attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<Id, bool> OutputAttrComponentType(EmitContext& ctx, IR::Attribute attr) {
|
||||||
|
if (IR::IsParam(attr)) {
|
||||||
|
const u32 index{u32(attr) - u32(IR::Attribute::Param0)};
|
||||||
|
const auto& info{ctx.output_params.at(index)};
|
||||||
|
return {info.component_type, info.is_integer};
|
||||||
|
}
|
||||||
|
switch (attr) {
|
||||||
|
case IR::Attribute::Position0:
|
||||||
|
case IR::Attribute::Position1:
|
||||||
|
case IR::Attribute::Position2:
|
||||||
|
case IR::Attribute::Position3:
|
||||||
|
case IR::Attribute::Depth:
|
||||||
|
return {ctx.F32[1], false};
|
||||||
|
case IR::Attribute::RenderTarget0:
|
||||||
|
case IR::Attribute::RenderTarget1:
|
||||||
|
case IR::Attribute::RenderTarget2:
|
||||||
|
case IR::Attribute::RenderTarget3:
|
||||||
|
case IR::Attribute::RenderTarget4:
|
||||||
|
case IR::Attribute::RenderTarget5:
|
||||||
|
case IR::Attribute::RenderTarget6:
|
||||||
|
case IR::Attribute::RenderTarget7: {
|
||||||
|
const u32 index = u32(attr) - u32(IR::Attribute::RenderTarget0);
|
||||||
|
const auto& info{ctx.frag_outputs.at(index)};
|
||||||
|
return {info.component_type, info.is_integer};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw NotImplementedException("Write attribute {}", attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
@ -156,17 +187,21 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp) {
|
|||||||
// Attribute is disabled or varying component is not written
|
// Attribute is disabled or varying component is not written
|
||||||
return ctx.ConstF32(comp == 3 ? 1.0f : 0.0f);
|
return ctx.ConstF32(comp == 3 ? 1.0f : 0.0f);
|
||||||
}
|
}
|
||||||
if (param.is_default) {
|
|
||||||
return ctx.OpCompositeExtract(param.component_type, param.id, comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.num_components > 1) {
|
Id result;
|
||||||
|
if (param.is_default) {
|
||||||
|
result = ctx.OpCompositeExtract(param.component_type, param.id, comp);
|
||||||
|
} else if (param.num_components > 1) {
|
||||||
const Id pointer{
|
const Id pointer{
|
||||||
ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))};
|
ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))};
|
||||||
return ctx.OpLoad(param.component_type, pointer);
|
result = ctx.OpLoad(param.component_type, pointer);
|
||||||
} else {
|
} else {
|
||||||
return ctx.OpLoad(param.component_type, param.id);
|
result = ctx.OpLoad(param.component_type, param.id);
|
||||||
}
|
}
|
||||||
|
if (param.is_integer) {
|
||||||
|
result = ctx.OpBitcast(ctx.F32[1], result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
} else {
|
} else {
|
||||||
const auto step_rate = EmitReadStepRate(ctx, param.id.value);
|
const auto step_rate = EmitReadStepRate(ctx, param.id.value);
|
||||||
const auto offset = ctx.OpIAdd(
|
const auto offset = ctx.OpIAdd(
|
||||||
@ -222,7 +257,12 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 elemen
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const Id pointer{OutputAttrPointer(ctx, attr, element)};
|
const Id pointer{OutputAttrPointer(ctx, attr, element)};
|
||||||
ctx.OpStore(pointer, ctx.OpBitcast(ctx.F32[1], value));
|
const auto component_type{OutputAttrComponentType(ctx, attr)};
|
||||||
|
if (component_type.second) {
|
||||||
|
ctx.OpStore(pointer, ctx.OpBitcast(component_type.first, value));
|
||||||
|
} else {
|
||||||
|
ctx.OpStore(pointer, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <u32 N>
|
template <u32 N>
|
||||||
|
@ -120,6 +120,7 @@ void EmitContext::DefineArithmeticTypes() {
|
|||||||
|
|
||||||
output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32");
|
output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32");
|
||||||
output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32");
|
output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32");
|
||||||
|
output_s32 = Name(TypePointer(spv::StorageClass::Output, S32[1]), "output_s32");
|
||||||
|
|
||||||
full_result_i32x2 = Name(TypeStruct(S32[1], S32[1]), "full_result_i32x2");
|
full_result_i32x2 = Name(TypeStruct(S32[1], S32[1]), "full_result_i32x2");
|
||||||
full_result_u32x2 = Name(TypeStruct(U32[1], U32[1]), "full_result_u32x2");
|
full_result_u32x2 = Name(TypeStruct(U32[1], U32[1]), "full_result_u32x2");
|
||||||
@ -151,21 +152,21 @@ const VectorIds& GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) {
|
|||||||
UNREACHABLE_MSG("Invalid attribute type {}", fmt);
|
UNREACHABLE_MSG("Invalid attribute type {}", fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id) {
|
EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id,
|
||||||
|
bool output) {
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case AmdGpu::NumberFormat::Float:
|
case AmdGpu::NumberFormat::Float:
|
||||||
case AmdGpu::NumberFormat::Unorm:
|
case AmdGpu::NumberFormat::Unorm:
|
||||||
case AmdGpu::NumberFormat::Snorm:
|
case AmdGpu::NumberFormat::Snorm:
|
||||||
case AmdGpu::NumberFormat::SnormNz:
|
case AmdGpu::NumberFormat::SnormNz:
|
||||||
return {id, input_f32, F32[1], 4};
|
|
||||||
case AmdGpu::NumberFormat::Uint:
|
|
||||||
return {id, input_u32, U32[1], 4};
|
|
||||||
case AmdGpu::NumberFormat::Sint:
|
|
||||||
return {id, input_s32, S32[1], 4};
|
|
||||||
case AmdGpu::NumberFormat::Sscaled:
|
case AmdGpu::NumberFormat::Sscaled:
|
||||||
return {id, input_f32, F32[1], 4};
|
|
||||||
case AmdGpu::NumberFormat::Uscaled:
|
case AmdGpu::NumberFormat::Uscaled:
|
||||||
return {id, input_f32, F32[1], 4};
|
case AmdGpu::NumberFormat::Srgb:
|
||||||
|
return {id, output ? output_f32 : input_f32, F32[1], 4, false};
|
||||||
|
case AmdGpu::NumberFormat::Uint:
|
||||||
|
return {id, output ? output_u32 : input_u32, U32[1], 4, true};
|
||||||
|
case AmdGpu::NumberFormat::Sint:
|
||||||
|
return {id, output ? output_s32 : input_s32, S32[1], 4, true};
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -236,9 +237,13 @@ void EmitContext::DefineInputs() {
|
|||||||
: 1;
|
: 1;
|
||||||
// Note that we pass index rather than Id
|
// Note that we pass index rather than Id
|
||||||
input_params[input.binding] = {
|
input_params[input.binding] = {
|
||||||
rate_idx, input_u32,
|
rate_idx,
|
||||||
U32[1], input.num_components,
|
input_u32,
|
||||||
false, input.instance_data_buf,
|
U32[1],
|
||||||
|
input.num_components,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
input.instance_data_buf,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
Id id{DefineInput(type, input.binding)};
|
Id id{DefineInput(type, input.binding)};
|
||||||
@ -247,7 +252,7 @@ void EmitContext::DefineInputs() {
|
|||||||
} else {
|
} else {
|
||||||
Name(id, fmt::format("vs_in_attr{}", input.binding));
|
Name(id, fmt::format("vs_in_attr{}", input.binding));
|
||||||
}
|
}
|
||||||
input_params[input.binding] = GetAttributeInfo(input.fmt, id);
|
input_params[input.binding] = GetAttributeInfo(input.fmt, id, false);
|
||||||
interfaces.push_back(id);
|
interfaces.push_back(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,10 +325,12 @@ void EmitContext::DefineOutputs() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const u32 num_components = info.stores.NumComponents(mrt);
|
const u32 num_components = info.stores.NumComponents(mrt);
|
||||||
frag_color[i] = DefineOutput(F32[num_components], i);
|
const AmdGpu::NumberFormat num_format{runtime_info.fs_info.color_buffers[i].num_format};
|
||||||
frag_num_comp[i] = num_components;
|
const Id type{GetAttributeType(*this, num_format)[num_components]};
|
||||||
Name(frag_color[i], fmt::format("frag_color{}", i));
|
const Id id = DefineOutput(type, i);
|
||||||
interfaces.push_back(frag_color[i]);
|
Name(id, fmt::format("frag_color{}", i));
|
||||||
|
frag_outputs[i] = GetAttributeInfo(num_format, id, true);
|
||||||
|
interfaces.push_back(id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -166,6 +166,7 @@ public:
|
|||||||
Id input_s32{};
|
Id input_s32{};
|
||||||
Id output_u32{};
|
Id output_u32{};
|
||||||
Id output_f32{};
|
Id output_f32{};
|
||||||
|
Id output_s32{};
|
||||||
|
|
||||||
boost::container::small_vector<Id, 16> interfaces;
|
boost::container::small_vector<Id, 16> interfaces;
|
||||||
|
|
||||||
@ -177,8 +178,6 @@ public:
|
|||||||
Id frag_coord{};
|
Id frag_coord{};
|
||||||
Id front_facing{};
|
Id front_facing{};
|
||||||
Id frag_depth{};
|
Id frag_depth{};
|
||||||
std::array<Id, 8> frag_color{};
|
|
||||||
std::array<u32, 8> frag_num_comp{};
|
|
||||||
Id clip_distances{};
|
Id clip_distances{};
|
||||||
Id cull_distances{};
|
Id cull_distances{};
|
||||||
|
|
||||||
@ -237,11 +236,13 @@ public:
|
|||||||
Id pointer_type;
|
Id pointer_type;
|
||||||
Id component_type;
|
Id component_type;
|
||||||
u32 num_components;
|
u32 num_components;
|
||||||
|
bool is_integer{};
|
||||||
bool is_default{};
|
bool is_default{};
|
||||||
s32 buffer_handle{-1};
|
s32 buffer_handle{-1};
|
||||||
};
|
};
|
||||||
std::array<SpirvAttribute, 32> input_params{};
|
std::array<SpirvAttribute, 32> input_params{};
|
||||||
std::array<SpirvAttribute, 32> output_params{};
|
std::array<SpirvAttribute, 32> output_params{};
|
||||||
|
std::array<SpirvAttribute, 8> frag_outputs{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DefineArithmeticTypes();
|
void DefineArithmeticTypes();
|
||||||
@ -254,7 +255,7 @@ private:
|
|||||||
void DefineImagesAndSamplers();
|
void DefineImagesAndSamplers();
|
||||||
void DefineSharedMemory();
|
void DefineSharedMemory();
|
||||||
|
|
||||||
SpirvAttribute GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id);
|
SpirvAttribute GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id, bool output);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Shader::Backend::SPIRV
|
} // namespace Shader::Backend::SPIRV
|
||||||
|
@ -25,7 +25,7 @@ void Translator::EmitExport(const GcnInst& inst) {
|
|||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
const u32 index = u32(attrib) - u32(IR::Attribute::RenderTarget0);
|
const u32 index = u32(attrib) - u32(IR::Attribute::RenderTarget0);
|
||||||
switch (runtime_info.fs_info.mrt_swizzles[index]) {
|
switch (runtime_info.fs_info.color_buffers[index].mrt_swizzle) {
|
||||||
case MrtSwizzle::Identity:
|
case MrtSwizzle::Identity:
|
||||||
return comp;
|
return comp;
|
||||||
case MrtSwizzle::Alt:
|
case MrtSwizzle::Alt:
|
||||||
|
@ -80,10 +80,16 @@ struct FragmentRuntimeInfo {
|
|||||||
auto operator<=>(const PsInput&) const noexcept = default;
|
auto operator<=>(const PsInput&) const noexcept = default;
|
||||||
};
|
};
|
||||||
boost::container::static_vector<PsInput, 32> inputs;
|
boost::container::static_vector<PsInput, 32> inputs;
|
||||||
std::array<MrtSwizzle, MaxColorBuffers> mrt_swizzles;
|
struct PsColorBuffer {
|
||||||
|
AmdGpu::NumberFormat num_format;
|
||||||
|
MrtSwizzle mrt_swizzle;
|
||||||
|
|
||||||
|
auto operator<=>(const PsColorBuffer&) const noexcept = default;
|
||||||
|
};
|
||||||
|
std::array<PsColorBuffer, MaxColorBuffers> color_buffers;
|
||||||
|
|
||||||
bool operator==(const FragmentRuntimeInfo& other) const noexcept {
|
bool operator==(const FragmentRuntimeInfo& other) const noexcept {
|
||||||
return std::ranges::equal(mrt_swizzles, other.mrt_swizzles) &&
|
return std::ranges::equal(color_buffers, other.color_buffers) &&
|
||||||
std::ranges::equal(inputs, other.inputs);
|
std::ranges::equal(inputs, other.inputs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -253,6 +253,13 @@ struct Liverpool {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ModeControl {
|
||||||
|
s32 msaa_enable : 1;
|
||||||
|
s32 vport_scissor_enable : 1;
|
||||||
|
s32 line_stripple_enable : 1;
|
||||||
|
s32 send_unlit_stiles_to_pkr : 1;
|
||||||
|
};
|
||||||
|
|
||||||
enum class ZOrder : u32 {
|
enum class ZOrder : u32 {
|
||||||
LateZ = 0,
|
LateZ = 0,
|
||||||
EarlyZLateZ = 1,
|
EarlyZLateZ = 1,
|
||||||
@ -559,29 +566,39 @@ struct Liverpool {
|
|||||||
s16 top_left_x;
|
s16 top_left_x;
|
||||||
s16 top_left_y;
|
s16 top_left_y;
|
||||||
};
|
};
|
||||||
union {
|
struct {
|
||||||
BitField<0, 15, u32> bottom_right_x;
|
s16 bottom_right_x;
|
||||||
BitField<16, 15, u32> bottom_right_y;
|
s16 bottom_right_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// From AMD spec: 'Negative numbers clamped to 0'
|
||||||
|
static s16 Clamp(s16 value) {
|
||||||
|
return std::max(s16(0), value);
|
||||||
|
}
|
||||||
|
|
||||||
u32 GetWidth() const {
|
u32 GetWidth() const {
|
||||||
return static_cast<u32>(bottom_right_x - top_left_x);
|
return static_cast<u32>(Clamp(bottom_right_x) - Clamp(top_left_x));
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetHeight() const {
|
u32 GetHeight() const {
|
||||||
return static_cast<u32>(bottom_right_y - top_left_y);
|
return static_cast<u32>(Clamp(bottom_right_y) - Clamp(top_left_y));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WindowOffset {
|
||||||
|
s32 window_x_offset : 16;
|
||||||
|
s32 window_y_offset : 16;
|
||||||
|
};
|
||||||
|
|
||||||
struct ViewportScissor {
|
struct ViewportScissor {
|
||||||
union {
|
union {
|
||||||
BitField<0, 15, s32> top_left_x;
|
BitField<0, 15, s32> top_left_x;
|
||||||
BitField<15, 15, s32> top_left_y;
|
BitField<16, 15, s32> top_left_y;
|
||||||
BitField<30, 1, s32> window_offset_disable;
|
BitField<31, 1, s32> window_offset_disable;
|
||||||
};
|
};
|
||||||
union {
|
struct {
|
||||||
BitField<0, 15, s32> bottom_right_x;
|
s16 bottom_right_x;
|
||||||
BitField<15, 15, s32> bottom_right_y;
|
s16 bottom_right_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 GetWidth() const {
|
u32 GetWidth() const {
|
||||||
@ -953,10 +970,14 @@ struct Liverpool {
|
|||||||
Scissor screen_scissor;
|
Scissor screen_scissor;
|
||||||
INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2);
|
INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2);
|
||||||
DepthBuffer depth_buffer;
|
DepthBuffer depth_buffer;
|
||||||
INSERT_PADDING_WORDS(0xA08E - 0xA018);
|
INSERT_PADDING_WORDS(0xA080 - 0xA018);
|
||||||
|
WindowOffset window_offset;
|
||||||
|
ViewportScissor window_scissor;
|
||||||
|
INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2);
|
||||||
ColorBufferMask color_target_mask;
|
ColorBufferMask color_target_mask;
|
||||||
ColorBufferMask color_shader_mask;
|
ColorBufferMask color_shader_mask;
|
||||||
INSERT_PADDING_WORDS(0xA094 - 0xA08E - 2);
|
ViewportScissor generic_scissor;
|
||||||
|
INSERT_PADDING_WORDS(2);
|
||||||
std::array<ViewportScissor, NumViewports> viewport_scissors;
|
std::array<ViewportScissor, NumViewports> viewport_scissors;
|
||||||
std::array<ViewportDepth, NumViewports> viewport_depths;
|
std::array<ViewportDepth, NumViewports> viewport_depths;
|
||||||
INSERT_PADDING_WORDS(0xA103 - 0xA0D4);
|
INSERT_PADDING_WORDS(0xA103 - 0xA0D4);
|
||||||
@ -994,7 +1015,9 @@ struct Liverpool {
|
|||||||
PolygonControl polygon_control;
|
PolygonControl polygon_control;
|
||||||
ViewportControl viewport_control;
|
ViewportControl viewport_control;
|
||||||
VsOutputControl vs_output_control;
|
VsOutputControl vs_output_control;
|
||||||
INSERT_PADDING_WORDS(0xA29E - 0xA207 - 2);
|
INSERT_PADDING_WORDS(0xA292 - 0xA207 - 1);
|
||||||
|
ModeControl mode_control;
|
||||||
|
INSERT_PADDING_WORDS(0xA29D - 0xA292 - 1);
|
||||||
u32 index_size;
|
u32 index_size;
|
||||||
u32 max_index_size;
|
u32 max_index_size;
|
||||||
IndexBufferType index_buffer_type;
|
IndexBufferType index_buffer_type;
|
||||||
@ -1206,8 +1229,11 @@ static_assert(GFX6_3D_REG_INDEX(depth_htile_data_base) == 0xA005);
|
|||||||
static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C);
|
static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C);
|
||||||
static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010);
|
static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010);
|
||||||
static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017);
|
static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017);
|
||||||
|
static_assert(GFX6_3D_REG_INDEX(window_offset) == 0xA080);
|
||||||
|
static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081);
|
||||||
static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E);
|
static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E);
|
||||||
static_assert(GFX6_3D_REG_INDEX(color_shader_mask) == 0xA08F);
|
static_assert(GFX6_3D_REG_INDEX(color_shader_mask) == 0xA08F);
|
||||||
|
static_assert(GFX6_3D_REG_INDEX(generic_scissor) == 0xA090);
|
||||||
static_assert(GFX6_3D_REG_INDEX(viewport_scissors) == 0xA094);
|
static_assert(GFX6_3D_REG_INDEX(viewport_scissors) == 0xA094);
|
||||||
static_assert(GFX6_3D_REG_INDEX(primitive_restart_index) == 0xA103);
|
static_assert(GFX6_3D_REG_INDEX(primitive_restart_index) == 0xA103);
|
||||||
static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B);
|
static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B);
|
||||||
@ -1227,6 +1253,7 @@ static_assert(GFX6_3D_REG_INDEX(color_control) == 0xA202);
|
|||||||
static_assert(GFX6_3D_REG_INDEX(clipper_control) == 0xA204);
|
static_assert(GFX6_3D_REG_INDEX(clipper_control) == 0xA204);
|
||||||
static_assert(GFX6_3D_REG_INDEX(viewport_control) == 0xA206);
|
static_assert(GFX6_3D_REG_INDEX(viewport_control) == 0xA206);
|
||||||
static_assert(GFX6_3D_REG_INDEX(vs_output_control) == 0xA207);
|
static_assert(GFX6_3D_REG_INDEX(vs_output_control) == 0xA207);
|
||||||
|
static_assert(GFX6_3D_REG_INDEX(mode_control) == 0xA292);
|
||||||
static_assert(GFX6_3D_REG_INDEX(index_size) == 0xA29D);
|
static_assert(GFX6_3D_REG_INDEX(index_size) == 0xA29D);
|
||||||
static_assert(GFX6_3D_REG_INDEX(index_buffer_type) == 0xA29F);
|
static_assert(GFX6_3D_REG_INDEX(index_buffer_type) == 0xA29F);
|
||||||
static_assert(GFX6_3D_REG_INDEX(enable_primitive_id) == 0xA2A1);
|
static_assert(GFX6_3D_REG_INDEX(enable_primitive_id) == 0xA2A1);
|
||||||
|
@ -95,7 +95,8 @@ Buffer::Buffer(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
|
|||||||
// Create buffer object.
|
// Create buffer object.
|
||||||
const vk::BufferCreateInfo buffer_ci = {
|
const vk::BufferCreateInfo buffer_ci = {
|
||||||
.size = size_bytes,
|
.size = size_bytes,
|
||||||
.usage = flags,
|
// When maintenance5 is not supported, use all flags since we can't add flags to views.
|
||||||
|
.usage = instance->IsMaintenance5Supported() ? flags : AllFlags,
|
||||||
};
|
};
|
||||||
VmaAllocationInfo alloc_info{};
|
VmaAllocationInfo alloc_info{};
|
||||||
buffer.Create(buffer_ci, usage, &alloc_info);
|
buffer.Create(buffer_ci, usage, &alloc_info);
|
||||||
@ -119,7 +120,7 @@ vk::BufferView Buffer::View(u32 offset, u32 size, bool is_written, AmdGpu::DataF
|
|||||||
: vk::BufferUsageFlagBits2KHR::eUniformTexelBuffer,
|
: vk::BufferUsageFlagBits2KHR::eUniformTexelBuffer,
|
||||||
};
|
};
|
||||||
const vk::BufferViewCreateInfo view_ci = {
|
const vk::BufferViewCreateInfo view_ci = {
|
||||||
.pNext = &usage_flags,
|
.pNext = instance->IsMaintenance5Supported() ? &usage_flags : nullptr,
|
||||||
.buffer = buffer.buffer,
|
.buffer = buffer.buffer,
|
||||||
.format = Vulkan::LiverpoolToVK::SurfaceFormat(dfmt, nfmt),
|
.format = Vulkan::LiverpoolToVK::SurfaceFormat(dfmt, nfmt),
|
||||||
.offset = offset,
|
.offset = offset,
|
||||||
|
@ -88,11 +88,17 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
|
|||||||
"Rectangle List primitive type is only supported for embedded VS");
|
"Rectangle List primitive type is only supported for embedded VS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto prim_restart = key.enable_primitive_restart != 0;
|
||||||
|
if (prim_restart && IsPrimitiveListTopology() && !instance.IsListRestartSupported()) {
|
||||||
|
LOG_WARNING(Render_Vulkan,
|
||||||
|
"Primitive restart is enabled for list topology but not supported by driver.");
|
||||||
|
prim_restart = false;
|
||||||
|
}
|
||||||
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
||||||
.topology = LiverpoolToVK::PrimitiveType(key.prim_type),
|
.topology = LiverpoolToVK::PrimitiveType(key.prim_type),
|
||||||
.primitiveRestartEnable = key.enable_primitive_restart != 0,
|
.primitiveRestartEnable = prim_restart,
|
||||||
};
|
};
|
||||||
ASSERT_MSG(!key.enable_primitive_restart || key.primitive_restart_index == 0xFFFF ||
|
ASSERT_MSG(!prim_restart || key.primitive_restart_index == 0xFFFF ||
|
||||||
key.primitive_restart_index == 0xFFFFFFFF,
|
key.primitive_restart_index == 0xFFFFFFFF,
|
||||||
"Primitive restart index other than -1 is not supported yet");
|
"Primitive restart index other than -1 is not supported yet");
|
||||||
|
|
||||||
@ -387,7 +393,7 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs,
|
|||||||
for (const auto& buffer : stage->buffers) {
|
for (const auto& buffer : stage->buffers) {
|
||||||
const auto vsharp = buffer.GetSharp(*stage);
|
const auto vsharp = buffer.GetSharp(*stage);
|
||||||
const bool is_storage = buffer.IsStorage(vsharp);
|
const bool is_storage = buffer.IsStorage(vsharp);
|
||||||
if (vsharp) {
|
if (vsharp && vsharp.GetSize() > 0) {
|
||||||
const VAddr address = vsharp.base_address;
|
const VAddr address = vsharp.base_address;
|
||||||
if (texture_cache.IsMeta(address)) {
|
if (texture_cache.IsMeta(address)) {
|
||||||
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (buffer)");
|
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (buffer)");
|
||||||
|
@ -26,6 +26,7 @@ using Liverpool = AmdGpu::Liverpool;
|
|||||||
struct GraphicsPipelineKey {
|
struct GraphicsPipelineKey {
|
||||||
std::array<size_t, MaxShaderStages> stage_hashes;
|
std::array<size_t, MaxShaderStages> stage_hashes;
|
||||||
std::array<vk::Format, Liverpool::NumColorBuffers> color_formats;
|
std::array<vk::Format, Liverpool::NumColorBuffers> color_formats;
|
||||||
|
std::array<AmdGpu::NumberFormat, Liverpool::NumColorBuffers> color_num_formats;
|
||||||
std::array<Liverpool::ColorBuffer::SwapMode, Liverpool::NumColorBuffers> mrt_swizzles;
|
std::array<Liverpool::ColorBuffer::SwapMode, Liverpool::NumColorBuffers> mrt_swizzles;
|
||||||
vk::Format depth_format;
|
vk::Format depth_format;
|
||||||
vk::Format stencil_format;
|
vk::Format stencil_format;
|
||||||
@ -84,6 +85,16 @@ public:
|
|||||||
return key.depth_stencil.depth_enable.Value();
|
return key.depth_stencil.depth_enable.Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsPrimitiveListTopology() const {
|
||||||
|
return key.prim_type == Liverpool::PrimitiveType::PointList ||
|
||||||
|
key.prim_type == Liverpool::PrimitiveType::LineList ||
|
||||||
|
key.prim_type == Liverpool::PrimitiveType::TriangleList ||
|
||||||
|
key.prim_type == Liverpool::PrimitiveType::AdjLineList ||
|
||||||
|
key.prim_type == Liverpool::PrimitiveType::AdjTriangleList ||
|
||||||
|
key.prim_type == Liverpool::PrimitiveType::RectList ||
|
||||||
|
key.prim_type == Liverpool::PrimitiveType::QuadList;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BuildDescSetLayout();
|
void BuildDescSetLayout();
|
||||||
|
|
||||||
|
@ -260,9 +260,8 @@ bool Instance::CreateDevice() {
|
|||||||
color_write_en &= add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
color_write_en &= add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
||||||
const bool calibrated_timestamps = add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);
|
const bool calibrated_timestamps = add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);
|
||||||
const bool robustness = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
|
const bool robustness = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
|
||||||
const bool topology_restart =
|
list_restart = add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME);
|
||||||
add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME);
|
maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
|
||||||
const bool maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
|
|
||||||
|
|
||||||
// These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2
|
// These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2
|
||||||
// with extensions.
|
// with extensions.
|
||||||
@ -415,7 +414,7 @@ bool Instance::CreateDevice() {
|
|||||||
if (!workgroup_memory_explicit_layout) {
|
if (!workgroup_memory_explicit_layout) {
|
||||||
device_chain.unlink<vk::PhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR>();
|
device_chain.unlink<vk::PhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR>();
|
||||||
}
|
}
|
||||||
if (!topology_restart) {
|
if (!list_restart) {
|
||||||
device_chain.unlink<vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT>();
|
device_chain.unlink<vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT>();
|
||||||
}
|
}
|
||||||
if (robustness) {
|
if (robustness) {
|
||||||
|
@ -138,6 +138,15 @@ public:
|
|||||||
return null_descriptor;
|
return null_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when VK_KHR_maintenance5 is supported.
|
||||||
|
bool IsMaintenance5Supported() const {
|
||||||
|
return maintenance5;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsListRestartSupported() const {
|
||||||
|
return list_restart;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the vendor ID of the physical device
|
/// Returns the vendor ID of the physical device
|
||||||
u32 GetVendorID() const {
|
u32 GetVendorID() const {
|
||||||
return properties.vendorID;
|
return properties.vendorID;
|
||||||
@ -280,6 +289,8 @@ private:
|
|||||||
bool color_write_en{};
|
bool color_write_en{};
|
||||||
bool vertex_input_dynamic_state{};
|
bool vertex_input_dynamic_state{};
|
||||||
bool null_descriptor{};
|
bool null_descriptor{};
|
||||||
|
bool maintenance5{};
|
||||||
|
bool list_restart{};
|
||||||
u64 min_imported_host_pointer_alignment{};
|
u64 min_imported_host_pointer_alignment{};
|
||||||
u32 subgroup_size{};
|
u32 subgroup_size{};
|
||||||
bool tooling_info{};
|
bool tooling_info{};
|
||||||
|
@ -95,10 +95,6 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||||||
case Shader::Stage::Fragment: {
|
case Shader::Stage::Fragment: {
|
||||||
info.num_user_data = regs.ps_program.settings.num_user_regs;
|
info.num_user_data = regs.ps_program.settings.num_user_regs;
|
||||||
info.num_allocated_vgprs = regs.ps_program.settings.num_vgprs * 4;
|
info.num_allocated_vgprs = regs.ps_program.settings.num_vgprs * 4;
|
||||||
std::ranges::transform(graphics_key.mrt_swizzles, info.fs_info.mrt_swizzles.begin(),
|
|
||||||
[](Liverpool::ColorBuffer::SwapMode mode) {
|
|
||||||
return static_cast<Shader::MrtSwizzle>(mode);
|
|
||||||
});
|
|
||||||
const auto& ps_inputs = regs.ps_inputs;
|
const auto& ps_inputs = regs.ps_inputs;
|
||||||
for (u32 i = 0; i < regs.num_interp; i++) {
|
for (u32 i = 0; i < regs.num_interp; i++) {
|
||||||
info.fs_info.inputs.push_back({
|
info.fs_info.inputs.push_back({
|
||||||
@ -108,6 +104,12 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) {
|
|||||||
.default_value = u8(ps_inputs[i].default_value),
|
.default_value = u8(ps_inputs[i].default_value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
for (u32 i = 0; i < Shader::MaxColorBuffers; i++) {
|
||||||
|
info.fs_info.color_buffers[i] = {
|
||||||
|
.num_format = graphics_key.color_num_formats[i],
|
||||||
|
.mrt_swizzle = static_cast<Shader::MrtSwizzle>(graphics_key.mrt_swizzles[i]),
|
||||||
|
};
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Shader::Stage::Compute: {
|
case Shader::Stage::Compute: {
|
||||||
@ -244,6 +246,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
// attachments. This might be not a case as HW color buffers can be bound in an arbitrary
|
// attachments. This might be not a case as HW color buffers can be bound in an arbitrary
|
||||||
// order. We need to do some arrays compaction at this stage
|
// order. We need to do some arrays compaction at this stage
|
||||||
key.color_formats.fill(vk::Format::eUndefined);
|
key.color_formats.fill(vk::Format::eUndefined);
|
||||||
|
key.color_num_formats.fill(AmdGpu::NumberFormat::Unorm);
|
||||||
key.blend_controls.fill({});
|
key.blend_controls.fill({});
|
||||||
key.write_masks.fill({});
|
key.write_masks.fill({});
|
||||||
key.mrt_swizzles.fill(Liverpool::ColorBuffer::SwapMode::Standard);
|
key.mrt_swizzles.fill(Liverpool::ColorBuffer::SwapMode::Standard);
|
||||||
@ -261,6 +264,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
const bool is_vo_surface = renderer->IsVideoOutSurface(col_buf);
|
const bool is_vo_surface = renderer->IsVideoOutSurface(col_buf);
|
||||||
key.color_formats[remapped_cb] = LiverpoolToVK::AdjustColorBufferFormat(
|
key.color_formats[remapped_cb] = LiverpoolToVK::AdjustColorBufferFormat(
|
||||||
base_format, col_buf.info.comp_swap.Value(), false /*is_vo_surface*/);
|
base_format, col_buf.info.comp_swap.Value(), false /*is_vo_surface*/);
|
||||||
|
key.color_num_formats[remapped_cb] = col_buf.NumFormat();
|
||||||
if (base_format == key.color_formats[remapped_cb]) {
|
if (base_format == key.color_formats[remapped_cb]) {
|
||||||
key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value();
|
key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value();
|
||||||
}
|
}
|
||||||
|
@ -368,11 +368,55 @@ void Rasterizer::UpdateViewportScissorState() {
|
|||||||
.maxDepth = vp.zscale + vp.zoffset,
|
.maxDepth = vp.zscale + vp.zoffset,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const auto& sc = regs.screen_scissor;
|
|
||||||
|
const bool enable_offset = !regs.window_scissor.window_offset_disable.Value();
|
||||||
|
Liverpool::Scissor scsr{};
|
||||||
|
const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
|
||||||
|
return std::max({scr, s16(win + win_offset), s16(gen + win_offset)});
|
||||||
|
};
|
||||||
|
|
||||||
|
scsr.top_left_x = combined_scissor_value_tl(
|
||||||
|
regs.screen_scissor.top_left_x, s16(regs.window_scissor.top_left_x.Value()),
|
||||||
|
s16(regs.generic_scissor.top_left_x.Value()),
|
||||||
|
enable_offset ? regs.window_offset.window_x_offset : 0);
|
||||||
|
|
||||||
|
scsr.top_left_y = combined_scissor_value_tl(
|
||||||
|
regs.screen_scissor.top_left_y, s16(regs.window_scissor.top_left_y.Value()),
|
||||||
|
s16(regs.generic_scissor.top_left_y.Value()),
|
||||||
|
enable_offset ? regs.window_offset.window_y_offset : 0);
|
||||||
|
|
||||||
|
const auto combined_scissor_value_br = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
|
||||||
|
return std::min({scr, s16(win + win_offset), s16(gen + win_offset)});
|
||||||
|
};
|
||||||
|
|
||||||
|
scsr.bottom_right_x = combined_scissor_value_br(
|
||||||
|
regs.screen_scissor.bottom_right_x, regs.window_scissor.bottom_right_x,
|
||||||
|
regs.generic_scissor.bottom_right_x,
|
||||||
|
enable_offset ? regs.window_offset.window_x_offset : 0);
|
||||||
|
|
||||||
|
scsr.bottom_right_y = combined_scissor_value_br(
|
||||||
|
regs.screen_scissor.bottom_right_y, regs.window_scissor.bottom_right_y,
|
||||||
|
regs.generic_scissor.bottom_right_y,
|
||||||
|
enable_offset ? regs.window_offset.window_y_offset : 0);
|
||||||
|
|
||||||
|
for (u32 idx = 0; idx < Liverpool::NumViewports; idx++) {
|
||||||
|
auto vp_scsr = scsr;
|
||||||
|
if (regs.mode_control.vport_scissor_enable) {
|
||||||
|
vp_scsr.top_left_x =
|
||||||
|
std::max(vp_scsr.top_left_x, s16(regs.viewport_scissors[idx].top_left_x.Value()));
|
||||||
|
vp_scsr.top_left_y =
|
||||||
|
std::max(vp_scsr.top_left_y, s16(regs.viewport_scissors[idx].top_left_y.Value()));
|
||||||
|
vp_scsr.bottom_right_x =
|
||||||
|
std::min(vp_scsr.bottom_right_x, regs.viewport_scissors[idx].bottom_right_x);
|
||||||
|
vp_scsr.bottom_right_y =
|
||||||
|
std::min(vp_scsr.bottom_right_y, regs.viewport_scissors[idx].bottom_right_y);
|
||||||
|
}
|
||||||
scissors.push_back({
|
scissors.push_back({
|
||||||
.offset = {sc.top_left_x, sc.top_left_y},
|
.offset = {vp_scsr.top_left_x, vp_scsr.top_left_y},
|
||||||
.extent = {sc.GetWidth(), sc.GetHeight()},
|
.extent = {vp_scsr.GetWidth(), vp_scsr.GetHeight()},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
cmdbuf.setViewport(0, viewports);
|
cmdbuf.setViewport(0, viewports);
|
||||||
cmdbuf.setScissor(0, scissors);
|
cmdbuf.setScissor(0, scissors);
|
||||||
|
Loading…
Reference in New Issue
Block a user