Merge branch 'shadps4-emu:main' into unity-flip-fix

This commit is contained in:
Stephen Miller 2025-02-13 07:12:16 -06:00 committed by GitHub
commit 9218d2e381
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
61 changed files with 48114 additions and 39975 deletions

View File

@ -111,10 +111,10 @@ jobs:
- name: Setup Qt - name: Setup Qt
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
version: 6.7.3 version: 6.8.2
host: windows host: windows
target: desktop target: desktop
arch: win64_msvc2019_64 arch: win64_msvc2022_64
archives: qtbase qttools archives: qtbase qttools
modules: qtmultimedia modules: qtmultimedia
@ -228,7 +228,7 @@ jobs:
- name: Setup Qt - name: Setup Qt
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
version: 6.7.3 version: 6.8.2
host: mac host: mac
target: desktop target: desktop
arch: clang_64 arch: clang_64

View File

@ -3,6 +3,7 @@ version = 1
[[annotations]] [[annotations]]
path = [ path = [
"REUSE.toml", "REUSE.toml",
"crowdin.yml",
"CMakeSettings.json", "CMakeSettings.json",
".github/FUNDING.yml", ".github/FUNDING.yml",
".github/shadps4.png", ".github/shadps4.png",

3
crowdin.yml Normal file
View File

@ -0,0 +1,3 @@
files:
- source: /src/qt_gui/translations/en_US.ts
translation: /%original_path%/%locale_with_underscore%.ts

View File

@ -25,7 +25,7 @@ Once you are within the installer:
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`. 1. Under the current, non beta version of Qt (at the time of writing 6.8.2), select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`.
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead. If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space. Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this,
Once you are finished, you will have to configure Qt within Visual Studio: Once you are finished, you will have to configure Qt within Visual Studio:
1. Tools -> Options -> Qt -> Versions 1. Tools -> Options -> Qt -> Versions
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64` 2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.8.2\msvc2022_64`
3. Enable the default checkmark on the new version you just created. 3. Enable the default checkmark on the new version you just created.
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) ### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
@ -55,7 +55,7 @@ Go through the Git for Windows installation as normal
3. If you want to build shadPS4 with the Qt Gui: 3. If you want to build shadPS4 with the Qt Gui:
1. Click x64-Clang-Release and select "Manage Configurations" 1. Click x64-Clang-Release and select "Manage Configurations"
2. Look for "CMake command arguments" and add to the text field 2. Look for "CMake command arguments" and add to the text field
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64` `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.8.2\msvc2022_64`
(Change Qt path if you've installed it to non-default path) (Change Qt path if you've installed it to non-default path)
3. Press CTRL+S to save and wait a moment for CMake generation 3. Press CTRL+S to save and wait a moment for CMake generation
4. Change the project to build to shadps4.exe 4. Change the project to build to shadps4.exe
@ -64,7 +64,7 @@ Go through the Git for Windows installation as normal
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"` `C:\Qt\6.8.2\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"`
(Change Qt path if you've installed it to non-default path) (Change Qt path if you've installed it to non-default path)
## Option 2: MSYS2/MinGW ## Option 2: MSYS2/MinGW

View File

@ -2307,8 +2307,36 @@ s32 PS4_SYSV_ABI sceGnmUpdateGsShader(u32* cmdbuf, u32 size, const u32* gs_regs)
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceGnmUpdateHsShader() { int PS4_SYSV_ABI sceGnmUpdateHsShader(u32* cmdbuf, u32 size, const u32* hs_regs, u32 ls_hs_config) {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); LOG_TRACE(Lib_GnmDriver, "called");
if (!cmdbuf || size <= 0x1c) {
return -1;
}
if (!hs_regs) {
LOG_ERROR(Lib_GnmDriver, "Null pointer passed as argument");
return -1;
}
if (hs_regs[1] != 0) {
LOG_ERROR(Lib_GnmDriver, "Invalid shader address");
return -1;
}
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x108u, hs_regs[0],
0u); // SPI_SHADER_PGM_LO_HS/SPI_SHADER_PGM_HI_HS
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x10au, hs_regs[2],
hs_regs[3]); // SPI_SHADER_PGM_RSRC1_HS/SPI_SHADER_PGM_RSRC1_LS
cmdbuf = WritePacket<PM4ItOpcode::Nop>(
cmdbuf, PM4ShaderType::ShaderGraphics, 0xc01e0286u, hs_regs[5],
hs_regs[6]); // VGT_HOS_MAX_TESS_LEVEL/VGT_HOS_MIN_TESS_LEVEL update
cmdbuf = WritePacket<PM4ItOpcode::Nop>(cmdbuf, PM4ShaderType::ShaderGraphics, 0xc01e02dbu,
hs_regs[4]); // VGT_TF_PARAM update
cmdbuf = WritePacket<PM4ItOpcode::Nop>(cmdbuf, PM4ShaderType::ShaderGraphics, 0xc01e02d6u,
ls_hs_config); // VGT_LS_HS_CONFIG update
WriteTrailingNop<11>(cmdbuf);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -227,7 +227,7 @@ int PS4_SYSV_ABI sceGnmUnregisterAllResourcesForOwner();
int PS4_SYSV_ABI sceGnmUnregisterOwnerAndResources(); int PS4_SYSV_ABI sceGnmUnregisterOwnerAndResources();
int PS4_SYSV_ABI sceGnmUnregisterResource(); int PS4_SYSV_ABI sceGnmUnregisterResource();
s32 PS4_SYSV_ABI sceGnmUpdateGsShader(u32* cmdbuf, u32 size, const u32* gs_regs); s32 PS4_SYSV_ABI sceGnmUpdateGsShader(u32* cmdbuf, u32 size, const u32* gs_regs);
int PS4_SYSV_ABI sceGnmUpdateHsShader(); int PS4_SYSV_ABI sceGnmUpdateHsShader(u32* cmdbuf, u32 size, const u32* ps_regs, u32 ls_hs_config);
s32 PS4_SYSV_ABI sceGnmUpdatePsShader(u32* cmdbuf, u32 size, const u32* ps_regs); s32 PS4_SYSV_ABI sceGnmUpdatePsShader(u32* cmdbuf, u32 size, const u32* ps_regs);
s32 PS4_SYSV_ABI sceGnmUpdatePsShader350(u32* cmdbuf, u32 size, const u32* ps_regs); s32 PS4_SYSV_ABI sceGnmUpdatePsShader350(u32* cmdbuf, u32 size, const u32* ps_regs);
s32 PS4_SYSV_ABI sceGnmUpdateVsShader(u32* cmdbuf, u32 size, const u32* vs_regs, s32 PS4_SYSV_ABI sceGnmUpdateVsShader(u32* cmdbuf, u32 size, const u32* vs_regs,

View File

@ -568,7 +568,7 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer
} else { } else {
QMessageBox::warning(this, tr("Error"), QMessageBox::warning(this, tr("Error"),
QString(tr("Failed to download file:") + QString(tr("Failed to download file:") +
"%1\n\n" + tr("Error:") + "%2") "%1\n\n" + tr("Error") + ":%2")
.arg(fileUrl) .arg(fileUrl)
.arg(fileReply->errorString())); .arg(fileReply->errorString()));
} }
@ -644,7 +644,7 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer
} else { } else {
QMessageBox::warning(this, tr("Error"), QMessageBox::warning(this, tr("Error"),
QString(tr("Failed to download file:") + QString(tr("Failed to download file:") +
"%1\n\n" + tr("Error:") + "%2") "%1\n\n" + tr("Error") + ":%2")
.arg(fileUrl) .arg(fileUrl)
.arg(fileReply->errorString())); .arg(fileReply->errorString()));
} }
@ -843,7 +843,7 @@ void CheatsPatches::compatibleVersionNotice(const QString repository) {
foreach (const QString& xmlFile, xmlFiles) { foreach (const QString& xmlFile, xmlFiles) {
QFile file(dir.filePath(xmlFile)); QFile file(dir.filePath(xmlFile));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("ERROR"), QMessageBox::warning(this, tr("Error"),
QString(tr("Failed to open file:") + "\n%1").arg(xmlFile)); QString(tr("Failed to open file:") + "\n%1").arg(xmlFile));
continue; continue;
} }
@ -871,7 +871,7 @@ void CheatsPatches::compatibleVersionNotice(const QString repository) {
} }
if (xmlReader.hasError()) { if (xmlReader.hasError()) {
QMessageBox::warning(this, tr("ERROR"), QMessageBox::warning(this, tr("Error"),
QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString())); QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString()));
} }
@ -926,7 +926,7 @@ void CheatsPatches::createFilesJson(const QString& repository) {
foreach (const QString& xmlFile, xmlFiles) { foreach (const QString& xmlFile, xmlFiles) {
QFile file(dir.filePath(xmlFile)); QFile file(dir.filePath(xmlFile));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("ERROR"), QMessageBox::warning(this, tr("Error"),
QString(tr("Failed to open file:") + "\n%1").arg(xmlFile)); QString(tr("Failed to open file:") + "\n%1").arg(xmlFile));
continue; continue;
} }
@ -944,7 +944,7 @@ void CheatsPatches::createFilesJson(const QString& repository) {
} }
if (xmlReader.hasError()) { if (xmlReader.hasError()) {
QMessageBox::warning(this, tr("ERROR"), QMessageBox::warning(this, tr("Error"),
QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString())); QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString()));
} }
filesObject[xmlFile] = titleIdsArray; filesObject[xmlFile] = titleIdsArray;
@ -952,7 +952,7 @@ void CheatsPatches::createFilesJson(const QString& repository) {
QFile jsonFile(dir.absolutePath() + "/files.json"); QFile jsonFile(dir.absolutePath() + "/files.json");
if (!jsonFile.open(QIODevice::WriteOnly)) { if (!jsonFile.open(QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("ERROR"), tr("Failed to open files.json for writing")); QMessageBox::warning(this, tr("Error"), tr("Failed to open files.json for writing"));
return; return;
} }
@ -1155,7 +1155,7 @@ void CheatsPatches::addPatchesToLayout(const QString& filePath) {
QString fullPath = dir.filePath(folderPath); QString fullPath = dir.filePath(folderPath);
if (!dir.exists(fullPath)) { if (!dir.exists(fullPath)) {
QMessageBox::warning(this, tr("ERROR"), QMessageBox::warning(this, tr("Error"),
QString(tr("Directory does not exist:") + "\n%1").arg(fullPath)); QString(tr("Directory does not exist:") + "\n%1").arg(fullPath));
return; return;
} }
@ -1165,7 +1165,7 @@ void CheatsPatches::addPatchesToLayout(const QString& filePath) {
QFile jsonFile(filesJsonPath); QFile jsonFile(filesJsonPath);
if (!jsonFile.open(QIODevice::ReadOnly)) { if (!jsonFile.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("ERROR"), tr("Failed to open files.json for reading.")); QMessageBox::warning(this, tr("Error"), tr("Failed to open files.json for reading."));
return; return;
} }
@ -1189,7 +1189,7 @@ void CheatsPatches::addPatchesToLayout(const QString& filePath) {
if (!xmlFile.open(QIODevice::ReadOnly)) { if (!xmlFile.open(QIODevice::ReadOnly)) {
QMessageBox::warning( QMessageBox::warning(
this, tr("ERROR"), this, tr("Error"),
QString(tr("Failed to open file:") + "\n%1").arg(xmlFile.fileName())); QString(tr("Failed to open file:") + "\n%1").arg(xmlFile.fileName()));
continue; continue;
} }

View File

@ -18,17 +18,8 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* pare
treeWidget = new QTreeWidget(this); treeWidget = new QTreeWidget(this);
treeWidget->setColumnCount(9); treeWidget->setColumnCount(9);
QStringList headers; QStringList headers;
headers << "Name" headers << tr("Name") << tr("Serial") << tr("Installed") << tr("Size") << tr("Category")
<< "Serial" << tr("Type") << tr("App Ver") << tr("FW") << tr("Region") << tr("Flags") << tr("Path");
<< "Installed"
<< "Size"
<< "Category"
<< "Type"
<< "App Ver"
<< "FW"
<< "Region"
<< "Flags"
<< "Path";
treeWidget->setHeaderLabels(headers); treeWidget->setHeaderLabels(headers);
treeWidget->header()->setDefaultAlignment(Qt::AlignCenter); treeWidget->header()->setDefaultAlignment(Qt::AlignCenter);
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
@ -36,7 +27,7 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* pare
this->setCentralWidget(treeWidget); this->setCentralWidget(treeWidget);
QMenuBar* menuBar = new QMenuBar(this); QMenuBar* menuBar = new QMenuBar(this);
menuBar->setContextMenuPolicy(Qt::PreventContextMenu); menuBar->setContextMenuPolicy(Qt::PreventContextMenu);
QMenu* fileMenu = menuBar->addMenu(tr("&File")); QMenu* fileMenu = menuBar->addMenu(tr("File"));
QAction* openFolderAct = new QAction(tr("Open Folder"), this); QAction* openFolderAct = new QAction(tr("Open Folder"), this);
fileMenu->addAction(openFolderAct); fileMenu->addAction(openFolderAct);
this->setMenuBar(menuBar); this->setMenuBar(menuBar);
@ -114,15 +105,15 @@ void PKGViewer::ProcessPKGInfo() {
return; return;
} }
psf.Open(package.sfo); psf.Open(package.sfo);
QString title_name = QString title_name = QString::fromStdString(
QString::fromStdString(std::string{psf.GetString("TITLE").value_or("Unknown")}); std::string{psf.GetString("TITLE").value_or(std::string{tr("Unknown").toStdString()})});
QString title_id = QString title_id = QString::fromStdString(std::string{
QString::fromStdString(std::string{psf.GetString("TITLE_ID").value_or("Unknown")}); psf.GetString("TITLE_ID").value_or(std::string{tr("Unknown").toStdString()})});
QString app_type = GameListUtils::GetAppType(psf.GetInteger("APP_TYPE").value_or(0)); QString app_type = GameListUtils::GetAppType(psf.GetInteger("APP_TYPE").value_or(0));
QString app_version = QString app_version = QString::fromStdString(std::string{
QString::fromStdString(std::string{psf.GetString("APP_VER").value_or("Unknown")}); psf.GetString("APP_VER").value_or(std::string{tr("Unknown").toStdString()})});
QString title_category = QString title_category = QString::fromStdString(std::string{
QString::fromStdString(std::string{psf.GetString("CATEGORY").value_or("Unknown")}); psf.GetString("CATEGORY").value_or(std::string{tr("Unknown").toStdString()})});
QString pkg_size = GameListUtils::FormatSize(package.GetPkgHeader().pkg_size); QString pkg_size = GameListUtils::FormatSize(package.GetPkgHeader().pkg_size);
pkg_content_flag = package.GetPkgHeader().pkg_content_flags; pkg_content_flag = package.GetPkgHeader().pkg_content_flags;
QString flagss = ""; QString flagss = "";
@ -134,7 +125,7 @@ void PKGViewer::ProcessPKGInfo() {
} }
} }
QString fw_ = "Unknown"; QString fw_ = tr("Unknown");
if (const auto fw_int_opt = psf.GetInteger("SYSTEM_VER"); fw_int_opt.has_value()) { if (const auto fw_int_opt = psf.GetInteger("SYSTEM_VER"); fw_int_opt.has_value()) {
const u32 fw_int = *fw_int_opt; const u32 fw_int = *fw_int_opt;
if (fw_int == 0) { if (fw_int == 0) {
@ -221,6 +212,6 @@ void PKGViewer::ProcessPKGInfo() {
// Update status bar. // Update status bar.
statusBar->clearMessage(); statusBar->clearMessage();
int numPkgs = m_pkg_list.size(); int numPkgs = m_pkg_list.size();
QString statusMessage = QString::number(numPkgs) + " Package."; QString statusMessage = QString::number(numPkgs) + " " + tr("Package");
statusBar->showMessage(statusMessage); statusBar->showMessage(statusMessage);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later -->
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="sv_SE"> <TS version="2.1" language="sv_SE">
<!-- SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later -->
<context> <context>
<name>AboutDialog</name> <name>AboutDialog</name>
<message> <message>
@ -244,14 +244,6 @@
<source>Can&apos;t apply cheats before the game is started</source> <source>Can&apos;t apply cheats before the game is started</source>
<translation>Kan inte tillämpa fusk innan spelet är startat</translation> <translation>Kan inte tillämpa fusk innan spelet är startat</translation>
</message> </message>
<message>
<source>Error:</source>
<translation>Fel:</translation>
</message>
<message>
<source>ERROR</source>
<translation>FEL</translation>
</message>
<message> <message>
<source>Close</source> <source>Close</source>
<translation>Stäng</translation> <translation>Stäng</translation>
@ -386,10 +378,6 @@
<source>Unable to update compatibility data! Try again later.</source> <source>Unable to update compatibility data! Try again later.</source>
<translation>Kunde inte uppdatera kompatibilitetsdata! Försök igen senare.</translation> <translation>Kunde inte uppdatera kompatibilitetsdata! Försök igen senare.</translation>
</message> </message>
<message>
<source>Unable to open compatibility.json for writing.</source>
<translation type="vanished">Kunde inte öppna compatibility.json för skrivning.</translation>
</message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<translation>Okänt</translation> <translation>Okänt</translation>
@ -673,10 +661,6 @@
<source>Game can be completed with playable performance and no major glitches</source> <source>Game can be completed with playable performance and no major glitches</source>
<translation>Spelet kan spelas klart med spelbar prestanda och utan större problem</translation> <translation>Spelet kan spelas klart med spelbar prestanda och utan större problem</translation>
</message> </message>
<message>
<source>Click to go to issue</source>
<translation type="vanished">Klicka för att till problem</translation>
</message>
<message> <message>
<source>Last updated</source> <source>Last updated</source>
<translation>Senast uppdaterad</translation> <translation>Senast uppdaterad</translation>
@ -1196,14 +1180,66 @@
<source>Open Folder</source> <source>Open Folder</source>
<translation>Öppna mapp</translation> <translation>Öppna mapp</translation>
</message> </message>
<message>
<source>&amp;File</source>
<translation>&amp;Arkiv</translation>
</message>
<message> <message>
<source>PKG ERROR</source> <source>PKG ERROR</source>
<translation>PKG-FEL</translation> <translation>PKG-FEL</translation>
</message> </message>
<message>
<source>Name</source>
<translation type="unfinished">Namn</translation>
</message>
<message>
<source>Serial</source>
<translation type="unfinished">Serienummer</translation>
</message>
<message>
<source>Installed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Size</source>
<translation type="unfinished">Storlek</translation>
</message>
<message>
<source>Category</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Type</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>App Ver</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>FW</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Region</source>
<translation type="unfinished">Region</translation>
</message>
<message>
<source>Flags</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Path</source>
<translation type="unfinished">Sökväg</translation>
</message>
<message>
<source>File</source>
<translation type="unfinished">Arkiv</translation>
</message>
<message>
<source>Unknown</source>
<translation type="unfinished">Okänt</translation>
</message>
<message>
<source>Package</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsDialog</name> <name>SettingsDialog</name>
@ -1255,10 +1291,6 @@
<source>Show Splash</source> <source>Show Splash</source>
<translation>Visa startskärm</translation> <translation>Visa startskärm</translation>
</message> </message>
<message>
<source>ps4proCheckBox</source>
<translation type="vanished">Är PS4 Pro:\nGör att emulatorn agerar som en PS4 PRO, vilket kan aktivera speciella funktioner i spel som har stöd för det</translation>
</message>
<message> <message>
<source>Enable Discord Rich Presence</source> <source>Enable Discord Rich Presence</source>
<translation>Aktivera Discord Rich Presence</translation> <translation>Aktivera Discord Rich Presence</translation>
@ -1323,10 +1355,6 @@
<source>Graphics</source> <source>Graphics</source>
<translation>Grafik</translation> <translation>Grafik</translation>
</message> </message>
<message>
<source>Gui</source>
<translation type="vanished">Gränssnitt</translation>
</message>
<message> <message>
<source>User</source> <source>User</source>
<translation>Användare</translation> <translation>Användare</translation>
@ -1743,6 +1771,14 @@
<source>GUIBackgroundImageGroupBox</source> <source>GUIBackgroundImageGroupBox</source>
<translation>Bakgrundsbild:\nKontrollerar opaciteten för spelets bakgrundsbild</translation> <translation>Bakgrundsbild:\nKontrollerar opaciteten för spelets bakgrundsbild</translation>
</message> </message>
<message>
<source>Enable HDR</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>enableHDRCheckBox</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>TrophyViewer</name> <name>TrophyViewer</name>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -120,10 +120,8 @@ Id EmitUndefU32(EmitContext& ctx);
Id EmitUndefU64(EmitContext& ctx); Id EmitUndefU64(EmitContext& ctx);
Id EmitLoadSharedU32(EmitContext& ctx, Id offset); Id EmitLoadSharedU32(EmitContext& ctx, Id offset);
Id EmitLoadSharedU64(EmitContext& ctx, Id offset); Id EmitLoadSharedU64(EmitContext& ctx, Id offset);
Id EmitLoadSharedU128(EmitContext& ctx, Id offset);
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value); void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value);
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value); void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value);
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value);

