diff --git a/CMakeLists.txt b/CMakeLists.txt
index a88c8cd06..c8883a042 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,8 +54,9 @@ else()
endif()
if (ARCHITECTURE STREQUAL "x86_64")
- # Target the same x86_64 feature set as the PS4 CPU to match requirements.
- add_compile_options(-march=btver2 -mno-sse4a)
+ # Target the same CPU architecture as the PS4, to maintain the same level of compatibility.
+ # Exclude SSE4a as it is only available on AMD CPUs.
+ add_compile_options(-march=btver2 -mtune=generic -mno-sse4a)
endif()
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
@@ -202,6 +203,8 @@ execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE
)
+set(APP_VERSION "0.7.1 WIP")
+set(APP_IS_RELEASE false)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY)
message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}")
@@ -218,7 +221,7 @@ find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.0 CONFIG)
-find_package(VulkanHeaders 1.4.305 CONFIG)
+find_package(VulkanHeaders 1.4.309 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
@@ -674,7 +677,6 @@ set(COMMON src/common/logging/backend.cpp
src/common/uint128.h
src/common/unique_function.h
src/common/va_ctx.h
- src/common/version.h
src/common/ntapi.h
src/common/ntapi.cpp
src/common/number_utils.h
@@ -1196,8 +1198,8 @@ if (ENABLE_QT_GUI)
MACOSX_BUNDLE ON
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in"
MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
- MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1"
- )
+ MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}"
+ )
set_source_files_properties(src/images/shadPS4.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
diff --git a/README.md b/README.md
index 6462c8bd2..0e2248970 100644
--- a/README.md
+++ b/README.md
@@ -122,6 +122,27 @@ R3 | M |
Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more.
+# Firmware files
+
+shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.\
+The following firmware modules are supported and must be placed in shadPS4's `user/sys_modules` folder.
+
+
+
+| Modules | Modules | Modules | Modules |
+|-------------------------|-------------------------|-------------------------|-------------------------|
+| libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx |
+| libSceJson.sprx | libSceJson2.sprx | libSceLibcInternal.sprx | libSceNgs2.sprx |
+| libSceRtc.sprx | libSceUlt.sprx | | |
+
+
+
+> [!Caution]
+> The above modules are required to run the games properly and must be extracted from your PlayStation 4.\
+> **We do not provide any information or support on how to do this**.
+
+
+
# Main team
- [**georgemoralis**](https://github.com/georgemoralis)
diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md
index 9c6bc5a6f..55825ac7d 100644
--- a/documents/Quickstart/Quickstart.md
+++ b/documents/Quickstart/Quickstart.md
@@ -24,7 +24,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
- **Intel**: Haswell generation or newer
- **AMD**: Jaguar generation or newer
- - **Apple**: Rosetta 2 on macOS 15 or newer
+ - **Apple**: Rosetta 2 on macOS 15.4 or newer
### GPU
diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK
index 2048427e5..83510e0f3 160000
--- a/externals/MoltenVK/MoltenVK
+++ b/externals/MoltenVK/MoltenVK
@@ -1 +1 @@
-Subproject commit 2048427e50f9eb20f2b8f98d316ecaee398c9b91
+Subproject commit 83510e0f3835c3c43651dda087305abc42572e17
diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross
index 2c32b6bf8..cb71abe30 160000
--- a/externals/MoltenVK/SPIRV-Cross
+++ b/externals/MoltenVK/SPIRV-Cross
@@ -1 +1 @@
-Subproject commit 2c32b6bf86f3c4a5539aa1f0bacbd59fe61759cf
+Subproject commit cb71abe3063094bf383379b15473d39cb1144120
diff --git a/externals/vulkan-headers b/externals/vulkan-headers
index a03d2f6d5..952f776f6 160000
--- a/externals/vulkan-headers
+++ b/externals/vulkan-headers
@@ -1 +1 @@
-Subproject commit a03d2f6d5753b365d704d58161825890baad0755
+Subproject commit 952f776f6573aafbb62ea717d871cd1d6816c387
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 2657cd12a..111c0cfa9 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -7,10 +7,10 @@
#include // for wstring support
#include
+#include "common/config.h"
+#include "common/logging/formatter.h"
#include "common/path_util.h"
-#include "config.h"
-#include "logging/formatter.h"
-#include "version.h"
+#include "common/scm_rev.h"
namespace toml {
template
@@ -763,7 +763,7 @@ void load(const std::filesystem::path& path) {
logFilter = toml::find_or(general, "logFilter", "");
logType = toml::find_or(general, "logType", "sync");
userName = toml::find_or(general, "userName", "shadPS4");
- if (Common::isRelease) {
+ if (Common::g_is_release) {
updateChannel = toml::find_or(general, "updateChannel", "Release");
} else {
updateChannel = toml::find_or(general, "updateChannel", "Nightly");
@@ -1108,7 +1108,7 @@ void setDefaultValues() {
logFilter = "";
logType = "sync";
userName = "shadPS4";
- if (Common::isRelease) {
+ if (Common::g_is_release) {
updateChannel = "Release";
} else {
updateChannel = "Nightly";
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 2de04e0be..71c4c2d0a 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -3,21 +3,17 @@
#include "common/scm_rev.h"
-#define GIT_REV "@GIT_REV@"
-#define GIT_BRANCH "@GIT_BRANCH@"
-#define GIT_DESC "@GIT_DESC@"
-#define GIT_REMOTE_NAME "@GIT_REMOTE_NAME@"
-#define GIT_REMOTE_URL "@GIT_REMOTE_URL@"
-#define BUILD_DATE "@BUILD_DATE@"
-
namespace Common {
-const char g_scm_rev[] = GIT_REV;
-const char g_scm_branch[] = GIT_BRANCH;
-const char g_scm_desc[] = GIT_DESC;
-const char g_scm_remote_name[] = GIT_REMOTE_NAME;
-const char g_scm_remote_url[] = GIT_REMOTE_URL;
-const char g_scm_date[] = BUILD_DATE;
+constexpr char g_version[] = "@APP_VERSION@";
+constexpr bool g_is_release = @APP_IS_RELEASE@;
+
+constexpr char g_scm_rev[] = "@GIT_REV@";
+constexpr char g_scm_branch[] = "@GIT_BRANCH@";
+constexpr char g_scm_desc[] = "@GIT_DESC@";
+constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@";
+constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@";
+constexpr char g_scm_date[] = "@BUILD_DATE@";
} // namespace
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index f38efff42..36b844e94 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -5,6 +5,9 @@
namespace Common {
+extern const char g_version[];
+extern const bool g_is_release;
+
extern const char g_scm_rev[];
extern const char g_scm_branch[];
extern const char g_scm_desc[];
diff --git a/src/common/version.h b/src/common/version.h
deleted file mode 100644
index 652f36e6d..000000000
--- a/src/common/version.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-
-namespace Common {
-
-constexpr char VERSION[] = "0.7.1 WIP";
-constexpr bool isRelease = false;
-
-} // namespace Common
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 5f94f008a..1a71b99cb 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -22,7 +22,6 @@
#include "common/polyfill_thread.h"
#include "common/scm_rev.h"
#include "common/singleton.h"
-#include "common/version.h"
#include "core/file_format/psf.h"
#include "core/file_format/trp.h"
#include "core/file_sys/fs.h"
@@ -123,7 +122,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version);
std::string window_title = "";
- if (Common::isRelease) {
- window_title = fmt::format("shadPS4 v{} | {}", Common::VERSION, game_title);
+ if (Common::g_is_release) {
+ window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
} else {
std::string remote_url(Common::g_scm_remote_url);
std::string remote_host;
@@ -208,10 +207,10 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorAxis(0, c_axis, GetAxis(0x0, 0x80, *new_param));
+ controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
controller->CheckButton(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20);
return;
case Axis::TriggerRight:
ApplyDeadzone(new_param, righttrigger_deadzone);
- controller->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param));
+ controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
controller->CheckButton(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20);
return;
default:
break;
}
- controller->Axis(0, c_axis, GetAxis(-0x80, 0x80, *new_param * multiplier));
+ controller->Axis(0, c_axis, GetAxis(-0x80, 0x7f, *new_param * multiplier));
}
}
diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp
index 11feaeebb..c84d14b3f 100644
--- a/src/input/input_mouse.cpp
+++ b/src/input/input_mouse.cpp
@@ -61,11 +61,11 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed;
if (d_x != 0 && d_y != 0) {
- controller->Axis(0, axis_x, GetAxis(-0x80, 0x80, a_x));
- controller->Axis(0, axis_y, GetAxis(-0x80, 0x80, a_y));
+ controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, a_x));
+ controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, a_y));
} else {
- controller->Axis(0, axis_x, GetAxis(-0x80, 0x80, 0));
- controller->Axis(0, axis_y, GetAxis(-0x80, 0x80, 0));
+ controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, 0));
+ controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, 0));
}
return interval;
diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp
index e73a66a71..7d3a42798 100644
--- a/src/qt_gui/check_update.cpp
+++ b/src/qt_gui/check_update.cpp
@@ -24,7 +24,6 @@
#include
#include
#include
-#include
#include "check_update.h"
using namespace Common::FS;
@@ -52,7 +51,7 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) {
url = QUrl("https://api.github.com/repos/shadps4-emu/shadPS4/releases/latest");
checkName = false;
} else {
- if (Common::isRelease) {
+ if (Common::g_is_release) {
Config::setUpdateChannel("Release");
} else {
Config::setUpdateChannel("Nightly");
@@ -162,7 +161,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
QString currentRev = (updateChannel == "Nightly")
? QString::fromStdString(Common::g_scm_rev)
- : "v." + QString::fromStdString(Common::VERSION);
+ : "v." + QString::fromStdString(Common::g_version);
QString currentDate = Common::g_scm_date;
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h
index b5732d0ca..2fd4588d2 100644
--- a/src/qt_gui/gui_context_menus.h
+++ b/src/qt_gui/gui_context_menus.h
@@ -13,7 +13,7 @@
#include "cheats_patches.h"
#include "common/config.h"
#include "common/path_util.h"
-#include "common/version.h"
+#include "common/scm_rev.h"
#include "compatibility_info.h"
#include "game_info.h"
#include "trophy_viewer.h"
@@ -115,14 +115,15 @@ public:
compatibilityMenu->addAction(updateCompatibility);
compatibilityMenu->addAction(viewCompatibilityReport);
- if (Common::isRelease) {
+ if (Common::g_is_release) {
compatibilityMenu->addAction(submitCompatibilityReport);
}
menu.addMenu(compatibilityMenu);
compatibilityMenu->setEnabled(Config::getCompatibilityEnabled());
- viewCompatibilityReport->setEnabled(!m_games[itemID].compatibility.url.isEmpty());
+ viewCompatibilityReport->setEnabled(m_games[itemID].compatibility.status !=
+ CompatibilityStatus::Unknown);
// Show menu.
auto selected = menu.exec(global_pos);
@@ -557,24 +558,36 @@ public:
}
if (selected == viewCompatibilityReport) {
- if (!m_games[itemID].compatibility.url.isEmpty())
- QDesktopServices::openUrl(QUrl(m_games[itemID].compatibility.url));
+ if (m_games[itemID].compatibility.issue_number != "") {
+ auto url_issues =
+ "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/";
+ QDesktopServices::openUrl(
+ QUrl(url_issues + m_games[itemID].compatibility.issue_number));
+ }
}
if (selected == submitCompatibilityReport) {
- QUrl url = QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new");
- QUrlQuery query;
- query.addQueryItem("template", QString("game_compatibility.yml"));
- query.addQueryItem(
- "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial),
- QString::fromStdString(m_games[itemID].name)));
- query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name));
- query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial));
- query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version));
- query.addQueryItem("emulator-version", QString(Common::VERSION));
- url.setQuery(query);
+ if (m_games[itemID].compatibility.issue_number == "") {
+ QUrl url =
+ QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new");
+ QUrlQuery query;
+ query.addQueryItem("template", QString("game_compatibility.yml"));
+ query.addQueryItem(
+ "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial),
+ QString::fromStdString(m_games[itemID].name)));
+ query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name));
+ query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial));
+ query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version));
+ query.addQueryItem("emulator-version", QString(Common::g_version));
+ url.setQuery(query);
- QDesktopServices::openUrl(url);
+ QDesktopServices::openUrl(url);
+ } else {
+ auto url_issues =
+ "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/";
+ QDesktopServices::openUrl(
+ QUrl(url_issues + m_games[itemID].compatibility.issue_number));
+ }
}
}
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 072ad70e5..60ab58274 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -18,7 +18,6 @@
#include "common/path_util.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
-#include "common/version.h"
#include "control_settings.h"
#include "game_install_dialog.h"
#include "kbm_gui.h"
@@ -58,8 +57,8 @@ bool MainWindow::Init() {
// show ui
setMinimumSize(720, 405);
std::string window_title = "";
- if (Common::isRelease) {
- window_title = fmt::format("shadPS4 v{}", Common::VERSION);
+ if (Common::g_is_release) {
+ window_title = fmt::format("shadPS4 v{}", Common::g_version);
} else {
std::string remote_url(Common::g_scm_remote_url);
std::string remote_host;
@@ -69,10 +68,10 @@ bool MainWindow::Init() {
remote_host = "unknown";
}
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
- window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch,
+ window_title = fmt::format("shadPS4 v{} {} {}", Common::g_version, Common::g_scm_branch,
Common::g_scm_desc);
} else {
- window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::VERSION, remote_host,
+ window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::g_version, remote_host,
Common::g_scm_branch, Common::g_scm_desc);
}
}
diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp
index 25c27fef3..5ee802b0c 100644
--- a/src/qt_gui/settings_dialog.cpp
+++ b/src/qt_gui/settings_dialog.cpp
@@ -9,7 +9,7 @@
#include
#include "common/config.h"
-#include "common/version.h"
+#include "common/scm_rev.h"
#include "qt_gui/compatibility_info.h"
#ifdef ENABLE_DISCORD_RPC
#include "common/discord_rpc_handler.h"
@@ -491,7 +491,7 @@ void SettingsDialog::LoadValuesFromConfig() {
QString updateChannel = QString::fromStdString(Config::getUpdateChannel());
ui->updateComboBox->setCurrentText(
channelMap.key(updateChannel != "Release" && updateChannel != "Nightly"
- ? (Common::isRelease ? "Release" : "Nightly")
+ ? (Common::g_is_release ? "Release" : "Nightly")
: updateChannel));
#endif
diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp
index fcdde7240..e369240c6 100644
--- a/src/sdl_window.cpp
+++ b/src/sdl_window.cpp
@@ -10,7 +10,6 @@
#include "common/assert.h"
#include "common/config.h"
#include "common/elf_info.h"
-#include "common/version.h"
#include "core/debug_state.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/pad/pad.h"
diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp
index 843bedb20..a6ae0c304 100644
--- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp
@@ -156,6 +156,18 @@ vk::CullModeFlags CullMode(Liverpool::CullMode mode) {
}
}
+vk::FrontFace FrontFace(Liverpool::FrontFace face) {
+ switch (face) {
+ case Liverpool::FrontFace::Clockwise:
+ return vk::FrontFace::eClockwise;
+ case Liverpool::FrontFace::CounterClockwise:
+ return vk::FrontFace::eCounterClockwise;
+ default:
+ UNREACHABLE();
+ return vk::FrontFace::eClockwise;
+ }
+}
+
vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor) {
using BlendFactor = Liverpool::BlendControl::BlendFactor;
switch (factor) {
diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h
index 42da7aa06..fca0a8378 100644
--- a/src/video_core/renderer_vulkan/liverpool_to_vk.h
+++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h
@@ -26,6 +26,8 @@ vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode);
vk::CullModeFlags CullMode(Liverpool::CullMode mode);
+vk::FrontFace FrontFace(Liverpool::FrontFace mode);
+
vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor);
vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 7cd4bd872..354e22331 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -28,6 +28,15 @@ static constexpr std::array LogicalStageToStageBit = {
vk::ShaderStageFlagBits::eCompute,
};
+static bool IsPrimitiveTopologyList(const vk::PrimitiveTopology topology) {
+ return topology == vk::PrimitiveTopology::ePointList ||
+ topology == vk::PrimitiveTopology::eLineList ||
+ topology == vk::PrimitiveTopology::eTriangleList ||
+ topology == vk::PrimitiveTopology::eLineListWithAdjacency ||
+ topology == vk::PrimitiveTopology::eTriangleListWithAdjacency ||
+ topology == vk::PrimitiveTopology::ePatchList;
+}
+
GraphicsPipeline::GraphicsPipeline(
const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
const Shader::Profile& profile, const GraphicsPipelineKey& key_,
@@ -75,19 +84,15 @@ GraphicsPipeline::GraphicsPipeline(
.pVertexAttributeDescriptions = vertex_attributes.data(),
};
- auto prim_restart = key.enable_primitive_restart != 0;
- if (prim_restart && IsPrimitiveListTopology() && !instance.IsListRestartSupported()) {
- LOG_DEBUG(Render_Vulkan,
- "Primitive restart is enabled for list topology but not supported by driver.");
- prim_restart = false;
- }
+ const auto topology = LiverpoolToVK::PrimitiveType(key.prim_type);
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
- .topology = LiverpoolToVK::PrimitiveType(key.prim_type),
- .primitiveRestartEnable = prim_restart,
+ .topology = topology,
+ // Avoid warning spam on all pipelines about unsupported restart disable, if not supported.
+ // However, must be false for list topologies to avoid validation errors.
+ .primitiveRestartEnable =
+ !instance.IsPrimitiveRestartDisableSupported() && !IsPrimitiveTopologyList(topology),
};
- ASSERT_MSG(!prim_restart || key.primitive_restart_index == 0xFFFF ||
- key.primitive_restart_index == 0xFFFFFFFF,
- "Primitive restart index other than -1 is not supported yet");
+
const bool is_rect_list = key.prim_type == AmdGpu::PrimitiveType::RectList;
const bool is_quad_list = key.prim_type == AmdGpu::PrimitiveType::QuadList;
const auto& fs_info = runtime_infos[u32(Shader::LogicalStage::Fragment)].fs_info;
@@ -99,12 +104,6 @@ GraphicsPipeline::GraphicsPipeline(
.depthClampEnable = false,
.rasterizerDiscardEnable = false,
.polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode),
- .cullMode = LiverpoolToVK::IsPrimitiveCulled(key.prim_type)
- ? LiverpoolToVK::CullMode(key.cull_mode)
- : vk::CullModeFlagBits::eNone,
- .frontFace = key.front_face == Liverpool::FrontFace::Clockwise
- ? vk::FrontFace::eClockwise
- : vk::FrontFace::eCounterClockwise,
.lineWidth = 1.0f,
};
@@ -122,16 +121,20 @@ GraphicsPipeline::GraphicsPipeline(
.pNext = instance.IsDepthClipControlSupported() ? &clip_control : nullptr,
};
- boost::container::static_vector dynamic_states = {
+ boost::container::static_vector dynamic_states = {
vk::DynamicState::eViewportWithCountEXT, vk::DynamicState::eScissorWithCountEXT,
vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthTestEnableEXT,
vk::DynamicState::eDepthWriteEnableEXT, vk::DynamicState::eDepthCompareOpEXT,
vk::DynamicState::eDepthBiasEnableEXT, vk::DynamicState::eDepthBias,
vk::DynamicState::eStencilTestEnableEXT, vk::DynamicState::eStencilReference,
vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask,
- vk::DynamicState::eStencilOpEXT,
+ vk::DynamicState::eStencilOpEXT, vk::DynamicState::eCullModeEXT,
+ vk::DynamicState::eFrontFaceEXT,
};
+ if (instance.IsPrimitiveRestartDisableSupported()) {
+ dynamic_states.push_back(vk::DynamicState::ePrimitiveRestartEnableEXT);
+ }
if (instance.IsDepthBoundsSupported()) {
dynamic_states.push_back(vk::DynamicState::eDepthBoundsTestEnableEXT);
dynamic_states.push_back(vk::DynamicState::eDepthBounds);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 7ffd14064..59230ae46 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -42,11 +42,7 @@ struct GraphicsPipelineKey {
u32 num_samples;
u32 mrt_mask;
AmdGpu::PrimitiveType prim_type;
- u32 enable_primitive_restart;
- u32 primitive_restart_index;
Liverpool::PolygonMode polygon_mode;
- Liverpool::CullMode cull_mode;
- Liverpool::FrontFace front_face;
Liverpool::ClipSpace clip_space;
Liverpool::ColorBufferMask cb_shader_mask;
std::array blend_controls;
@@ -82,16 +78,6 @@ public:
return key.mrt_mask;
}
- [[nodiscard]] bool IsPrimitiveListTopology() const {
- return key.prim_type == AmdGpu::PrimitiveType::PointList ||
- key.prim_type == AmdGpu::PrimitiveType::LineList ||
- key.prim_type == AmdGpu::PrimitiveType::TriangleList ||
- key.prim_type == AmdGpu::PrimitiveType::AdjLineList ||
- key.prim_type == AmdGpu::PrimitiveType::AdjTriangleList ||
- key.prim_type == AmdGpu::PrimitiveType::RectList ||
- key.prim_type == AmdGpu::PrimitiveType::QuadList;
- }
-
/// Gets the attributes and bindings for vertex inputs.
template
void GetVertexInputs(VertexInputs& attributes, VertexInputs& bindings,
diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h
index 04b68c1d0..6de419041 100644
--- a/src/video_core/renderer_vulkan/vk_instance.h
+++ b/src/video_core/renderer_vulkan/vk_instance.h
@@ -292,6 +292,11 @@ public:
properties.limits.framebufferStencilSampleCounts;
}
+ /// Returns whether disabling primitive restart is supported.
+ bool IsPrimitiveRestartDisableSupported() const {
+ return driver_id != vk::DriverId::eMoltenvk;
+ }
+
private:
/// Creates the logical device opportunistically enabling extensions
bool CreateDevice();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 17a1fdec4..bad2a549c 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -283,12 +283,8 @@ bool PipelineCache::RefreshGraphicsKey() {
}
key.prim_type = regs.primitive_type;
- key.enable_primitive_restart = regs.enable_primitive_restart & 1;
- key.primitive_restart_index = regs.primitive_restart_index;
key.polygon_mode = regs.polygon_control.PolyMode();
- key.cull_mode = regs.polygon_control.CullingMode();
key.clip_space = regs.clipper_control.clip_space;
- key.front_face = regs.polygon_control.front_face;
key.num_samples = regs.NumSamples();
const bool skip_cb_binding =
diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp
index 716473377..e656369b2 100644
--- a/src/video_core/renderer_vulkan/vk_platform.cpp
+++ b/src/video_core/renderer_vulkan/vk_platform.cpp
@@ -278,7 +278,6 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e
vk::Bool32 enable_force_barriers = vk::True;
#ifdef __APPLE__
const vk::Bool32 mvk_debug_mode = enable_crash_diagnostic ? vk::True : vk::False;
- constexpr vk::Bool32 mvk_use_mtlheap = vk::True;
#endif
const std::array layer_setings = {
@@ -355,15 +354,6 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e
.valueCount = 1,
.pValues = &mvk_debug_mode,
},
- // Use MTLHeap to back device memory, which among other things allows us to
- // use VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT via memory aliasing.
- vk::LayerSettingEXT{
- .pLayerName = "MoltenVK",
- .pSettingName = "MVK_CONFIG_USE_MTLHEAP",
- .type = vk::LayerSettingTypeEXT::eBool32,
- .valueCount = 1,
- .pValues = &mvk_use_mtlheap,
- },
#endif
};
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ecb0c0a75..30102960a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -946,19 +946,20 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) {
mapped_ranges -= boost::icl::interval::right_open(addr, addr + size);
}
-void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) {
+void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) const {
UpdateViewportScissorState();
UpdateDepthStencilState();
+ UpdatePrimitiveState();
- const auto& regs = liverpool->regs;
- const auto cmdbuf = scheduler.CommandBuffer();
- cmdbuf.setBlendConstants(®s.blend_constants.red);
- if (instance.IsDynamicColorWriteMaskSupported()) {
- cmdbuf.setColorWriteMaskEXT(0, pipeline.GetWriteMasks());
- }
+ auto& dynamic_state = scheduler.GetDynamicState();
+ dynamic_state.SetBlendConstants(&liverpool->regs.blend_constants.red);
+ dynamic_state.SetColorWriteMasks(pipeline.GetWriteMasks());
+
+ // Commit new dynamic state to the command buffer.
+ dynamic_state.Commit(instance, scheduler.CommandBuffer());
}
-void Rasterizer::UpdateViewportScissorState() {
+void Rasterizer::UpdateViewportScissorState() const {
const auto& regs = liverpool->regs;
const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
@@ -1071,95 +1072,86 @@ void Rasterizer::UpdateViewportScissorState() {
scissors.push_back(empty_scissor);
}
- const auto cmdbuf = scheduler.CommandBuffer();
- cmdbuf.setViewportWithCountEXT(viewports);
- cmdbuf.setScissorWithCountEXT(scissors);
+ auto& dynamic_state = scheduler.GetDynamicState();
+ dynamic_state.SetViewports(viewports);
+ dynamic_state.SetScissors(scissors);
}
-void Rasterizer::UpdateDepthStencilState() {
- auto& regs = liverpool->regs;
- const auto cmdbuf = scheduler.CommandBuffer();
+void Rasterizer::UpdateDepthStencilState() const {
+ const auto& regs = liverpool->regs;
+ auto& dynamic_state = scheduler.GetDynamicState();
- bool depth_test = regs.depth_control.depth_enable && regs.depth_buffer.DepthValid();
- cmdbuf.setDepthTestEnableEXT(depth_test);
- cmdbuf.setDepthWriteEnableEXT(regs.depth_control.depth_write_enable &&
- !regs.depth_render_control.depth_clear_enable);
- if (depth_test) {
- cmdbuf.setDepthCompareOpEXT(LiverpoolToVK::CompareOp(regs.depth_control.depth_func));
+ const auto depth_test_enabled =
+ regs.depth_control.depth_enable && regs.depth_buffer.DepthValid();
+ dynamic_state.SetDepthTestEnabled(depth_test_enabled);
+ if (depth_test_enabled) {
+ dynamic_state.SetDepthWriteEnabled(regs.depth_control.depth_write_enable &&
+ !regs.depth_render_control.depth_clear_enable);
+ dynamic_state.SetDepthCompareOp(LiverpoolToVK::CompareOp(regs.depth_control.depth_func));
}
- if (instance.IsDepthBoundsSupported()) {
- cmdbuf.setDepthBoundsTestEnableEXT(regs.depth_control.depth_bounds_enable);
- if (regs.depth_control.depth_bounds_enable) {
- cmdbuf.setDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max);
- }
+ const auto depth_bounds_test_enabled = regs.depth_control.depth_bounds_enable;
+ dynamic_state.SetDepthBoundsTestEnabled(depth_bounds_test_enabled);
+ if (depth_bounds_test_enabled) {
+ dynamic_state.SetDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max);
}
- cmdbuf.setDepthBiasEnableEXT(regs.polygon_control.NeedsBias());
- if (regs.polygon_control.enable_polygon_offset_front) {
- cmdbuf.setDepthBias(regs.poly_offset.front_offset, regs.poly_offset.depth_bias,
- regs.poly_offset.front_scale / 16.f);
- } else if (regs.polygon_control.enable_polygon_offset_back) {
- cmdbuf.setDepthBias(regs.poly_offset.back_offset, regs.poly_offset.depth_bias,
- regs.poly_offset.back_scale / 16.f);
+ const auto depth_bias_enabled = regs.polygon_control.NeedsBias();
+ dynamic_state.SetDepthBiasEnabled(depth_bias_enabled);
+ if (depth_bias_enabled) {
+ const bool front = regs.polygon_control.enable_polygon_offset_front;
+ dynamic_state.SetDepthBias(
+ front ? regs.poly_offset.front_offset : regs.poly_offset.back_offset,
+ regs.poly_offset.depth_bias,
+ (front ? regs.poly_offset.front_scale : regs.poly_offset.back_scale) / 16.f);
}
- cmdbuf.setStencilTestEnableEXT(regs.depth_control.stencil_enable &&
- regs.depth_buffer.StencilValid());
- if (regs.depth_control.stencil_enable) {
- const auto front_fail_op =
- LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front);
- const auto front_pass_op =
- LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front);
- const auto front_depth_fail_op =
- LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front);
- const auto front_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func);
- if (regs.depth_control.backface_enable) {
- const auto back_fail_op =
- LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back);
- const auto back_pass_op =
- LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back);
- const auto back_depth_fail_op =
- LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back);
- const auto back_compare_op =
- LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func);
- cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, front_fail_op, front_pass_op,
- front_depth_fail_op, front_compare_op);
- cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, back_fail_op, back_pass_op,
- back_depth_fail_op, back_compare_op);
- } else {
- cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, front_fail_op,
- front_pass_op, front_depth_fail_op, front_compare_op);
- }
+ const auto stencil_test_enabled =
+ regs.depth_control.stencil_enable && regs.depth_buffer.StencilValid();
+ dynamic_state.SetStencilTestEnabled(stencil_test_enabled);
+ if (stencil_test_enabled) {
+ const StencilOps front_ops{
+ .fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front),
+ .pass_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front),
+ .depth_fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front),
+ .compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func),
+ };
+ const StencilOps back_ops = regs.depth_control.backface_enable ? StencilOps{
+ .fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back),
+ .pass_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back),
+ .depth_fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back),
+ .compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func),
+ } : front_ops;
+ dynamic_state.SetStencilOps(front_ops, back_ops);
const auto front = regs.stencil_ref_front;
- const auto back = regs.stencil_ref_back;
- if (front.stencil_test_val == back.stencil_test_val) {
- cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack,
- front.stencil_test_val);
- } else {
- cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront, front.stencil_test_val);
- cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, back.stencil_test_val);
- }
-
- if (front.stencil_write_mask == back.stencil_write_mask) {
- cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack,
- front.stencil_write_mask);
- } else {
- cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront, front.stencil_write_mask);
- cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, back.stencil_write_mask);
- }
-
- if (front.stencil_mask == back.stencil_mask) {
- cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack,
- front.stencil_mask);
- } else {
- cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront, front.stencil_mask);
- cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack, back.stencil_mask);
- }
+ const auto back =
+ regs.depth_control.backface_enable ? regs.stencil_ref_back : regs.stencil_ref_front;
+ dynamic_state.SetStencilReferences(front.stencil_test_val, back.stencil_test_val);
+ dynamic_state.SetStencilWriteMasks(front.stencil_write_mask, back.stencil_write_mask);
+ dynamic_state.SetStencilCompareMasks(front.stencil_mask, back.stencil_mask);
}
}
+void Rasterizer::UpdatePrimitiveState() const {
+ const auto& regs = liverpool->regs;
+ auto& dynamic_state = scheduler.GetDynamicState();
+
+ const auto prim_restart = (regs.enable_primitive_restart & 1) != 0;
+ ASSERT_MSG(!prim_restart || regs.primitive_restart_index == 0xFFFF ||
+ regs.primitive_restart_index == 0xFFFFFFFF,
+ "Primitive restart index other than -1 is not supported yet");
+
+ const auto cull_mode = LiverpoolToVK::IsPrimitiveCulled(regs.primitive_type)
+ ? LiverpoolToVK::CullMode(regs.polygon_control.CullingMode())
+ : vk::CullModeFlagBits::eNone;
+ const auto front_face = LiverpoolToVK::FrontFace(regs.polygon_control.front_face);
+
+ dynamic_state.SetPrimitiveRestartEnabled(prim_restart);
+ dynamic_state.SetCullMode(cull_mode);
+ dynamic_state.SetFrontFace(front_face);
+}
+
void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest) {
if ((from_guest && !Config::getVkGuestMarkersEnabled()) ||
(!from_guest && !Config::getVkHostMarkersEnabled())) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 8e5d0065b..54bf3d253 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -75,9 +75,10 @@ private:
void DepthStencilCopy(bool is_depth, bool is_stencil);
void EliminateFastClear();
- void UpdateDynamicState(const GraphicsPipeline& pipeline);
- void UpdateViewportScissorState();
- void UpdateDepthStencilState();
+ void UpdateDynamicState(const GraphicsPipeline& pipeline) const;
+ void UpdateViewportScissorState() const;
+ void UpdateDepthStencilState() const;
+ void UpdatePrimitiveState() const;
bool FilterDraw();
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index fd84c54ed..a48d93dee 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -97,6 +97,9 @@ void Scheduler::AllocateWorkerCommandBuffers() {
ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}",
vk::to_string(begin_result));
+ // Invalidate dynamic state so it gets applied to the new command buffer.
+ dynamic_state.Invalidate();
+
#if TRACY_GPU_ENABLED
auto* profiler_ctx = instance.GetProfilerContext();
if (profiler_ctx) {
@@ -164,4 +167,151 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
}
}
+void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf) {
+ if (dirty_state.viewports) {
+ dirty_state.viewports = false;
+ cmdbuf.setViewportWithCountEXT(viewports);
+ }
+ if (dirty_state.scissors) {
+ dirty_state.scissors = false;
+ cmdbuf.setScissorWithCountEXT(scissors);
+ }
+ if (dirty_state.depth_test_enabled) {
+ dirty_state.depth_test_enabled = false;
+ cmdbuf.setDepthTestEnableEXT(depth_test_enabled);
+ }
+ if (dirty_state.depth_write_enabled) {
+ dirty_state.depth_write_enabled = false;
+ // Note that this must be set in a command buffer even if depth test is disabled.
+ cmdbuf.setDepthWriteEnableEXT(depth_write_enabled);
+ }
+ if (depth_test_enabled && dirty_state.depth_compare_op) {
+ dirty_state.depth_compare_op = false;
+ cmdbuf.setDepthCompareOpEXT(depth_compare_op);
+ }
+ if (dirty_state.depth_bounds_test_enabled) {
+ dirty_state.depth_bounds_test_enabled = false;
+ if (instance.IsDepthBoundsSupported()) {
+ cmdbuf.setDepthBoundsTestEnableEXT(depth_bounds_test_enabled);
+ }
+ }
+ if (depth_bounds_test_enabled && dirty_state.depth_bounds) {
+ dirty_state.depth_bounds = false;
+ if (instance.IsDepthBoundsSupported()) {
+ cmdbuf.setDepthBounds(depth_bounds_min, depth_bounds_max);
+ }
+ }
+ if (dirty_state.depth_bias_enabled) {
+ dirty_state.depth_bias_enabled = false;
+ cmdbuf.setDepthBiasEnableEXT(depth_bias_enabled);
+ }
+ if (depth_bias_enabled && dirty_state.depth_bias) {
+ dirty_state.depth_bias = false;
+ cmdbuf.setDepthBias(depth_bias_constant, depth_bias_clamp, depth_bias_slope);
+ }
+ if (dirty_state.stencil_test_enabled) {
+ dirty_state.stencil_test_enabled = false;
+ cmdbuf.setStencilTestEnableEXT(stencil_test_enabled);
+ }
+ if (stencil_test_enabled) {
+ if (dirty_state.stencil_front_ops && dirty_state.stencil_back_ops &&
+ stencil_front_ops == stencil_back_ops) {
+ dirty_state.stencil_front_ops = false;
+ dirty_state.stencil_back_ops = false;
+ cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack,
+ stencil_front_ops.fail_op, stencil_front_ops.pass_op,
+ stencil_front_ops.depth_fail_op, stencil_front_ops.compare_op);
+ } else {
+ if (dirty_state.stencil_front_ops) {
+ dirty_state.stencil_front_ops = false;
+ cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, stencil_front_ops.fail_op,
+ stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op,
+ stencil_front_ops.compare_op);
+ }
+ if (dirty_state.stencil_back_ops) {
+ dirty_state.stencil_back_ops = false;
+ cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, stencil_back_ops.fail_op,
+ stencil_back_ops.pass_op, stencil_back_ops.depth_fail_op,
+ stencil_back_ops.compare_op);
+ }
+ }
+ if (dirty_state.stencil_front_reference && dirty_state.stencil_back_reference &&
+ stencil_front_reference == stencil_back_reference) {
+ dirty_state.stencil_front_reference = false;
+ dirty_state.stencil_back_reference = false;
+ cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack,
+ stencil_front_reference);
+ } else {
+ if (dirty_state.stencil_front_reference) {
+ dirty_state.stencil_front_reference = false;
+ cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront,
+ stencil_front_reference);
+ }
+ if (dirty_state.stencil_back_reference) {
+ dirty_state.stencil_back_reference = false;
+ cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, stencil_back_reference);
+ }
+ }
+ if (dirty_state.stencil_front_write_mask && dirty_state.stencil_back_write_mask &&
+ stencil_front_write_mask == stencil_back_write_mask) {
+ dirty_state.stencil_front_write_mask = false;
+ dirty_state.stencil_back_write_mask = false;
+ cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack,
+ stencil_front_write_mask);
+ } else {
+ if (dirty_state.stencil_front_write_mask) {
+ dirty_state.stencil_front_write_mask = false;
+ cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront,
+ stencil_front_write_mask);
+ }
+ if (dirty_state.stencil_back_write_mask) {
+ dirty_state.stencil_back_write_mask = false;
+ cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, stencil_back_write_mask);
+ }
+ }
+ if (dirty_state.stencil_front_compare_mask && dirty_state.stencil_back_compare_mask &&
+ stencil_front_compare_mask == stencil_back_compare_mask) {
+ dirty_state.stencil_front_compare_mask = false;
+ dirty_state.stencil_back_compare_mask = false;
+ cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack,
+ stencil_front_compare_mask);
+ } else {
+ if (dirty_state.stencil_front_compare_mask) {
+ dirty_state.stencil_front_compare_mask = false;
+ cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront,
+ stencil_front_compare_mask);
+ }
+ if (dirty_state.stencil_back_compare_mask) {
+ dirty_state.stencil_back_compare_mask = false;
+ cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack,
+ stencil_back_compare_mask);
+ }
+ }
+ }
+ if (dirty_state.primitive_restart_enable) {
+ dirty_state.primitive_restart_enable = false;
+ if (instance.IsPrimitiveRestartDisableSupported()) {
+ cmdbuf.setPrimitiveRestartEnableEXT(primitive_restart_enable);
+ }
+ }
+ if (dirty_state.cull_mode) {
+ dirty_state.cull_mode = false;
+ cmdbuf.setCullModeEXT(cull_mode);
+ }
+ if (dirty_state.front_face) {
+ dirty_state.front_face = false;
+ cmdbuf.setFrontFaceEXT(front_face);
+ }
+ if (dirty_state.blend_constants) {
+ dirty_state.blend_constants = false;
+ cmdbuf.setBlendConstants(blend_constants);
+ }
+ if (dirty_state.color_write_masks) {
+ dirty_state.color_write_masks = false;
+ if (instance.IsDynamicColorWriteMaskSupported()) {
+ cmdbuf.setColorWriteMaskEXT(0, color_write_masks);
+ }
+ }
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index fd5e68373..7709e1d41 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -7,6 +7,7 @@
#include
#include "common/types.h"
#include "common/unique_function.h"
+#include "video_core/amdgpu/liverpool.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
@@ -55,6 +56,248 @@ struct SubmitInfo {
}
};
+using Viewports = boost::container::static_vector;
+using Scissors = boost::container::static_vector;
+using ColorWriteMasks = std::array;
+struct StencilOps {
+ vk::StencilOp fail_op{};
+ vk::StencilOp pass_op{};
+ vk::StencilOp depth_fail_op{};
+ vk::CompareOp compare_op{};
+
+ bool operator==(const StencilOps& other) const {
+ return fail_op == other.fail_op && pass_op == other.pass_op &&
+ depth_fail_op == other.depth_fail_op && compare_op == other.compare_op;
+ }
+};
+struct DynamicState {
+ struct {
+ bool viewports : 1;
+ bool scissors : 1;
+
+ bool depth_test_enabled : 1;
+ bool depth_write_enabled : 1;
+ bool depth_compare_op : 1;
+
+ bool depth_bounds_test_enabled : 1;
+ bool depth_bounds : 1;
+
+ bool depth_bias_enabled : 1;
+ bool depth_bias : 1;
+
+ bool stencil_test_enabled : 1;
+ bool stencil_front_ops : 1;
+ bool stencil_front_reference : 1;
+ bool stencil_front_write_mask : 1;
+ bool stencil_front_compare_mask : 1;
+ bool stencil_back_ops : 1;
+ bool stencil_back_reference : 1;
+ bool stencil_back_write_mask : 1;
+ bool stencil_back_compare_mask : 1;
+
+ bool primitive_restart_enable : 1;
+ bool cull_mode : 1;
+ bool front_face : 1;
+
+ bool blend_constants : 1;
+ bool color_write_masks : 1;
+ } dirty_state{};
+
+ Viewports viewports{};
+ Scissors scissors{};
+
+ bool depth_test_enabled{};
+ bool depth_write_enabled{};
+ vk::CompareOp depth_compare_op{};
+
+ bool depth_bounds_test_enabled{};
+ float depth_bounds_min{};
+ float depth_bounds_max{};
+
+ bool depth_bias_enabled{};
+ float depth_bias_constant{};
+ float depth_bias_clamp{};
+ float depth_bias_slope{};
+
+ bool stencil_test_enabled{};
+ StencilOps stencil_front_ops{};
+ u32 stencil_front_reference{};
+ u32 stencil_front_write_mask{};
+ u32 stencil_front_compare_mask{};
+ StencilOps stencil_back_ops{};
+ u32 stencil_back_reference{};
+ u32 stencil_back_write_mask{};
+ u32 stencil_back_compare_mask{};
+
+ bool primitive_restart_enable{};
+ vk::CullModeFlags cull_mode{};
+ vk::FrontFace front_face{};
+
+ float blend_constants[4]{};
+ ColorWriteMasks color_write_masks{};
+
+ /// Commits the dynamic state to the provided command buffer.
+ void Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf);
+
+ /// Invalidates all dynamic state to be flushed into the next command buffer.
+ void Invalidate() {
+ std::memset(&dirty_state, 0xFF, sizeof(dirty_state));
+ }
+
+ void SetViewports(const Viewports& viewports_) {
+ if (!std::ranges::equal(viewports, viewports_)) {
+ viewports = viewports_;
+ dirty_state.viewports = true;
+ }
+ }
+
+ void SetScissors(const Scissors& scissors_) {
+ if (!std::ranges::equal(scissors, scissors_)) {
+ scissors = scissors_;
+ dirty_state.scissors = true;
+ }
+ }
+
+ void SetDepthTestEnabled(const bool enabled) {
+ if (depth_test_enabled != enabled) {
+ depth_test_enabled = enabled;
+ dirty_state.depth_test_enabled = true;
+ }
+ }
+
+ void SetDepthWriteEnabled(const bool enabled) {
+ if (depth_write_enabled != enabled) {
+ depth_write_enabled = enabled;
+ dirty_state.depth_write_enabled = true;
+ }
+ }
+
+ void SetDepthCompareOp(const vk::CompareOp compare_op) {
+ if (depth_compare_op != compare_op) {
+ depth_compare_op = compare_op;
+ dirty_state.depth_compare_op = true;
+ }
+ }
+
+ void SetDepthBoundsTestEnabled(const bool enabled) {
+ if (depth_bounds_test_enabled != enabled) {
+ depth_bounds_test_enabled = enabled;
+ dirty_state.depth_bounds_test_enabled = true;
+ }
+ }
+
+ void SetDepthBounds(const float min, const float max) {
+ if (depth_bounds_min != min || depth_bounds_max != max) {
+ depth_bounds_min = min;
+ depth_bounds_max = max;
+ dirty_state.depth_bounds = true;
+ }
+ }
+
+ void SetDepthBiasEnabled(const bool enabled) {
+ if (depth_bias_enabled != enabled) {
+ depth_bias_enabled = enabled;
+ dirty_state.depth_bias_enabled = true;
+ }
+ }
+
+ void SetDepthBias(const float constant, const float clamp, const float slope) {
+ if (depth_bias_constant != constant || depth_bias_clamp != clamp ||
+ depth_bias_slope != slope) {
+ depth_bias_constant = constant;
+ depth_bias_clamp = clamp;
+ depth_bias_slope = slope;
+ dirty_state.depth_bias = true;
+ }
+ }
+
+ void SetStencilTestEnabled(const bool enabled) {
+ if (stencil_test_enabled != enabled) {
+ stencil_test_enabled = enabled;
+ dirty_state.stencil_test_enabled = true;
+ }
+ }
+
+ void SetStencilOps(const StencilOps& front_ops, const StencilOps& back_ops) {
+ if (stencil_front_ops != front_ops) {
+ stencil_front_ops = front_ops;
+ dirty_state.stencil_front_ops = true;
+ }
+ if (stencil_back_ops != back_ops) {
+ stencil_back_ops = back_ops;
+ dirty_state.stencil_back_ops = true;
+ }
+ }
+
+ void SetStencilReferences(const u32 front_reference, const u32 back_reference) {
+ if (stencil_front_reference != front_reference) {
+ stencil_front_reference = front_reference;
+ dirty_state.stencil_front_reference = true;
+ }
+ if (stencil_back_reference != back_reference) {
+ stencil_back_reference = back_reference;
+ dirty_state.stencil_back_reference = true;
+ }
+ }
+
+ void SetStencilWriteMasks(const u32 front_write_mask, const u32 back_write_mask) {
+ if (stencil_front_write_mask != front_write_mask) {
+ stencil_front_write_mask = front_write_mask;
+ dirty_state.stencil_front_write_mask = true;
+ }
+ if (stencil_back_write_mask != back_write_mask) {
+ stencil_back_write_mask = back_write_mask;
+ dirty_state.stencil_back_write_mask = true;
+ }
+ }
+
+ void SetStencilCompareMasks(const u32 front_compare_mask, const u32 back_compare_mask) {
+ if (stencil_front_compare_mask != front_compare_mask) {
+ stencil_front_compare_mask = front_compare_mask;
+ dirty_state.stencil_front_compare_mask = true;
+ }
+ if (stencil_back_compare_mask != back_compare_mask) {
+ stencil_back_compare_mask = back_compare_mask;
+ dirty_state.stencil_back_compare_mask = true;
+ }
+ }
+
+ void SetPrimitiveRestartEnabled(const bool enabled) {
+ if (primitive_restart_enable != enabled) {
+ primitive_restart_enable = enabled;
+ dirty_state.primitive_restart_enable = true;
+ }
+ }
+
+ void SetCullMode(const vk::CullModeFlags cull_mode_) {
+ if (cull_mode != cull_mode_) {
+ cull_mode = cull_mode_;
+ dirty_state.cull_mode = true;
+ }
+ }
+
+ void SetFrontFace(const vk::FrontFace front_face_) {
+ if (front_face != front_face_) {
+ front_face = front_face_;
+ dirty_state.front_face = true;
+ }
+ }
+
+ void SetBlendConstants(const float blend_constants_[4]) {
+ if (!std::equal(blend_constants, std::end(blend_constants), blend_constants_)) {
+ std::memcpy(blend_constants, blend_constants_, sizeof(blend_constants));
+ dirty_state.blend_constants = true;
+ }
+ }
+
+ void SetColorWriteMasks(const ColorWriteMasks& color_write_masks_) {
+ if (!std::ranges::equal(color_write_masks, color_write_masks_)) {
+ color_write_masks = color_write_masks_;
+ dirty_state.color_write_masks = true;
+ }
+ }
+};
+
class Scheduler {
public:
explicit Scheduler(const Instance& instance);
@@ -81,6 +324,10 @@ public:
return render_state;
}
+ DynamicState& GetDynamicState() {
+ return dynamic_state;
+ }
+
/// Returns the current command buffer.
vk::CommandBuffer CommandBuffer() const {
return current_cmdbuf;
@@ -125,6 +372,7 @@ private:
};
std::queue pending_ops;
RenderState render_state;
+ DynamicState dynamic_state;
bool is_rendering = false;
tracy::VkCtxScope* profiler_scope{};
};