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

View File

@ -3,6 +3,7 @@ version = 1
[[annotations]]
path = [
"REUSE.toml",
"crowdin.yml",
"CMakeSettings.json",
".github/FUNDING.yml",
".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.
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.
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:
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.
### (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:
1. Click x64-Clang-Release and select "Manage Configurations"
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)
3. Press CTRL+S to save and wait a moment for CMake generation
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\`
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)
## 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;
}
int PS4_SYSV_ABI sceGnmUpdateHsShader() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
int PS4_SYSV_ABI sceGnmUpdateHsShader(u32* cmdbuf, u32 size, const u32* hs_regs, u32 ls_hs_config) {
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;
}

View File

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

View File

@ -18,17 +18,8 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* pare
treeWidget = new QTreeWidget(this);
treeWidget->setColumnCount(9);
QStringList headers;
headers << "Name"
<< "Serial"
<< "Installed"
<< "Size"
<< "Category"
<< "Type"
<< "App Ver"
<< "FW"
<< "Region"
<< "Flags"
<< "Path";
headers << tr("Name") << tr("Serial") << tr("Installed") << tr("Size") << tr("Category")
<< tr("Type") << tr("App Ver") << tr("FW") << tr("Region") << tr("Flags") << tr("Path");
treeWidget->setHeaderLabels(headers);
treeWidget->header()->setDefaultAlignment(Qt::AlignCenter);
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
@ -36,7 +27,7 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* pare
this->setCentralWidget(treeWidget);
QMenuBar* menuBar = new QMenuBar(this);
menuBar->setContextMenuPolicy(Qt::PreventContextMenu);
QMenu* fileMenu = menuBar->addMenu(tr("&File"));
QMenu* fileMenu = menuBar->addMenu(tr("File"));
QAction* openFolderAct = new QAction(tr("Open Folder"), this);
fileMenu->addAction(openFolderAct);
this->setMenuBar(menuBar);
@ -114,15 +105,15 @@ void PKGViewer::ProcessPKGInfo() {
return;
}
psf.Open(package.sfo);
QString title_name =
QString::fromStdString(std::string{psf.GetString("TITLE").value_or("Unknown")});
QString title_id =
QString::fromStdString(std::string{psf.GetString("TITLE_ID").value_or("Unknown")});
QString title_name = QString::fromStdString(
std::string{psf.GetString("TITLE").value_or(std::string{tr("Unknown").toStdString()})});
QString title_id = QString::fromStdString(std::string{
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_version =
QString::fromStdString(std::string{psf.GetString("APP_VER").value_or("Unknown")});
QString title_category =
QString::fromStdString(std::string{psf.GetString("CATEGORY").value_or("Unknown")});
QString app_version = QString::fromStdString(std::string{
psf.GetString("APP_VER").value_or(std::string{tr("Unknown").toStdString()})});
QString title_category = QString::fromStdString(std::string{
psf.GetString("CATEGORY").value_or(std::string{tr("Unknown").toStdString()})});
QString pkg_size = GameListUtils::FormatSize(package.GetPkgHeader().pkg_size);
pkg_content_flag = package.GetPkgHeader().pkg_content_flags;
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()) {
const u32 fw_int = *fw_int_opt;
if (fw_int == 0) {
@ -221,6 +212,6 @@ void PKGViewer::ProcessPKGInfo() {
// Update status bar.
statusBar->clearMessage();
int numPkgs = m_pkg_list.size();
QString statusMessage = QString::number(numPkgs) + " Package.";
QString statusMessage = QString::number(numPkgs) + " " + tr("Package");
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"?>
<!-- SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later -->
<!DOCTYPE TS>
<TS version="2.1" language="sv_SE">
<!-- SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later -->
<context>
<name>AboutDialog</name>
<message>
@ -244,14 +244,6 @@
<source>Can&apos;t apply cheats before the game is started</source>
<translation>Kan inte tillämpa fusk innan spelet är startat</translation>
</message>
<message>
<source>Error:</source>
<translation>Fel:</translation>
</message>
<message>
<source>ERROR</source>
<translation>FEL</translation>
</message>
<message>
<source>Close</source>
<translation>Stäng</translation>
@ -386,10 +378,6 @@
<source>Unable to update compatibility data! Try again later.</source>
<translation>Kunde inte uppdatera kompatibilitetsdata! Försök igen senare.</translation>
</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>
<source>Unknown</source>
<translation>Okänt</translation>
@ -673,10 +661,6 @@
<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>
</message>
<message>
<source>Click to go to issue</source>
<translation type="vanished">Klicka för att till problem</translation>
</message>
<message>
<source>Last updated</source>
<translation>Senast uppdaterad</translation>
@ -1196,14 +1180,66 @@
<source>Open Folder</source>
<translation>Öppna mapp</translation>
</message>
<message>
<source>&amp;File</source>
<translation>&amp;Arkiv</translation>
</message>
<message>
<source>PKG ERROR</source>
<translation>PKG-FEL</translation>
</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>
<name>SettingsDialog</name>
@ -1255,10 +1291,6 @@
<source>Show Splash</source>
<translation>Visa startskärm</translation>
</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>
<source>Enable Discord Rich Presence</source>
<translation>Aktivera Discord Rich Presence</translation>
@ -1323,10 +1355,6 @@
<source>Graphics</source>
<translation>Grafik</translation>
</message>
<message>
<source>Gui</source>
<translation type="vanished">Gränssnitt</translation>
</message>
<message>
<source>User</source>
<translation>Användare</translation>
@ -1743,6 +1771,14 @@
<source>GUIBackgroundImageGroupBox</source>
<translation>Bakgrundsbild:\nKontrollerar opaciteten för spelets bakgrundsbild</translation>
</message>
<message>
<source>Enable HDR</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>enableHDRCheckBox</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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 EmitLoadSharedU32(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 EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value);
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMax32(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) {
const Id shift{ctx.ConstU32(2U)};
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

View File

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

View File

@ -233,7 +233,8 @@ struct Info {
}
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.unified += total_buffers + images.size() + samplers.size();
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);
case 64:
return Inst(Opcode::LoadSharedU64, offset);
case 128:
return Inst(Opcode::LoadSharedU128, offset);
default:
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:
Inst(Opcode::WriteSharedU64, offset, value);
break;
case 128:
Inst(Opcode::WriteSharedU128, offset, value);
break;
default:
UNREACHABLE_MSG("Invalid bit size {}", bit_size);
}

View File

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

View File

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

View File

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

View File

@ -1,38 +1,81 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// 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"
namespace Shader::Optimization {
void LowerSharedMemToRegisters(IR::Program& program) {
boost::container::small_vector<IR::Inst*, 8> ds_writes;
Info& info{program.info};
static bool IsSharedMemoryInst(const IR::Inst& inst) {
const auto opcode = inst.GetOpcode();
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::Inst& inst : block->Instructions()) {
const auto opcode = inst.GetOpcode();
if (opcode == IR::Opcode::WriteSharedU32 || opcode == IR::Opcode::WriteSharedU64) {
ds_writes.emplace_back(&inst);
if (!IsSharedMemoryInst(inst)) {
continue;
}
if (opcode == IR::Opcode::LoadSharedU32 || opcode == IR::Opcode::LoadSharedU64) {
// Search for write instruction with same offset
const IR::Inst* prod = inst.Arg(0).InstRecursive();
const auto it = std::ranges::find_if(ds_writes, [&](const IR::Inst* write) {
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));
const auto offset = GetSharedMemImmOffset(inst);
const auto vgpr = get_vgpr(offset);
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
ConvertSharedMemToVgpr(ir, inst, vgpr);
}
}
}
// We should have eliminated everything. Invalidate data write instructions.
for (const auto inst : ds_writes) {
inst->Invalidate();
}
}
} // namespace Shader::Optimization

View File

@ -65,6 +65,10 @@ IR::Program TranslateProgram(std::span<const u32> code, Pools& pools, Info& info
// Run optimization passes
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::IdentityRemovalPass(program.blocks);
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::RingAccessElimination(program, runtime_info, stage);
if (stage != Stage::Compute) {
Shader::Optimization::LowerSharedMemToRegisters(program);
}
Shader::Optimization::ConstantPropagationPass(program.post_order_blocks);
Shader::Optimization::FlattenExtendedUserdataPass(program);
Shader::Optimization::ResourceTrackingPass(program);

View File

@ -143,6 +143,11 @@ struct Liverpool {
const u32 num_dwords = bininfo.length / sizeof(u32);
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 {

View File

@ -84,7 +84,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS
const auto BuildCommon = [&](const auto& program) {
info.num_user_data = program.settings.num_user_regs;
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_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,
.pBufferInfo = &buffer_infos.back(),
});
++binding.buffer;
}
// Bind the flattened user data buffer as a UBO so it's accessible to the shader