View File

@ -38,24 +38,6 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
} }
} }
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
const Id shift_id{ctx.ConstU32(2U)};
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
std::array<Id, 4> values{};
for (u32 i = 0; i < 4; ++i) {
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.ConstU32(i))};
if (ctx.info.has_emulated_shared_memory) {
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32,
ctx.u32_zero_value, index)};
values[i] = ctx.OpLoad(ctx.U32[1], pointer);
} else {
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
values[i] = ctx.OpLoad(ctx.U32[1], pointer);
}
}
return ctx.OpCompositeConstruct(ctx.U32[4], values);
}
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
const Id shift{ctx.ConstU32(2U)}; const Id shift{ctx.ConstU32(2U)};
const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
@ -88,20 +70,4 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
} }
} }
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
const Id shift{ctx.ConstU32(2U)};
const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)};
for (u32 i = 0; i < 4; ++i) {
const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.ConstU32(i))};
if (ctx.info.has_emulated_shared_memory) {
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32,
ctx.u32_zero_value, index)};
ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i));
} else {
const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)};
ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i));
}
}
}
} // namespace Shader::Backend::SPIRV } // namespace Shader::Backend::SPIRV

View File

@ -813,6 +813,8 @@ void EmitContext::DefineSharedMemory() {
if (!info.uses_shared) { if (!info.uses_shared) {
return; return;
} }
ASSERT(info.stage == Stage::Compute);
const u32 max_shared_memory_size = profile.max_shared_memory_size; const u32 max_shared_memory_size = profile.max_shared_memory_size;
u32 shared_memory_size = runtime_info.cs_info.shared_memory_size; u32 shared_memory_size = runtime_info.cs_info.shared_memory_size;
if (shared_memory_size == 0) { if (shared_memory_size == 0) {

View File

@ -233,7 +233,8 @@ struct Info {
} }
void AddBindings(Backend::Bindings& bnd) const { void AddBindings(Backend::Bindings& bnd) const {
const auto total_buffers = buffers.size() + (has_readconst ? 1 : 0); const auto total_buffers =
buffers.size() + (has_readconst ? 1 : 0) + (has_emulated_shared_memory ? 1 : 0);
bnd.buffer += total_buffers; bnd.buffer += total_buffers;
bnd.unified += total_buffers + images.size() + samplers.size(); bnd.unified += total_buffers + images.size() + samplers.size();
bnd.user_data += ud_mask.NumRegs(); bnd.user_data += ud_mask.NumRegs();

View File

@ -308,8 +308,6 @@ Value IREmitter::LoadShared(int bit_size, bool is_signed, const U32& offset) {
return Inst<U32>(Opcode::LoadSharedU32, offset); return Inst<U32>(Opcode::LoadSharedU32, offset);
case 64: case 64:
return Inst(Opcode::LoadSharedU64, offset); return Inst(Opcode::LoadSharedU64, offset);
case 128:
return Inst(Opcode::LoadSharedU128, offset);
default: default:
UNREACHABLE_MSG("Invalid bit size {}", bit_size); UNREACHABLE_MSG("Invalid bit size {}", bit_size);
} }
@ -323,9 +321,6 @@ void IREmitter::WriteShared(int bit_size, const Value& value, const U32& offset)
case 64: case 64:
Inst(Opcode::WriteSharedU64, offset, value); Inst(Opcode::WriteSharedU64, offset, value);
break; break;
case 128:
Inst(Opcode::WriteSharedU128, offset, value);
break;
default: default:
UNREACHABLE_MSG("Invalid bit size {}", bit_size); UNREACHABLE_MSG("Invalid bit size {}", bit_size);
} }

View File

@ -78,7 +78,6 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::BufferAtomicSwap32: case Opcode::BufferAtomicSwap32:
case Opcode::DataAppend: case Opcode::DataAppend:
case Opcode::DataConsume: case Opcode::DataConsume:
case Opcode::WriteSharedU128:
case Opcode::WriteSharedU64: case Opcode::WriteSharedU64:
case Opcode::WriteSharedU32: case Opcode::WriteSharedU32:
case Opcode::SharedAtomicIAdd32: case Opcode::SharedAtomicIAdd32:

View File

@ -32,10 +32,8 @@ OPCODE(EmitPrimitive, Void,
// Shared memory operations // Shared memory operations
OPCODE(LoadSharedU32, U32, U32, ) OPCODE(LoadSharedU32, U32, U32, )
OPCODE(LoadSharedU64, U32x2, U32, ) OPCODE(LoadSharedU64, U32x2, U32, )
OPCODE(LoadSharedU128, U32x4, U32, )
OPCODE(WriteSharedU32, Void, U32, U32, ) OPCODE(WriteSharedU32, Void, U32, U32, )
OPCODE(WriteSharedU64, Void, U32, U32x2, ) OPCODE(WriteSharedU64, Void, U32, U32x2, )
OPCODE(WriteSharedU128, Void, U32, U32x4, )
// Shared atomic operations // Shared atomic operations
OPCODE(SharedAtomicIAdd32, U32, U32, U32, ) OPCODE(SharedAtomicIAdd32, U32, U32, U32, )

View File

@ -225,10 +225,8 @@ private:
switch (use.user->GetOpcode()) { switch (use.user->GetOpcode()) {
case IR::Opcode::LoadSharedU32: case IR::Opcode::LoadSharedU32:
case IR::Opcode::LoadSharedU64: case IR::Opcode::LoadSharedU64:
case IR::Opcode::LoadSharedU128:
case IR::Opcode::WriteSharedU32: case IR::Opcode::WriteSharedU32:
case IR::Opcode::WriteSharedU64: case IR::Opcode::WriteSharedU64: {
case IR::Opcode::WriteSharedU128: {
u32 counter = inst->Flags<u32>(); u32 counter = inst->Flags<u32>();
inst->SetFlags<u32>(counter + inc); inst->SetFlags<u32>(counter + inc);
// Stop here // Stop here
@ -435,12 +433,9 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
} }
case IR::Opcode::WriteSharedU32: case IR::Opcode::WriteSharedU32:
case IR::Opcode::WriteSharedU64: case IR::Opcode::WriteSharedU64: {
case IR::Opcode::WriteSharedU128: {
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const u32 num_dwords = opcode == IR::Opcode::WriteSharedU32 const u32 num_dwords = opcode == IR::Opcode::WriteSharedU32 ? 1 : 2;
? 1
: (opcode == IR::Opcode::WriteSharedU64 ? 2 : 4);
const IR::U32 addr{inst.Arg(0)}; const IR::U32 addr{inst.Arg(0)};
const IR::U32 data{inst.Arg(1).Resolve()}; const IR::U32 data{inst.Arg(1).Resolve()};
@ -480,15 +475,12 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
break; break;
} }
case IR::Opcode::LoadSharedU32: { case IR::Opcode::LoadSharedU32:
case IR::Opcode::LoadSharedU64: case IR::Opcode::LoadSharedU64: {
case IR::Opcode::LoadSharedU128:
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const IR::U32 addr{inst.Arg(0)}; const IR::U32 addr{inst.Arg(0)};
const AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); const AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info);
const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 ? 1 : 2;
? 1
: (opcode == IR::Opcode::LoadSharedU64 ? 2 : 4);
ASSERT_MSG(region == AttributeRegion::InputCP || ASSERT_MSG(region == AttributeRegion::InputCP ||
region == AttributeRegion::OutputCP, region == AttributeRegion::OutputCP,
"Unhandled read of patchconst attribute in hull shader"); "Unhandled read of patchconst attribute in hull shader");
@ -562,14 +554,11 @@ void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const auto opcode = inst.GetOpcode(); const auto opcode = inst.GetOpcode();
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU32: { case IR::Opcode::LoadSharedU32:
case IR::Opcode::LoadSharedU64: case IR::Opcode::LoadSharedU64: {
case IR::Opcode::LoadSharedU128:
const IR::U32 addr{inst.Arg(0)}; const IR::U32 addr{inst.Arg(0)};
AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info);
const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 ? 1 : 2;
? 1
: (opcode == IR::Opcode::LoadSharedU64 ? 2 : 4);
const auto GetInput = [&](IR::U32 addr, u32 off_dw) -> IR::F32 { const auto GetInput = [&](IR::U32 addr, u32 off_dw) -> IR::F32 {
if (region == AttributeRegion::OutputCP) { if (region == AttributeRegion::OutputCP) {
return ReadTessControlPointAttribute( return ReadTessControlPointAttribute(
@ -611,10 +600,8 @@ void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU32: case IR::Opcode::LoadSharedU32:
case IR::Opcode::LoadSharedU64: case IR::Opcode::LoadSharedU64:
case IR::Opcode::LoadSharedU128:
case IR::Opcode::WriteSharedU32: case IR::Opcode::WriteSharedU32:
case IR::Opcode::WriteSharedU64: case IR::Opcode::WriteSharedU64: {
case IR::Opcode::WriteSharedU128: {
IR::Value addr = inst.Arg(0); IR::Value addr = inst.Arg(0);
auto read_const_buffer = IR::BreadthFirstSearch( auto read_const_buffer = IR::BreadthFirstSearch(
addr, [](IR::Inst* maybe_tess_const) -> std::optional<IR::Inst*> { addr, [](IR::Inst* maybe_tess_const) -> std::optional<IR::Inst*> {

View File

@ -20,7 +20,7 @@ void FlattenExtendedUserdataPass(IR::Program& program);
void ResourceTrackingPass(IR::Program& program); void ResourceTrackingPass(IR::Program& program);
void CollectShaderInfoPass(IR::Program& program); void CollectShaderInfoPass(IR::Program& program);
void LowerBufferFormatToRaw(IR::Program& program); void LowerBufferFormatToRaw(IR::Program& program);
void LowerSharedMemToRegisters(IR::Program& program); void LowerSharedMemToRegisters(IR::Program& program, const RuntimeInfo& runtime_info);
void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info,
Stage stage); Stage stage);
void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info);

View File

@ -1,38 +1,81 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/small_vector.hpp> #include <unordered_map>
#include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/program.h"
namespace Shader::Optimization { namespace Shader::Optimization {
void LowerSharedMemToRegisters(IR::Program& program) { static bool IsSharedMemoryInst(const IR::Inst& inst) {
boost::container::small_vector<IR::Inst*, 8> ds_writes; const auto opcode = inst.GetOpcode();
Info& info{program.info}; return opcode == IR::Opcode::LoadSharedU32 || opcode == IR::Opcode::LoadSharedU64 ||
opcode == IR::Opcode::WriteSharedU32 || opcode == IR::Opcode::WriteSharedU64;
}
static u32 GetSharedMemImmOffset(const IR::Inst& inst) {
const auto* address = inst.Arg(0).InstRecursive();
ASSERT(address->GetOpcode() == IR::Opcode::IAdd32);
const auto ir_offset = address->Arg(1);
ASSERT_MSG(ir_offset.IsImmediate());
const auto offset = ir_offset.U32();
// Typical usage is the compiler spilling registers into shared memory, with 256 bytes between
// each register to account for 4 bytes per register times 64 threads per group. Ensure that
// this assumption holds, as if it does not this approach may need to be revised.
ASSERT_MSG(offset % 256 == 0, "Unexpected shared memory offset alignment: {}", offset);
return offset;
}
static void ConvertSharedMemToVgpr(IR::IREmitter& ir, IR::Inst& inst, const IR::VectorReg vgpr) {
switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU32:
inst.ReplaceUsesWithAndRemove(ir.GetVectorReg(vgpr));
break;
case IR::Opcode::LoadSharedU64:
inst.ReplaceUsesWithAndRemove(
ir.CompositeConstruct(ir.GetVectorReg(vgpr), ir.GetVectorReg(vgpr + 1)));
break;
case IR::Opcode::WriteSharedU32:
ir.SetVectorReg(vgpr, IR::U32{inst.Arg(1)});
inst.Invalidate();
break;
case IR::Opcode::WriteSharedU64: {
const auto value = inst.Arg(1);
ir.SetVectorReg(vgpr, IR::U32{ir.CompositeExtract(value, 0)});
ir.SetVectorReg(vgpr, IR::U32{ir.CompositeExtract(value, 1)});
inst.Invalidate();
break;
}
default:
UNREACHABLE_MSG("Unknown shared memory opcode: {}", inst.GetOpcode());
}
}
void LowerSharedMemToRegisters(IR::Program& program, const RuntimeInfo& runtime_info) {
u32 next_vgpr_num = runtime_info.num_allocated_vgprs;
std::unordered_map<u32, IR::VectorReg> vgpr_map;
const auto get_vgpr = [&next_vgpr_num, &vgpr_map](const u32 offset) {
const auto [it, is_new] = vgpr_map.try_emplace(offset);
if (is_new) {
ASSERT_MSG(next_vgpr_num < 256, "Out of VGPRs");
const auto new_vgpr = static_cast<IR::VectorReg>(next_vgpr_num++);
it->second = new_vgpr;
}
return it->second;
};
for (IR::Block* const block : program.blocks) { for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
const auto opcode = inst.GetOpcode(); if (!IsSharedMemoryInst(inst)) {
if (opcode == IR::Opcode::WriteSharedU32 || opcode == IR::Opcode::WriteSharedU64) {
ds_writes.emplace_back(&inst);
continue; continue;
} }
if (opcode == IR::Opcode::LoadSharedU32 || opcode == IR::Opcode::LoadSharedU64) { const auto offset = GetSharedMemImmOffset(inst);
// Search for write instruction with same offset const auto vgpr = get_vgpr(offset);
const IR::Inst* prod = inst.Arg(0).InstRecursive(); IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const auto it = std::ranges::find_if(ds_writes, [&](const IR::Inst* write) { ConvertSharedMemToVgpr(ir, inst, vgpr);
const IR::Inst* write_prod = write->Arg(0).InstRecursive();
return write_prod->Arg(1).U32() == prod->Arg(1).U32();
});
ASSERT(it != ds_writes.end());
// Replace data read with value written.
inst.ReplaceUsesWithAndRemove((*it)->Arg(1));
}
} }
} }
// We should have eliminated everything. Invalidate data write instructions.
for (const auto inst : ds_writes) {
inst->Invalidate();
}
} }
} // namespace Shader::Optimization } // namespace Shader::Optimization

View File

@ -65,6 +65,10 @@ IR::Program TranslateProgram(std::span<const u32> code, Pools& pools, Info& info
// Run optimization passes // Run optimization passes
const auto stage = program.info.stage; const auto stage = program.info.stage;
if (stage == Stage::Fragment) {
// Before SSA pass, as it will rewrite to VGPR load/store.
Shader::Optimization::LowerSharedMemToRegisters(program, runtime_info);
}
Shader::Optimization::SsaRewritePass(program.post_order_blocks); Shader::Optimization::SsaRewritePass(program.post_order_blocks);
Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::IdentityRemovalPass(program.blocks);
if (info.l_stage == LogicalStage::TessellationControl) { if (info.l_stage == LogicalStage::TessellationControl) {
@ -82,9 +86,6 @@ IR::Program TranslateProgram(std::span<const u32> code, Pools& pools, Info& info
} }
Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::ConstantPropagationPass(program.post_order_blocks);
Shader::Optimization::RingAccessElimination(program, runtime_info, stage); Shader::Optimization::RingAccessElimination(program, runtime_info, stage);
if (stage != Stage::Compute) {
Shader::Optimization::LowerSharedMemToRegisters(program);
}
Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::ConstantPropagationPass(program.post_order_blocks);
Shader::Optimization::FlattenExtendedUserdataPass(program); Shader::Optimization::FlattenExtendedUserdataPass(program);
Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::ResourceTrackingPass(program);

View File

@ -143,6 +143,11 @@ struct Liverpool {
const u32 num_dwords = bininfo.length / sizeof(u32); const u32 num_dwords = bininfo.length / sizeof(u32);
return std::span{code, num_dwords}; return std::span{code, num_dwords};
} }
[[nodiscard]] u32 NumVgprs() const {
// Each increment allocates 4 registers, where 0 = 4 registers.
return (settings.num_vgprs + 1) * 4;
}
}; };
struct HsTessFactorClamp { struct HsTessFactorClamp {

View File

@ -84,7 +84,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS
const auto BuildCommon = [&](const auto& program) { const auto BuildCommon = [&](const auto& program) {
info.num_user_data = program.settings.num_user_regs; info.num_user_data = program.settings.num_user_regs;
info.num_input_vgprs = program.settings.vgpr_comp_cnt; info.num_input_vgprs = program.settings.vgpr_comp_cnt;
info.num_allocated_vgprs = program.settings.num_vgprs * 4; info.num_allocated_vgprs = program.NumVgprs();
info.fp_denorm_mode32 = program.settings.fp_denorm_mode32; info.fp_denorm_mode32 = program.settings.fp_denorm_mode32;
info.fp_round_mode32 = program.settings.fp_round_mode32; info.fp_round_mode32 = program.settings.fp_round_mode32;
}; };

View File

@ -535,6 +535,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
.descriptorType = vk::DescriptorType::eStorageBuffer, .descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &buffer_infos.back(), .pBufferInfo = &buffer_infos.back(),
}); });
++binding.buffer;
} }
// Bind the flattened user data buffer as a UBO so it's accessible to the shader // Bind the flattened user data buffer as a UBO so it's accessible to the shader