Merge branch 'shadps4-emu:main' into multikey
|
@ -9,7 +9,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|||
|
||||
if(APPLE)
|
||||
list(APPEND ADDITIONAL_LANGUAGES OBJC)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 14)
|
||||
# Starting with 15.4, Rosetta 2 has support for all the necessary instruction sets.
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 15.4)
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
|
@ -53,8 +54,9 @@ else()
|
|||
endif()
|
||||
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
# Set x86_64 target level to Sandy Bridge to generally match what is supported for PS4 guest code with CPU patches.
|
||||
add_compile_options(-march=sandybridge)
|
||||
# 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")
|
||||
|
@ -201,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}")
|
||||
|
@ -217,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)
|
||||
|
@ -670,7 +674,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
|
||||
|
@ -1192,8 +1195,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)
|
||||
|
|
23
README.md
|
@ -71,7 +71,7 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad
|
|||
Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-macos.md).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> macOS users need at least macOS 15 on Apple Silicon-based Mac devices and at least macOS 14 on Intel-based Mac devices.
|
||||
> macOS users need at least macOS 15.4 to run shadPS4. Due to GPU issues there are currently heavy bugs on Intel Macs.
|
||||
|
||||
# Debugging and reporting issues
|
||||
|
||||
|
@ -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.
|
||||
|
||||
<div align="center">
|
||||
|
||||
| Modules | Modules | Modules | Modules |
|
||||
|-------------------------|-------------------------|-------------------------|-------------------------|
|
||||
| libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx |
|
||||
| libSceJson.sprx | libSceJson2.sprx | libSceLibcInternal.sprx | libSceNgs2.sprx |
|
||||
| libSceRtc.sprx | libSceUlt.sprx | | |
|
||||
|
||||
</div>
|
||||
|
||||
> [!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)
|
||||
|
|
|
@ -62,6 +62,12 @@ path = [
|
|||
"src/images/update_icon.png",
|
||||
"src/images/youtube.png",
|
||||
"src/images/website.png",
|
||||
"src/images/discord.svg",
|
||||
"src/images/github.svg",
|
||||
"src/images/ko-fi.svg",
|
||||
"src/images/shadps4.svg",
|
||||
"src/images/website.svg",
|
||||
"src/images/youtube.svg",
|
||||
"src/shadps4.qrc",
|
||||
"src/shadps4.rc",
|
||||
"src/qt_gui/translations/update_translation.sh",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
2
externals/MoltenVK/MoltenVK
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 2048427e50f9eb20f2b8f98d316ecaee398c9b91
|
||||
Subproject commit 83510e0f3835c3c43651dda087305abc42572e17
|
2
externals/MoltenVK/SPIRV-Cross
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 2c32b6bf86f3c4a5539aa1f0bacbd59fe61759cf
|
||||
Subproject commit 68300dc07ac3dc592dbbdb87e02d5180f984ad12
|
2
externals/MoltenVK/cereal
vendored
|
@ -1 +1 @@
|
|||
Subproject commit d1fcec807b372f04e4c1041b3058e11c12853e6e
|
||||
Subproject commit a56bad8bbb770ee266e930c95d37fff2a5be7fea
|
2
externals/date
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f
|
||||
Subproject commit a45ea7c17b4a7f320e199b71436074bd624c9e15
|
2
externals/ffmpeg-core
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 27de97c826b6b40c255891c37ac046a25836a575
|
||||
Subproject commit 42557a704720d1b7d85c03bff0c2d369a61848da
|
2
externals/fmt
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 8ee89546ffcf046309d1f0d38c0393f02fde56c8
|
||||
Subproject commit 64db979e38ec644b1798e41610b28c8d2c8a2739
|
2
externals/glslang
vendored
|
@ -1 +1 @@
|
|||
Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb
|
||||
Subproject commit ba1640446f3826a518721d1f083f3a8cca1120c3
|
2
externals/libpng
vendored
|
@ -1 +1 @@
|
|||
Subproject commit c1cc0f3f4c3d4abd11ca68c59446a29ff6f95003
|
||||
Subproject commit 34005e3d3d373c0c36898cc55eae48a79c8238a1
|
2
externals/libusb
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 8f0b4a38fc3eefa2b26a99dff89e1c12bf37afd4
|
||||
Subproject commit a63a7e43e0950a595cf4b98a0eaf4051749ace5f
|
2
externals/magic_enum
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 1a1824df7ac798177a521eed952720681b0bf482
|
||||
Subproject commit a413fcc9c46a020a746907136a384c227f3cd095
|
2
externals/pugixml
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e
|
||||
Subproject commit caade5a28aad86b92a4b5337a9dc70c4ba73c5eb
|
2
externals/robin-map
vendored
|
@ -1 +1 @@
|
|||
Subproject commit fe845fd7852ef541c5479ae23b3d36b57f8608ee
|
||||
Subproject commit 4ec1bf19c6a96125ea22062f38c2cf5b958e448e
|
2
externals/sdl3
vendored
|
@ -1 +1 @@
|
|||
Subproject commit a336b62d8b0b97b09214e053203e442e2b6e2be5
|
||||
Subproject commit 4093e4a193971ef1d4928158e0a1832be42e4599
|
2
externals/sirit
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 8b9b12c2089505ac8b10fa56bf56b3ed49d9d7b0
|
||||
Subproject commit 427a42c9ed99b38204d9107bc3dc14e92458acf1
|
2
externals/toml11
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca
|
||||
Subproject commit a01fe3b4c14c6d7b99ee3f07c9e80058c6403097
|
2
externals/vma
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263
|
||||
Subproject commit f378e7b3f18f6e2b06b957f6ba7b1c7207d2a536
|
2
externals/vulkan-headers
vendored
|
@ -1 +1 @@
|
|||
Subproject commit a03d2f6d5753b365d704d58161825890baad0755
|
||||
Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
|
2
externals/winpthreads
vendored
|
@ -1 +1 @@
|
|||
Subproject commit f00c973a6ab2a23573708568b8ef4acc20a9d36b
|
||||
Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f
|
2
externals/xbyak
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f
|
||||
Subproject commit 44a72f369268f7d552650891b296693e91db86bb
|
2
externals/xxhash
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852
|
||||
Subproject commit 953a09abc39096da9e216b6eb0002c681cdc1199
|
2
externals/zlib-ng
vendored
|
@ -1 +1 @@
|
|||
Subproject commit d54e3769be0c522015b784eca2af258b1c026107
|
||||
Subproject commit fd0d263cedab1a136f40d65199987e3eaeecfcbd
|
2
externals/zydis
vendored
|
@ -1 +1 @@
|
|||
Subproject commit bffbb610cfea643b98e87658b9058382f7522807
|
||||
Subproject commit 120e0e705f8e3b507dc49377ac2879979f0d545c
|
|
@ -7,10 +7,10 @@
|
|||
#include <fmt/xchar.h> // for wstring support
|
||||
#include <toml.hpp>
|
||||
|
||||
#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 <typename TC, typename K>
|
||||
|
@ -763,7 +763,7 @@ void load(const std::filesystem::path& path) {
|
|||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||
logType = toml::find_or<std::string>(general, "logType", "sync");
|
||||
userName = toml::find_or<std::string>(general, "userName", "shadPS4");
|
||||
if (Common::isRelease) {
|
||||
if (Common::g_is_release) {
|
||||
updateChannel = toml::find_or<std::string>(general, "updateChannel", "Release");
|
||||
} else {
|
||||
updateChannel = toml::find_or<std::string>(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";
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr char VERSION[] = "0.7.1 WIP";
|
||||
constexpr bool isRelease = false;
|
||||
|
||||
} // namespace Common
|
|
@ -22,10 +22,6 @@
|
|||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#ifdef __APPLE__
|
||||
#include <half.hpp>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
@ -81,538 +77,6 @@ static Xbyak::Address ZydisToXbyakMemoryOperand(const ZydisDecodedOperand& opera
|
|||
return ptr[expression];
|
||||
}
|
||||
|
||||
static u64 ZydisToXbyakImmediateOperand(const ZydisDecodedOperand& operand) {
|
||||
ASSERT_MSG(operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE,
|
||||
"Expected immediate operand, got type: {}", static_cast<u32>(operand.type));
|
||||
return operand.imm.value.u;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Xbyak::Operand> ZydisToXbyakOperand(const ZydisDecodedOperand& operand) {
|
||||
switch (operand.type) {
|
||||
case ZYDIS_OPERAND_TYPE_REGISTER: {
|
||||
return std::make_unique<Xbyak::Reg>(ZydisToXbyakRegisterOperand(operand));
|
||||
}
|
||||
case ZYDIS_OPERAND_TYPE_MEMORY: {
|
||||
return std::make_unique<Xbyak::Address>(ZydisToXbyakMemoryOperand(operand));
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Unsupported operand type: {}", static_cast<u32>(operand.type));
|
||||
}
|
||||
}
|
||||
|
||||
static bool OperandUsesRegister(const Xbyak::Operand* operand, int index) {
|
||||
if (operand->isREG()) {
|
||||
return operand->getIdx() == index;
|
||||
}
|
||||
if (operand->isMEM()) {
|
||||
const Xbyak::RegExp& reg_exp = operand->getAddress().getRegExp();
|
||||
return reg_exp.getBase().getIdx() == index || reg_exp.getIndex().getIdx() == index;
|
||||
}
|
||||
UNREACHABLE_MSG("Unsupported operand kind: {}", static_cast<u32>(operand->getKind()));
|
||||
}
|
||||
|
||||
static bool IsRegisterAllocated(
|
||||
const std::initializer_list<const Xbyak::Operand*>& allocated_registers, const int index) {
|
||||
return std::ranges::find_if(allocated_registers.begin(), allocated_registers.end(),
|
||||
[index](const Xbyak::Operand* operand) {
|
||||
return OperandUsesRegister(operand, index);
|
||||
}) != allocated_registers.end();
|
||||
}
|
||||
|
||||
static Xbyak::Reg AllocateScratchRegister(
|
||||
const std::initializer_list<const Xbyak::Operand*> allocated_registers, const u32 bits) {
|
||||
for (int index = Xbyak::Operand::R8; index <= Xbyak::Operand::R15; index++) {
|
||||
if (!IsRegisterAllocated(allocated_registers, index)) {
|
||||
return Xbyak::Reg32e(index, static_cast<int>(bits));
|
||||
}
|
||||
}
|
||||
UNREACHABLE_MSG("Out of scratch registers!");
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
static pthread_key_t stack_pointer_slot;
|
||||
static pthread_key_t patch_stack_slot;
|
||||
static std::once_flag patch_context_slots_init_flag;
|
||||
static constexpr u32 patch_stack_size = 0x1000;
|
||||
|
||||
static_assert(sizeof(void*) == sizeof(u64),
|
||||
"Cannot fit a register inside a thread local storage slot.");
|
||||
|
||||
static void FreePatchStack(void* patch_stack) {
|
||||
// Subtract back to the bottom of the stack for free.
|
||||
std::free(static_cast<u8*>(patch_stack) - patch_stack_size);
|
||||
}
|
||||
|
||||
static void InitializePatchContextSlots() {
|
||||
ASSERT_MSG(pthread_key_create(&stack_pointer_slot, nullptr) == 0,
|
||||
"Unable to allocate thread-local register for stack pointer.");
|
||||
ASSERT_MSG(pthread_key_create(&patch_stack_slot, FreePatchStack) == 0,
|
||||
"Unable to allocate thread-local register for patch stack.");
|
||||
}
|
||||
|
||||
void InitializeThreadPatchStack() {
|
||||
std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots);
|
||||
|
||||
pthread_setspecific(patch_stack_slot,
|
||||
static_cast<u8*>(std::malloc(patch_stack_size)) + patch_stack_size);
|
||||
}
|
||||
|
||||
/// Saves the stack pointer to thread local storage and loads the patch stack.
|
||||
static void SaveStack(Xbyak::CodeGenerator& c) {
|
||||
std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots);
|
||||
|
||||
// Save original stack pointer and load patch stack.
|
||||
c.putSeg(gs);
|
||||
c.mov(qword[reinterpret_cast<void*>(stack_pointer_slot * sizeof(void*))], rsp);
|
||||
c.putSeg(gs);
|
||||
c.mov(rsp, qword[reinterpret_cast<void*>(patch_stack_slot * sizeof(void*))]);
|
||||
}
|
||||
|
||||
/// Restores the stack pointer from thread local storage.
|
||||
static void RestoreStack(Xbyak::CodeGenerator& c) {
|
||||
std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots);
|
||||
|
||||
// Save patch stack pointer and load original stack.
|
||||
c.putSeg(gs);
|
||||
c.mov(qword[reinterpret_cast<void*>(patch_stack_slot * sizeof(void*))], rsp);
|
||||
c.putSeg(gs);
|
||||
c.mov(rsp, qword[reinterpret_cast<void*>(stack_pointer_slot * sizeof(void*))]);
|
||||
}
|
||||
|
||||
/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation.
|
||||
static void ValidateDst(const Xbyak::Reg& dst) {
|
||||
// No restrictions.
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void InitializeThreadPatchStack() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
// NOTE: Since stack pointer here is subtracted through safe zone and not saved anywhere,
|
||||
// it must not be modified during the instruction. Otherwise, we will not be able to find
|
||||
// and load registers back from where they were saved. Thus, a limitation is placed on
|
||||
// instructions, that they must not use the stack pointer register as a destination.
|
||||
|
||||
/// Saves the stack pointer to thread local storage and loads the patch stack.
|
||||
static void SaveStack(Xbyak::CodeGenerator& c) {
|
||||
c.lea(rsp, ptr[rsp - 128]); // red zone
|
||||
}
|
||||
|
||||
/// Restores the stack pointer from thread local storage.
|
||||
static void RestoreStack(Xbyak::CodeGenerator& c) {
|
||||
c.lea(rsp, ptr[rsp + 128]); // red zone
|
||||
}
|
||||
|
||||
/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation.
|
||||
static void ValidateDst(const Xbyak::Reg& dst) {
|
||||
// Stack pointer is not preserved, so it can't be used as a dst.
|
||||
ASSERT_MSG(dst.getIdx() != rsp.getIdx(), "Stack pointer not supported as destination.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// Switches to the patch stack, saves registers, and restores the original stack.
|
||||
static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list<Xbyak::Reg> regs) {
|
||||
// Uses a more robust solution for saving registers on MacOS to avoid potential stack corruption
|
||||
// if games decide to not follow the ABI and use the red zone.
|
||||
SaveStack(c);
|
||||
for (const auto& reg : regs) {
|
||||
c.push(reg.cvt64());
|
||||
}
|
||||
RestoreStack(c);
|
||||
}
|
||||
|
||||
/// Switches to the patch stack, restores registers, and restores the original stack.
|
||||
static void RestoreRegisters(Xbyak::CodeGenerator& c,
|
||||
const std::initializer_list<Xbyak::Reg> regs) {
|
||||
SaveStack(c);
|
||||
for (const auto& reg : regs) {
|
||||
c.pop(reg.cvt64());
|
||||
}
|
||||
RestoreStack(c);
|
||||
}
|
||||
|
||||
/// Switches to the patch stack and stores all registers.
|
||||
static void SaveContext(Xbyak::CodeGenerator& c, bool save_flags = false) {
|
||||
SaveStack(c);
|
||||
for (int reg = Xbyak::Operand::RAX; reg <= Xbyak::Operand::R15; reg++) {
|
||||
c.push(Xbyak::Reg64(reg));
|
||||
}
|
||||
c.lea(rsp, ptr[rsp - 32 * 16]);
|
||||
for (int reg = 0; reg <= 15; reg++) {
|
||||
c.vmovdqu(ptr[rsp + 32 * reg], Xbyak::Ymm(reg));
|
||||
}
|
||||
if (save_flags) {
|
||||
c.pushfq();
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores all registers and restores the original stack.
|
||||
/// If the destination is a register, it is not restored to preserve the output.
|
||||
static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst,
|
||||
bool restore_flags = false) {
|
||||
if (restore_flags) {
|
||||
c.popfq();
|
||||
}
|
||||
for (int reg = 15; reg >= 0; reg--) {
|
||||
if ((!dst.isXMM() && !dst.isYMM()) || dst.getIdx() != reg) {
|
||||
c.vmovdqu(Xbyak::Ymm(reg), ptr[rsp + 32 * reg]);
|
||||
}
|
||||
}
|
||||
c.lea(rsp, ptr[rsp + 32 * 16]);
|
||||
for (int reg = Xbyak::Operand::R15; reg >= Xbyak::Operand::RAX; reg--) {
|
||||
if (!dst.isREG() || dst.getIdx() != reg) {
|
||||
c.pop(Xbyak::Reg64(reg));
|
||||
} else {
|
||||
c.lea(rsp, ptr[rsp + 8]);
|
||||
}
|
||||
}
|
||||
RestoreStack(c);
|
||||
}
|
||||
|
||||
static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src1 = ZydisToXbyakRegisterOperand(operands[1]);
|
||||
const auto src2 = ZydisToXbyakOperand(operands[2]);
|
||||
ValidateDst(dst);
|
||||
|
||||
// Check if src2 is a memory operand or a register different to dst.
|
||||
// In those cases, we don't need to use a temporary register and are free to modify dst.
|
||||
// In cases where dst and src2 are the same register, a temporary needs to be used to avoid
|
||||
// modifying src2.
|
||||
bool src2_uses_dst = false;
|
||||
if (src2->isMEM()) {
|
||||
const auto base = src2->getAddress().getRegExp().getBase().getIdx();
|
||||
const auto index = src2->getAddress().getRegExp().getIndex().getIdx();
|
||||
src2_uses_dst = base == dst.getIdx() || index == dst.getIdx();
|
||||
} else {
|
||||
ASSERT(src2->isREG());
|
||||
src2_uses_dst = src2->getReg() == dst;
|
||||
}
|
||||
|
||||
if (!src2_uses_dst) {
|
||||
if (dst != src1)
|
||||
c.mov(dst, src1);
|
||||
c.not_(dst);
|
||||
c.and_(dst, *src2);
|
||||
} else {
|
||||
const auto scratch = AllocateScratchRegister({&dst, &src1, src2.get()}, dst.getBit());
|
||||
|
||||
SaveRegisters(c, {scratch});
|
||||
|
||||
c.mov(scratch, src1);
|
||||
c.not_(scratch);
|
||||
c.and_(scratch, *src2);
|
||||
c.mov(dst, scratch);
|
||||
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
const auto start_len = ZydisToXbyakRegisterOperand(operands[2]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast<int>(start_len.getBit()));
|
||||
const auto scratch1 =
|
||||
AllocateScratchRegister({&dst, src.get(), &start_len, &shift}, dst.getBit());
|
||||
const auto scratch2 =
|
||||
AllocateScratchRegister({&dst, src.get(), &start_len, &shift, &scratch1}, dst.getBit());
|
||||
|
||||
if (dst.getIdx() == shift.getIdx()) {
|
||||
SaveRegisters(c, {scratch1, scratch2});
|
||||
} else {
|
||||
SaveRegisters(c, {scratch1, scratch2, shift});
|
||||
}
|
||||
|
||||
c.mov(scratch1, *src);
|
||||
if (shift.getIdx() != start_len.getIdx()) {
|
||||
c.mov(shift, start_len);
|
||||
}
|
||||
|
||||
c.shr(scratch1, shift.cvt8());
|
||||
c.shr(shift, 8);
|
||||
c.mov(scratch2, 1);
|
||||
c.shl(scratch2, shift.cvt8());
|
||||
c.dec(scratch2);
|
||||
|
||||
c.mov(dst, scratch1);
|
||||
c.and_(dst, scratch2);
|
||||
|
||||
if (dst.getIdx() == shift.getIdx()) {
|
||||
RestoreRegisters(c, {scratch2, scratch1});
|
||||
} else {
|
||||
RestoreRegisters(c, {shift, scratch2, scratch1});
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||
|
||||
SaveRegisters(c, {scratch});
|
||||
|
||||
// BLSI sets CF to zero if source is zero, otherwise it sets CF to one.
|
||||
Xbyak::Label clear_carry, end;
|
||||
|
||||
c.mov(scratch, *src);
|
||||
c.neg(scratch); // NEG, like BLSI, clears CF if the source is zero and sets it otherwise
|
||||
c.jnc(clear_carry);
|
||||
|
||||
c.and_(scratch, *src);
|
||||
c.stc(); // setting/clearing carry needs to happen after the AND because that clears CF
|
||||
c.jmp(end);
|
||||
|
||||
c.L(clear_carry);
|
||||
c.and_(scratch, *src);
|
||||
// We don't need to clear carry here since AND does that for us
|
||||
|
||||
c.L(end);
|
||||
c.mov(dst, scratch);
|
||||
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
|
||||
static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||
|
||||
SaveRegisters(c, {scratch});
|
||||
|
||||
Xbyak::Label clear_carry, end;
|
||||
|
||||
// BLSMSK sets CF to zero if source is NOT zero, otherwise it sets CF to one.
|
||||
c.mov(scratch, *src);
|
||||
c.test(scratch, scratch);
|
||||
c.jnz(clear_carry);
|
||||
|
||||
c.dec(scratch);
|
||||
c.xor_(scratch, *src);
|
||||
c.stc();
|
||||
c.jmp(end);
|
||||
|
||||
c.L(clear_carry);
|
||||
c.dec(scratch);
|
||||
c.xor_(scratch, *src);
|
||||
// We don't need to clear carry here since XOR does that for us
|
||||
|
||||
c.L(end);
|
||||
c.mov(dst, scratch);
|
||||
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
|
||||
static void GenerateTZCNT(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
Xbyak::Label src_zero, end;
|
||||
|
||||
c.cmp(*src, 0);
|
||||
c.je(src_zero);
|
||||
|
||||
// If src is not zero, functions like a BSF, but also clears the CF
|
||||
c.bsf(dst, *src);
|
||||
c.clc();
|
||||
c.jmp(end);
|
||||
|
||||
c.L(src_zero);
|
||||
c.mov(dst, operands[0].size);
|
||||
// Since dst is not zero, also set ZF to zero. Testing dst with itself when we know
|
||||
// it isn't zero is a good way to do this.
|
||||
// Use cvt32 to avoid REX/Operand size prefixes.
|
||||
c.test(dst.cvt32(), dst.cvt32());
|
||||
// When source is zero, TZCNT also sets CF.
|
||||
c.stc();
|
||||
|
||||
c.L(end);
|
||||
}
|
||||
|
||||
static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||
|
||||
SaveRegisters(c, {scratch});
|
||||
|
||||
Xbyak::Label clear_carry, end;
|
||||
|
||||
// BLSR sets CF to zero if source is NOT zero, otherwise it sets CF to one.
|
||||
c.mov(scratch, *src);
|
||||
c.test(scratch, scratch);
|
||||
c.jnz(clear_carry);
|
||||
|
||||
c.dec(scratch);
|
||||
c.and_(scratch, *src);
|
||||
c.stc();
|
||||
c.jmp(end);
|
||||
|
||||
c.L(clear_carry);
|
||||
c.dec(scratch);
|
||||
c.and_(scratch, *src);
|
||||
// We don't need to clear carry here since AND does that for us
|
||||
|
||||
c.L(end);
|
||||
c.mov(dst, scratch);
|
||||
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in,
|
||||
const u32 count) {
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
out[i] = half_float::half_cast<float>(in[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateVCVTPH2PS(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
|
||||
const auto float_count = dst.getBit() / 32;
|
||||
const auto byte_count = float_count * 4;
|
||||
|
||||
SaveContext(c, true);
|
||||
|
||||
// Allocate stack space for outputs and load into first parameter.
|
||||
c.sub(rsp, byte_count);
|
||||
c.mov(rdi, rsp);
|
||||
|
||||
if (src->isXMM()) {
|
||||
// Allocate stack space for inputs and load into second parameter.
|
||||
c.sub(rsp, byte_count);
|
||||
c.mov(rsi, rsp);
|
||||
|
||||
// Move input to the allocated space.
|
||||
c.movdqu(ptr[rsp], *reinterpret_cast<Xbyak::Xmm*>(src.get()));
|
||||
} else {
|
||||
c.lea(rsi, src->getAddress());
|
||||
}
|
||||
|
||||
// Load float count into third parameter.
|
||||
c.mov(rdx, float_count);
|
||||
|
||||
c.mov(rax, reinterpret_cast<u64>(PerformVCVTPH2PS));
|
||||
c.call(rax);
|
||||
|
||||
if (src->isXMM()) {
|
||||
// Clean up after inputs space.
|
||||
c.add(rsp, byte_count);
|
||||
}
|
||||
|
||||
// Load outputs into destination register and clean up space.
|
||||
if (dst.isYMM()) {
|
||||
c.vmovdqu(*reinterpret_cast<const Xbyak::Ymm*>(&dst), ptr[rsp]);
|
||||
} else {
|
||||
c.movdqu(*reinterpret_cast<const Xbyak::Xmm*>(&dst), ptr[rsp]);
|
||||
}
|
||||
c.add(rsp, byte_count);
|
||||
|
||||
RestoreContext(c, dst, true);
|
||||
}
|
||||
|
||||
using SingleToHalfFloatConverter = half_float::half (*)(float);
|
||||
static const SingleToHalfFloatConverter SingleToHalfFloatConverters[4] = {
|
||||
half_float::half_cast<half_float::half, std::round_to_nearest, float>,
|
||||
half_float::half_cast<half_float::half, std::round_toward_neg_infinity, float>,
|
||||
half_float::half_cast<half_float::half, std::round_toward_infinity, float>,
|
||||
half_float::half_cast<half_float::half, std::round_toward_zero, float>,
|
||||
};
|
||||
|
||||
static __attribute__((sysv_abi)) void PerformVCVTPS2PH(half_float::half* out, const float* in,
|
||||
const u32 count, const u8 rounding_mode) {
|
||||
const auto conversion_func = SingleToHalfFloatConverters[rounding_mode];
|
||||
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
out[i] = conversion_func(in[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateVCVTPS2PH(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakRegisterOperand(operands[1]);
|
||||
const auto ctrl = ZydisToXbyakImmediateOperand(operands[2]);
|
||||
|
||||
const auto float_count = src.getBit() / 32;
|
||||
const auto byte_count = float_count * 4;
|
||||
|
||||
SaveContext(c, true);
|
||||
|
||||
if (dst->isXMM()) {
|
||||
// Allocate stack space for outputs and load into first parameter.
|
||||
c.sub(rsp, byte_count);
|
||||
c.mov(rdi, rsp);
|
||||
} else {
|
||||
c.lea(rdi, dst->getAddress());
|
||||
}
|
||||
|
||||
// Allocate stack space for inputs and load into second parameter.
|
||||
c.sub(rsp, byte_count);
|
||||
c.mov(rsi, rsp);
|
||||
|
||||
// Move input to the allocated space.
|
||||
if (src.isYMM()) {
|
||||
c.vmovdqu(ptr[rsp], *reinterpret_cast<const Xbyak::Ymm*>(&src));
|
||||
} else {
|
||||
c.movdqu(ptr[rsp], *reinterpret_cast<const Xbyak::Xmm*>(&src));
|
||||
}
|
||||
|
||||
// Load float count into third parameter.
|
||||
c.mov(rdx, float_count);
|
||||
|
||||
// Load rounding mode into fourth parameter.
|
||||
if (ctrl & 4) {
|
||||
// Load from MXCSR.RC.
|
||||
c.stmxcsr(ptr[rsp - 4]);
|
||||
c.mov(rcx, ptr[rsp - 4]);
|
||||
c.shr(rcx, 13);
|
||||
c.and_(rcx, 3);
|
||||
} else {
|
||||
c.mov(rcx, ctrl & 3);
|
||||
}
|
||||
|
||||
c.mov(rax, reinterpret_cast<u64>(PerformVCVTPS2PH));
|
||||
c.call(rax);
|
||||
|
||||
// Clean up after inputs space.
|
||||
c.add(rsp, byte_count);
|
||||
|
||||
if (dst->isXMM()) {
|
||||
// Load outputs into destination register and clean up space.
|
||||
c.movdqu(*reinterpret_cast<Xbyak::Xmm*>(dst.get()), ptr[rsp]);
|
||||
c.add(rsp, byte_count);
|
||||
}
|
||||
|
||||
RestoreContext(c, *dst, true);
|
||||
}
|
||||
|
||||
static bool FilterRosetta2Only(const ZydisDecodedOperand*) {
|
||||
int ret = 0;
|
||||
size_t size = sizeof(ret);
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) != 0) {
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else // __APPLE__
|
||||
|
||||
static bool FilterTcbAccess(const ZydisDecodedOperand* operands) {
|
||||
const auto& dst_op = operands[0];
|
||||
const auto& src_op = operands[1];
|
||||
|
@ -657,18 +121,11 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
|
||||
Cpu cpu;
|
||||
return !cpu.has(Cpu::tSSE4a);
|
||||
}
|
||||
|
||||
static bool FilterNoBMI1(const ZydisDecodedOperand*) {
|
||||
Cpu cpu;
|
||||
return !cpu.has(Cpu::tBMI1);
|
||||
}
|
||||
|
||||
static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||
|
@ -940,30 +397,16 @@ struct PatchInfo {
|
|||
};
|
||||
|
||||
static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
|
||||
// SSE4a
|
||||
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
|
||||
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows needs a trampoline.
|
||||
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, true}},
|
||||
#elif !defined(__APPLE__)
|
||||
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}},
|
||||
#endif
|
||||
|
||||
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
|
||||
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
|
||||
|
||||
// BMI1
|
||||
{ZYDIS_MNEMONIC_ANDN, {FilterNoBMI1, GenerateANDN, true}},
|
||||
{ZYDIS_MNEMONIC_BEXTR, {FilterNoBMI1, GenerateBEXTR, true}},
|
||||
{ZYDIS_MNEMONIC_BLSI, {FilterNoBMI1, GenerateBLSI, true}},
|
||||
{ZYDIS_MNEMONIC_BLSMSK, {FilterNoBMI1, GenerateBLSMSK, true}},
|
||||
{ZYDIS_MNEMONIC_BLSR, {FilterNoBMI1, GenerateBLSR, true}},
|
||||
{ZYDIS_MNEMONIC_TZCNT, {FilterNoBMI1, GenerateTZCNT, true}},
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Patches for instruction sets not supported by Rosetta 2.
|
||||
// F16C
|
||||
{ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}},
|
||||
{ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}},
|
||||
#endif
|
||||
};
|
||||
|
||||
static std::once_flag init_flag;
|
||||
|
@ -1280,18 +723,7 @@ void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_are
|
|||
}
|
||||
|
||||
void PrePatchInstructions(u64 segment_addr, u64 segment_size) {
|
||||
#if defined(__APPLE__)
|
||||
// HACK: For some reason patching in the signal handler at the start of a page does not work
|
||||
// under Rosetta 2. Patch any instructions at the start of a page ahead of time.
|
||||
if (!Patches.empty()) {
|
||||
auto* code_page = reinterpret_cast<u8*>(Common::AlignUp(segment_addr, 0x1000));
|
||||
const auto* end_page = code_page + Common::AlignUp(segment_size, 0x1000);
|
||||
while (code_page < end_page) {
|
||||
TryPatchJit(code_page);
|
||||
code_page += 0x1000;
|
||||
}
|
||||
}
|
||||
#elif !defined(_WIN32)
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
// Linux and others have an FS segment pointing to valid memory, so continue to do full
|
||||
// ahead-of-time patching for now until a better solution is worked out.
|
||||
if (!Patches.empty()) {
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
|
||||
namespace Core {
|
||||
|
||||
/// Initializes a stack for the current thread for use by patch implementations.
|
||||
void InitializeThreadPatchStack();
|
||||
|
||||
/// Cleans up the patch stack for the current thread.
|
||||
void CleanupThreadPatchStack();
|
||||
|
||||
/// Registers a module for patching, providing an area to generate trampoline code.
|
||||
void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_area_ptr,
|
||||
u64 trampoline_area_size);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "core/cpu_patches.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
|
@ -197,12 +196,7 @@ Tcb* GetTcbBase() {
|
|||
thread_local std::once_flag init_tls_flag;
|
||||
|
||||
void EnsureThreadInitialized() {
|
||||
std::call_once(init_tls_flag, [] {
|
||||
#ifdef ARCH_X86_64
|
||||
InitializeThreadPatchStack();
|
||||
#endif
|
||||
SetTcbBase(Libraries::Kernel::g_curthread->tcb);
|
||||
});
|
||||
std::call_once(init_tls_flag, [] { SetTcbBase(Libraries::Kernel::g_curthread->tcb); });
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -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<std::str
|
|||
Common::Log::Initialize(id + ".log");
|
||||
Common::Log::Start();
|
||||
}
|
||||
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::VERSION);
|
||||
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version);
|
||||
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
|
||||
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
|
||||
LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
|
||||
|
@ -197,8 +196,8 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
|
|||
|
||||
std::string game_title = fmt::format("{} - {} <{}>", 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::vector<std::str
|
|||
remote_host = "unknown";
|
||||
}
|
||||
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::VERSION,
|
||||
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::g_version,
|
||||
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
||||
} 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, game_title);
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 43 KiB |
24
src/images/discord.svg
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="270.93332mm"
|
||||
height="270.93332mm"
|
||||
viewBox="0 0 270.93332 270.93332"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"><circle
|
||||
style="fill:#000000;stroke-width:0.333618;fill-opacity:1"
|
||||
id="path1"
|
||||
cx="135.46666"
|
||||
cy="135.46666"
|
||||
r="135.46666" /><path
|
||||
d="m 194.12363,81.01373 c -10.93928,-5.11822 -22.63609,-8.83793 -34.86478,-10.95597 -1.50182,2.71524 -3.2564,6.36731 -4.46607,9.27253 -12.99943,-1.95495 -25.87929,-1.95495 -38.63961,0 -1.20947,-2.90522 -3.00389,-6.55729 -4.51915,-9.27253 -12.241889,2.11804 -23.952119,5.85142 -34.891409,10.98307 -22.06463,33.34254 -28.045983,65.85691 -25.055298,97.90961 14.634418,10.92863 28.816948,17.56744 42.760118,21.91169 3.44265,-4.73808 6.513019,-9.77474 9.158059,-15.08293 -5.037579,-1.91418 -9.862539,-4.27637 -14.421539,-7.01873 1.20947,-0.89604 2.39255,-1.83288 3.53556,-2.79678 27.806639,13.00588 58.019289,13.00588 85.493719,0 1.15643,0.9639 2.33926,1.90074 3.53556,2.79678 -4.57246,2.75579 -9.41061,5.11798 -14.4482,7.03241 2.64504,5.29451 5.70223,10.34483 9.15805,15.08268 13.95665,-4.34425 28.1523,-10.98282 42.78675,-21.92512 3.50912,-37.15724 -5.99459,-69.37299 -25.12176,-97.93671 z m -86.72985,78.22459 c -8.347289,0 -15.192739,-7.79268 -15.192739,-17.28227 0,-9.48958 6.69928,-17.29569 15.192739,-17.29569 8.49367,0 15.33891,7.79244 15.19272,17.29569 0.0124,9.48959 -6.69905,17.28227 -15.19272,17.28227 z m 56.14517,0 c -8.34727,0 -15.19272,-7.79268 -15.19272,-17.28227 0,-9.48958 6.69904,-17.29569 15.19272,-17.29569 8.49344,0 15.3389,7.79244 15.19272,17.29569 0,9.48959 -6.69928,17.28227 -15.19272,17.28227 z"
|
||||
fill="#5865F2"
|
||||
fill-rule="nonzero"
|
||||
id="path1-1"
|
||||
style="fill:#ffffff;stroke-width:0.660131" /></g></svg>
|
After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 42 KiB |
22
src/images/github.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="270.93332mm"
|
||||
height="270.93332mm"
|
||||
viewBox="0 0 270.93332 270.93332"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"><circle
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.638749"
|
||||
id="path6"
|
||||
cx="135.46666"
|
||||
cy="135.46666"
|
||||
r="135.46666" /><path
|
||||
d="m 169.16642,266.41733 0.03,-25.82065 c 0.0275,-14.56818 0.019,-22.82654 -0.0238,-23.70553 -0.45287,-9.28878 -3.18131,-16.59646 -8.00675,-21.44417 -0.51833,-0.52073 -0.93407,-0.95373 -0.92397,-0.96273 0.0101,-0.009 0.44777,-0.0663 0.97255,-0.12764 37.08454,-4.33929 55.61974,-20.97235 59.5452,-53.43395 2.47353,-20.45482 -1.25492,-35.28308 -11.991,-47.687007 -0.441,-0.50949 -0.85644,-0.996495 -0.92294,-1.082105 -0.11924,-0.153612 -0.11632,-0.168083 0.21291,-1.092956 3.48926,-9.801891 3.08146,-21.84476 -1.12293,-33.147331 -0.65397,-1.758042 -0.47565,-1.634201 -2.5709,-1.78077 -7.91812,-0.553905 -20.35359,4.335862 -33.80673,13.293762 l -1.15135,0.766879 -0.52658,-0.150896 c -12.92077,-3.704851 -29.09272,-5.157035 -43.79112,-3.932576 -7.21573,0.601112 -14.83438,1.91638 -21.45193,3.70365 l -1.41438,0.381889 -0.95446,-0.635621 C 86.99336,60.049959 72.837429,54.806634 65.589795,56.343848 l -0.309542,0.06563 -0.326595,0.837675 c -4.332806,11.118399 -4.907871,23.48198 -1.547192,33.270837 0.236072,0.687637 0.444934,1.31371 0.464054,1.39113 0.02704,0.109638 -0.130307,0.328834 -0.711584,0.990637 -9.160582,10.429614 -13.392033,22.887494 -12.958383,38.152714 1.106869,38.96344 19.486075,58.39373 59.852157,63.27469 0.65144,0.0787 1.19093,0.14903 1.19993,0.15503 0.008,0.007 -0.42227,0.45425 -0.95705,0.99477 -3.85869,3.90015 -6.54793,9.80164 -7.46414,16.38039 l -0.0827,0.59221 -0.36174,0.17829 c -1.57996,0.77769 -4.999926,1.92702 -7.319448,2.4598 -12.017875,2.76038 -22.054909,-0.77155 -29.200801,-10.27535 -0.611821,-0.81369 -1.002109,-1.39605 -2.631881,-3.92379 -6.342866,-9.8377 -15.85558,-14.92007 -24.055896,-12.85296 -4.140227,1.04365 -2.520489,4.818 3.538802,8.24704 4.584375,2.59434 11.458806,12.02482 14.328324,19.65565 5.313949,14.13125 20.872615,19.63362 43.89345,15.52308 0.48859,-0.0872 0.93963,-0.15902 1.00303,-0.15916 0.10915,-2.5e-4 0.11524,0.79731 0.11524,15.82539 v 19.35334 a 135.46666,135.46666 0 0 0 33.40882,4.45244 135.46666,135.46666 0 0 0 33.69975,-4.516 z"
|
||||
style="fill:#ffffff;stroke-width:0.777192"
|
||||
id="path5" /></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
22
src/images/ko-fi.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="270.93332mm"
|
||||
height="270.93332mm"
|
||||
viewBox="0 0 270.93332 270.93332"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"><circle
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.638749"
|
||||
id="path6"
|
||||
cx="135.46666"
|
||||
cy="135.46666"
|
||||
r="135.46666" /><path
|
||||
d="M 245.29662,112.91788 C 238.59944,77.525962 203.19888,73.124713 203.19888,73.124713 H 44.658761 c -5.232973,0 -5.882763,6.913782 -5.882763,6.913782 0,0 -0.710438,63.454165 -0.190612,102.424225 1.420874,21.00124 22.404764,23.14987 22.404764,23.14987 0,0 71.62419,-0.19923 103.67183,-0.42453 21.12252,-3.69082 23.24517,-22.23147 23.02856,-32.35089 37.70517,2.07933 64.30326,-24.52739 57.60608,-59.91929 z m -95.83971,30.41882 c -10.79518,12.58861 -34.75078,34.44754 -34.75078,34.44754 0,0 -1.04833,1.031 -2.6858,0.19924 -0.65845,-0.49382 -0.93569,-0.77972 -0.93569,-0.77972 -3.83809,-3.82079 -29.179908,-26.41616 -34.950046,-34.25694 -6.14268,-8.36065 -9.019085,-23.39244 -0.788412,-32.14296 8.239342,-8.75052 26.034928,-9.40895 37.800448,3.5262 0,0 13.55896,-15.439026 30.0463,-8.34332 16.49601,7.1044 15.8722,26.0869 6.26398,37.34992 z m 53.48205,4.14135 c -8.04007,1.00499 -14.57263,0.24252 -14.57263,0.24252 V 98.501153 h 15.33506 c 0,0 17.07647,4.773777 17.07647,22.855257 0,16.57399 -8.53391,23.10655 -17.8389,26.12157 z"
|
||||
id="path1"
|
||||
style="fill:#ffffff;stroke-width:8.66388" /></g></svg>
|
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 401 KiB |
105
src/images/shadps4.svg
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
viewBox="0 0 256 256"
|
||||
xml:space="preserve"
|
||||
id="svg10"
|
||||
width="256"
|
||||
height="256"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs3"><linearGradient
|
||||
id="linearGradient1"><stop
|
||||
stop-color="#0b034f"
|
||||
offset="0"
|
||||
id="stop1" /><stop
|
||||
stop-color="#4461f2"
|
||||
offset="1"
|
||||
id="stop2" /></linearGradient><linearGradient
|
||||
id="linearGradient2"
|
||||
x1="100"
|
||||
x2="100"
|
||||
y1="185.84"
|
||||
y2="14.157"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
xlink:href="#linearGradient1"
|
||||
gradientTransform="matrix(1.4489308,0,0,1.4489308,-16.890188,-16.890188)" /><mask
|
||||
id="mask9"
|
||||
maskUnits="userSpaceOnUse"><path
|
||||
d="m -4.1597,120.43 65.304,22.774 -20.899,59.926 -65.304,-22.774 z"
|
||||
fill="none"
|
||||
stroke="#149ffb"
|
||||
stroke-linecap="square"
|
||||
stroke-width="11"
|
||||
style="paint-order:markers fill stroke"
|
||||
id="path2" /></mask><mask
|
||||
id="mask10"
|
||||
maskUnits="userSpaceOnUse"><path
|
||||
d="M 178.79,52.448 133.386,71.834 139.2995,22.82 Z"
|
||||
fill="none"
|
||||
stroke="#149ffb"
|
||||
stroke-linecap="square"
|
||||
stroke-width="6.75"
|
||||
style="paint-order:markers fill stroke"
|
||||
id="path3" /></mask><linearGradient
|
||||
xlink:href="#linearGradient1"
|
||||
id="linearGradient10"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="100"
|
||||
y1="185.84"
|
||||
x2="100"
|
||||
y2="14.157"
|
||||
gradientTransform="matrix(1.4489308,0,0,1.4489308,-16.890188,-16.890188)" /></defs><path
|
||||
d="M 53.640867,3.6223249 H 202.35913 c 27.70935,0 50.01854,22.3077391 50.01854,50.0185421 V 202.35913 c 0,27.70935 -22.30774,50.01854 -50.01854,50.01854 H 53.640867 c -27.709354,0 -50.0185421,-22.30774 -50.0185421,-50.01854 V 53.640867 c 0,-27.709354 22.3077391,-50.0185421 50.0185421,-50.0185421 z"
|
||||
fill="url(#linearGradient2)"
|
||||
stroke="#ffffff"
|
||||
stroke-linecap="square"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="7.24465"
|
||||
style="fill:url(#linearGradient10);paint-order:markers fill stroke"
|
||||
id="path4" /><path
|
||||
d="M 53.640867,3.6223249 H 202.35913 c 27.70935,0 50.01854,22.3077391 50.01854,50.0185421 V 202.35913 c 0,27.70935 -22.30774,50.01854 -50.01854,50.01854 H 53.640867 c -27.709354,0 -50.0185421,-22.30774 -50.0185421,-50.01854 V 53.640867 c 0,-27.709354 22.3077391,-50.0185421 50.0185421,-50.0185421 z"
|
||||
fill="url(#linearGradient2)"
|
||||
stroke="#ffffff"
|
||||
stroke-linecap="square"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="7.24465"
|
||||
style="fill:url(#linearGradient2);paint-order:markers fill stroke"
|
||||
id="path5" /><path
|
||||
d="m 50.17,16.403 h 99.848 c 18.603,0 33.58,14.977 33.58,33.58 v 99.848 c 0,18.603 -14.977,33.58 -33.58,33.58 H 50.17 c -18.603,0 -33.58,-14.977 -33.58,-33.58 V 49.983 c 0,-18.603 14.977,-33.58 33.58,-33.58 z"
|
||||
fill="#00ffff"
|
||||
mask="url(#mask9)"
|
||||
style="paint-order:markers fill stroke"
|
||||
id="path6"
|
||||
transform="matrix(1.4489308,0,0,1.4489308,-16.890189,-16.890189)" /><path
|
||||
d="m 50.076,16.496 h 99.848 c 18.603,0 33.58,14.977 33.58,33.58 v 99.848 c 0,18.603 -14.977,33.58 -33.58,33.58 H 50.076 c -18.603,0 -33.58,-14.977 -33.58,-33.58 V 50.076 c 0,-18.603 14.977,-33.58 33.58,-33.58 z"
|
||||
fill="#00ffff"
|
||||
mask="url(#mask10)"
|
||||
style="paint-order:markers fill stroke"
|
||||
id="path7"
|
||||
transform="matrix(1.4489308,0,0,1.4489308,-16.890189,-16.890189)" /><g
|
||||
fill="none"
|
||||
id="g10"
|
||||
transform="matrix(1.4489308,0,0,1.4489308,-16.890189,-16.890189)"><path
|
||||
transform="matrix(0.91041,0.0063593,-0.0063593,0.91041,28.548,42.178)"
|
||||
d="m 166.13,88.68 -23.677,14.288 13.949,23.879 -14.288,-23.677 -23.879,13.949 23.677,-14.288 -13.949,-23.879 14.288,23.677 z"
|
||||
stroke="#139ffb"
|
||||
stroke-linecap="square"
|
||||
stroke-width="12.43"
|
||||
style="paint-order:markers fill stroke"
|
||||
id="path8" /><path
|
||||
d="M 117.98,135.88 V 47.598 l -65.093,69.568 h 83.807"
|
||||
stroke="#ffffff"
|
||||
stroke-linecap="square"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="10"
|
||||
style="font-variation-settings:normal;paint-order:markers fill stroke"
|
||||
id="path9" /><path
|
||||
d="M 69.923,56.468 A 20.994,21.009 0 0 1 49.012,77.477 20.994,21.009 0 0 1 27.936,56.634 20.994,21.009 0 0 1 48.68,35.46 20.994,21.009 0 0 1 69.92,56.135"
|
||||
stop-color="#000000"
|
||||
stroke="#139ffb"
|
||||
stroke-linecap="round"
|
||||
stroke-width="9.4488"
|
||||
style="paint-order:markers fill stroke"
|
||||
id="path10" /></g></svg>
|
After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 72 KiB |
22
src/images/website.svg
Normal file
After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 32 KiB |
28
src/images/youtube.svg
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="270.93332mm"
|
||||
height="270.93332mm"
|
||||
viewBox="0 0 270.93332 270.93332"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"><circle
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.638749"
|
||||
id="path6"
|
||||
cx="135.46666"
|
||||
cy="135.46666"
|
||||
r="135.46666" /><g
|
||||
style="fill:#ffffff;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none"
|
||||
transform="matrix(1.8837557,0,0,1.8837557,50.69765,50.69765)"
|
||||
id="g1">
|
||||
<path
|
||||
d="M 88.119,23.338 C 87.084,19.466 84.034,16.416 80.162,15.381 73.144,13.5 45,13.5 45,13.5 c 0,0 -28.144,0 -35.162,1.881 C 5.966,16.416 2.916,19.466 1.881,23.338 0,30.356 0,45 0,45 0,45 0,59.644 1.881,66.662 2.916,70.534 5.966,73.584 9.838,74.619 16.856,76.5 45,76.5 45,76.5 c 0,0 28.144,0 35.162,-1.881 3.872,-1.035 6.922,-4.085 7.957,-7.957 C 90,59.644 90,45 90,45 90,45 90,30.356 88.119,23.338 Z M 36,58.5 v -27 L 59.382,45 Z"
|
||||
style="opacity:1;fill:#ffffff;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none"
|
||||
stroke-linecap="round"
|
||||
id="path1" />
|
||||
</g></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -22,7 +22,6 @@
|
|||
#include "common/elf_info.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/version.h"
|
||||
#include "input/controller.h"
|
||||
#include "input/input_mouse.h"
|
||||
|
||||
|
@ -551,18 +550,18 @@ void ControllerOutput::FinalizeUpdate() {
|
|||
break;
|
||||
case Axis::TriggerLeft:
|
||||
ApplyDeadzone(new_param, lefttrigger_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::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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <common/config.h>
|
||||
#include <common/path_util.h>
|
||||
#include <common/scm_rev.h>
|
||||
#include <common/version.h>
|
||||
#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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <fmt/format.h>
|
||||
|
||||
#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
|
||||
|
||||
|
|
|
@ -1126,7 +1126,7 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Deadzone Offset (def 0.50):</source>
|
||||
<translation>:</translation>
|
||||
<translation> إزاحة المدى الغير فعال (الأصل ٠.٥٠).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Speed Multiplier (def 1.0):</source>
|
||||
|
@ -1704,7 +1704,7 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Update Compatibility Database</source>
|
||||
<translation type="unfinished">Update Compatibility Database</translation>
|
||||
<translation>تحديث قاعدة بيانات التوافق</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Volume</source>
|
||||
|
@ -1792,15 +1792,15 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
||||
<translation type="unfinished">Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
|
||||
<translation>عرض بيانات التوافق:\nيقوم بإظهار معلومات توافق اللعبة في طريقة عرض الطاولة. تشغيل"تحديث التوافق عند التشغيل" للحصول على معلومات محدثة.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</source>
|
||||
<translation type="unfinished">Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
|
||||
<translation>تحديث التوافق عند التشغيل:\nتحديث قاعدة بيانات التوافق تلقائياً عند تشغيل shadps4.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Update Compatibility Database:\nImmediately update the compatibility database.</source>
|
||||
<translation type="unfinished">Update Compatibility Database:\nImmediately update the compatibility database.</translation>
|
||||
<translation>تحديث قاعدة بيانات التوافق:\nقم بتحديث قاعدة بيانات التوافق حالاً.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Never</source>
|
||||
|
@ -1852,7 +1852,7 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</source>
|
||||
<translation type="unfinished">Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</translation>
|
||||
<translation>تشغيل HDR:\n يقوم بتشغيل HDR في الألعاب المدعومة.\nيجب أن تدعم شاشتك أطياف ألوان BT2020 PQ و صيغة تنسيق المبادلة RGB10A2.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Game Folders:\nThe list of folders to check for installed games.</source>
|
||||
|
@ -1884,11 +1884,11 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</source>
|
||||
<translation type="unfinished">Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
|
||||
<translation>تجميع برامج التظليل:\n يجب أن تقوم بتشغيل هذا لتعديل برامج التظليل باستخدام قائمة تصحيح الأخطاء (Ctrl + F10).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</source>
|
||||
<translation type="unfinished">Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
|
||||
<translation>تشخيص الأعطال:\nيقوم بإنشاء ملف بصيغة .yaml يحتوي على معلومات عن حالة Vulkan في وقت حدوث العطل.\nمفيد لتصحيح أخطاء 'فصل الجهاز'. إذا قمت بتشغيل هذا من الأفضل أن تقوم بتشغيل "استضافة علامات تصحيح الأخطاء" و "ضيف علامات تصحيح الأخطاء".\nلا يعمل على وحدة معالجة رسوم إنتل.\nتحتاج لتشغيل التحقق من طبقات Vulkan و مجموعة تطوير البرامج الخاصة بـVulkan من أجل أن يعمل هذا.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</source>
|
||||
|
|
|
@ -1277,11 +1277,11 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Trophy Viewer</source>
|
||||
<translation type="unfinished">Trophy Viewer</translation>
|
||||
<translation>Trophäenansicht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No games found. Please add your games to your library first.</source>
|
||||
<translation type="unfinished">No games found. Please add your games to your library first.</translation>
|
||||
<translation>Keine Spiele gefunden. Bitte fügen Sie zuerst Ihre Spiele zu Ihrer Bibliothek hinzu.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search...</source>
|
||||
|
@ -1409,43 +1409,43 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Play</source>
|
||||
<translation type="unfinished">Play</translation>
|
||||
<translation>Spielen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pause</source>
|
||||
<translation type="unfinished">Pause</translation>
|
||||
<translation>Pause</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop</source>
|
||||
<translation type="unfinished">Stop</translation>
|
||||
<translation>Stopp</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restart</source>
|
||||
<translation type="unfinished">Restart</translation>
|
||||
<translation>Neustarten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Full Screen</source>
|
||||
<translation type="unfinished">Full Screen</translation>
|
||||
<translation>Vollbild</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Controllers</source>
|
||||
<translation type="unfinished">Controllers</translation>
|
||||
<translation>Controller</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Keyboard</source>
|
||||
<translation type="unfinished">Keyboard</translation>
|
||||
<translation>Tastatur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Refresh List</source>
|
||||
<translation type="unfinished">Refresh List</translation>
|
||||
<translation>Liste aktualisieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Resume</source>
|
||||
<translation type="unfinished">Resume</translation>
|
||||
<translation>Fortsetzen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show Labels Under Icons</source>
|
||||
<translation type="unfinished">Show Labels Under Icons</translation>
|
||||
<translation>Etiketten unter Symbolen anzeigen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -2048,7 +2048,11 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions.</source>
|
||||
<translation type="unfinished">Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions.</translation>
|
||||
<translation>Öffnen Sie den Ordner für benutzerdefinierte Trophäenbilder/-sounds:\n
|
||||
Sie können benutzerdefinierte Bilder zu den Trophäen hinzufügen und einen Ton hinzufügen.\n
|
||||
Fügen Sie die Dateien dem Ordner custom_trophy mit folgenden Namen hinzu:\n
|
||||
trophy.wav ODER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\n
|
||||
Hinweis: Der Sound funktioniert nur in Qt-Versionen.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -2059,23 +2063,23 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Select Game:</source>
|
||||
<translation type="unfinished">Select Game:</translation>
|
||||
<translation>Spiel auswählen:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Progress</source>
|
||||
<translation type="unfinished">Progress</translation>
|
||||
<translation>Fortschritt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show Earned Trophies</source>
|
||||
<translation type="unfinished">Show Earned Trophies</translation>
|
||||
<translation>Verdiente Trophäen anzeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show Not Earned Trophies</source>
|
||||
<translation type="unfinished">Show Not Earned Trophies</translation>
|
||||
<translation>Nicht verdiente Trophäen anzeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show Hidden Trophies</source>
|
||||
<translation type="unfinished">Show Hidden Trophies</translation>
|
||||
<translation>Verborgene Trophäen anzeigen</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -1409,43 +1409,43 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Play</source>
|
||||
<translation type="unfinished">Play</translation>
|
||||
<translation>Luaj</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pause</source>
|
||||
<translation type="unfinished">Pause</translation>
|
||||
<translation>Pezullo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop</source>
|
||||
<translation type="unfinished">Stop</translation>
|
||||
<translation>Ndalo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restart</source>
|
||||
<translation type="unfinished">Restart</translation>
|
||||
<translation>Rinis</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Full Screen</source>
|
||||
<translation type="unfinished">Full Screen</translation>
|
||||
<translation>Ekran i Plotë</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Controllers</source>
|
||||
<translation type="unfinished">Controllers</translation>
|
||||
<translation>Dorezat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Keyboard</source>
|
||||
<translation type="unfinished">Keyboard</translation>
|
||||
<translation>Tastiera</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Refresh List</source>
|
||||
<translation type="unfinished">Refresh List</translation>
|
||||
<translation>Rifresko Listën</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Resume</source>
|
||||
<translation type="unfinished">Resume</translation>
|
||||
<translation>Rifillo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show Labels Under Icons</source>
|
||||
<translation type="unfinished">Show Labels Under Icons</translation>
|
||||
<translation>Shfaq Etiketat Poshtë Ikonave</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -75,6 +75,28 @@ Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
|
|||
return ctx.OpFMin(ctx.F64[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitFPMinTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpFMin3AMD(ctx.F32[1], a, b, c);
|
||||
}
|
||||
return ctx.OpFMin(ctx.F32[1], a, ctx.OpFMin(ctx.F32[1], b, c));
|
||||
}
|
||||
|
||||
Id EmitFPMaxTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpFMax3AMD(ctx.F32[1], a, b, c);
|
||||
}
|
||||
return ctx.OpFMax(ctx.F32[1], a, ctx.OpFMax(ctx.F32[1], b, c));
|
||||
}
|
||||
|
||||
Id EmitFPMedTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpFMid3AMD(ctx.F32[1], a, b, c);
|
||||
}
|
||||
const Id mmx{ctx.OpFMin(ctx.F32[1], ctx.OpFMax(ctx.F32[1], a, b), c)};
|
||||
return ctx.OpFMax(ctx.F32[1], ctx.OpFMin(ctx.F32[1], a, b), mmx);
|
||||
}
|
||||
|
||||
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
|
||||
}
|
||||
|
|
|
@ -247,6 +247,9 @@ Id EmitFPMax32(EmitContext& ctx, Id a, Id b, bool is_legacy = false);
|
|||
Id EmitFPMax64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitFPMin32(EmitContext& ctx, Id a, Id b, bool is_legacy = false);
|
||||
Id EmitFPMin64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitFPMinTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitFPMaxTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitFPMedTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
|
@ -372,6 +375,12 @@ Id EmitSMin32(EmitContext& ctx, Id a, Id b);
|
|||
Id EmitUMin32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSMinTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitUMinTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitSMaxTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitUMaxTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitSMedTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitUMedTri32(EmitContext& ctx, Id a, Id b, Id c);
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
|
||||
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
|
||||
Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
|
|
|
@ -256,6 +256,50 @@ Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
|
|||
return ctx.OpUMax(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitSMinTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpSMin3AMD(ctx.U32[1], a, b, c);
|
||||
}
|
||||
return ctx.OpSMin(ctx.U32[1], a, ctx.OpSMin(ctx.U32[1], b, c));
|
||||
}
|
||||
|
||||
Id EmitUMinTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpUMin3AMD(ctx.U32[1], a, b, c);
|
||||
}
|
||||
return ctx.OpUMin(ctx.U32[1], a, ctx.OpUMin(ctx.U32[1], b, c));
|
||||
}
|
||||
|
||||
Id EmitSMaxTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpSMax3AMD(ctx.U32[1], a, b, c);
|
||||
}
|
||||
return ctx.OpSMax(ctx.U32[1], a, ctx.OpSMax(ctx.U32[1], b, c));
|
||||
}
|
||||
|
||||
Id EmitUMaxTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpUMax3AMD(ctx.U32[1], a, b, c);
|
||||
}
|
||||
return ctx.OpUMax(ctx.U32[1], a, ctx.OpUMax(ctx.U32[1], b, c));
|
||||
}
|
||||
|
||||
Id EmitSMedTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpSMid3AMD(ctx.U32[1], a, b, c);
|
||||
}
|
||||
const Id mmx{ctx.OpSMin(ctx.U32[1], ctx.OpSMax(ctx.U32[1], a, b), c)};
|
||||
return ctx.OpSMax(ctx.U32[1], ctx.OpSMin(ctx.U32[1], a, b), mmx);
|
||||
}
|
||||
|
||||
Id EmitUMedTri32(EmitContext& ctx, Id a, Id b, Id c) {
|
||||
if (ctx.profile.supports_trinary_minmax) {
|
||||
return ctx.OpUMid3AMD(ctx.U32[1], a, b, c);
|
||||
}
|
||||
const Id mmx{ctx.OpUMin(ctx.U32[1], ctx.OpUMax(ctx.U32[1], a, b), c)};
|
||||
return ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], a, b), mmx);
|
||||
}
|
||||
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
||||
Id result{};
|
||||
if (ctx.profile.has_broken_spirv_clamp) {
|
||||
|
|
|
@ -238,13 +238,11 @@ public:
|
|||
void V_FMA_F32(const GcnInst& inst);
|
||||
void V_FMA_F64(const GcnInst& inst);
|
||||
void V_MIN3_F32(const GcnInst& inst);
|
||||
void V_MIN3_I32(const GcnInst& inst);
|
||||
void V_MIN3_U32(const GcnInst& inst);
|
||||
void V_MIN3_U32(bool is_signed, const GcnInst& inst);
|
||||
void V_MAX3_F32(const GcnInst& inst);
|
||||
void V_MAX3_U32(bool is_signed, const GcnInst& inst);
|
||||
void V_MED3_F32(const GcnInst& inst);
|
||||
void V_MED3_I32(const GcnInst& inst);
|
||||
void V_MED3_U32(const GcnInst& inst);
|
||||
void V_MED3_U32(bool is_signed, const GcnInst& inst);
|
||||
void V_SAD(const GcnInst& inst);
|
||||
void V_SAD_U32(const GcnInst& inst);
|
||||
void V_CVT_PK_U16_U32(const GcnInst& inst);
|
||||
|
|
|
@ -359,9 +359,9 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
|||
case Opcode::V_MIN3_F32:
|
||||
return V_MIN3_F32(inst);
|
||||
case Opcode::V_MIN3_I32:
|
||||
return V_MIN3_I32(inst);
|
||||
return V_MIN3_U32(true, inst);
|
||||
case Opcode::V_MIN3_U32:
|
||||
return V_MIN3_U32(inst);
|
||||
return V_MIN3_U32(false, inst);
|
||||
case Opcode::V_MAX3_F32:
|
||||
return V_MAX3_F32(inst);
|
||||
case Opcode::V_MAX3_I32:
|
||||
|
@ -371,9 +371,9 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
|||
case Opcode::V_MED3_F32:
|
||||
return V_MED3_F32(inst);
|
||||
case Opcode::V_MED3_I32:
|
||||
return V_MED3_I32(inst);
|
||||
return V_MED3_U32(true, inst);
|
||||
case Opcode::V_MED3_U32:
|
||||
return V_MED3_U32(inst);
|
||||
return V_MED3_U32(false, inst);
|
||||
case Opcode::V_SAD_U32:
|
||||
return V_SAD_U32(inst);
|
||||
case Opcode::V_CVT_PK_U16_U32:
|
||||
|
@ -1166,59 +1166,42 @@ void Translator::V_MIN3_F32(const GcnInst& inst) {
|
|||
const IR::F32 src0{GetSrc<IR::F32>(inst.src[0])};
|
||||
const IR::F32 src1{GetSrc<IR::F32>(inst.src[1])};
|
||||
const IR::F32 src2{GetSrc<IR::F32>(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.FPMin(src0, ir.FPMin(src1, src2)));
|
||||
SetDst(inst.dst[0], ir.FPMinTri(src0, src1, src2));
|
||||
}
|
||||
|
||||
void Translator::V_MIN3_I32(const GcnInst& inst) {
|
||||
void Translator::V_MIN3_U32(bool is_signed, const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2)));
|
||||
}
|
||||
|
||||
void Translator::V_MIN3_U32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.UMin(src0, ir.UMin(src1, src2)));
|
||||
SetDst(inst.dst[0], ir.IMinTri(src0, src1, src2, is_signed));
|
||||
}
|
||||
|
||||
void Translator::V_MAX3_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc<IR::F32>(inst.src[0])};
|
||||
const IR::F32 src1{GetSrc<IR::F32>(inst.src[1])};
|
||||
const IR::F32 src2{GetSrc<IR::F32>(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.FPMax(src0, ir.FPMax(src1, src2)));
|
||||
SetDst(inst.dst[0], ir.FPMaxTri(src0, src1, src2));
|
||||
}
|
||||
|
||||
void Translator::V_MAX3_U32(bool is_signed, const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.IMax(src0, ir.IMax(src1, src2, is_signed), is_signed));
|
||||
SetDst(inst.dst[0], ir.IMaxTri(src0, src1, src2, is_signed));
|
||||
}
|
||||
|
||||
void Translator::V_MED3_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc<IR::F32>(inst.src[0])};
|
||||
const IR::F32 src1{GetSrc<IR::F32>(inst.src[1])};
|
||||
const IR::F32 src2{GetSrc<IR::F32>(inst.src[2])};
|
||||
const IR::F32 mmx = ir.FPMin(ir.FPMax(src0, src1), src2);
|
||||
SetDst(inst.dst[0], ir.FPMax(ir.FPMin(src0, src1), mmx));
|
||||
SetDst(inst.dst[0], ir.FPMedTri(src0, src1, src2));
|
||||
}
|
||||
|
||||
void Translator::V_MED3_I32(const GcnInst& inst) {
|
||||
void Translator::V_MED3_U32(bool is_signed, const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
const IR::U32 mmx = ir.SMin(ir.SMax(src0, src1), src2);
|
||||
SetDst(inst.dst[0], ir.SMax(ir.SMin(src0, src1), mmx));
|
||||
}
|
||||
|
||||
void Translator::V_MED3_U32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
const IR::U32 mmx = ir.UMin(ir.UMax(src0, src1), src2);
|
||||
SetDst(inst.dst[0], ir.UMax(ir.UMin(src0, src1), mmx));
|
||||
SetDst(inst.dst[0], ir.IMedTri(src0, src1, src2, is_signed));
|
||||
}
|
||||
|
||||
void Translator::V_SAD(const GcnInst& inst) {
|
||||
|
|
|
@ -1336,6 +1336,18 @@ F32F64 IREmitter::FPMin(const F32F64& lhs, const F32F64& rhs, bool is_legacy) {
|
|||
}
|
||||
}
|
||||
|
||||
F32F64 IREmitter::FPMinTri(const F32F64& a, const F32F64& b, const F32F64& c) {
|
||||
return Inst<F32>(Opcode::FPMinTri32, a, b, c);
|
||||
}
|
||||
|
||||
F32F64 IREmitter::FPMaxTri(const F32F64& a, const F32F64& b, const F32F64& c) {
|
||||
return Inst<F32>(Opcode::FPMaxTri32, a, b, c);
|
||||
}
|
||||
|
||||
F32F64 IREmitter::FPMedTri(const F32F64& a, const F32F64& b, const F32F64& c) {
|
||||
return Inst<F32>(Opcode::FPMedTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32U64 IREmitter::IAdd(const U32U64& a, const U32U64& b) {
|
||||
if (a.Type() != b.Type()) {
|
||||
UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type());
|
||||
|
@ -1567,6 +1579,42 @@ U32 IREmitter::IMax(const U32& a, const U32& b, bool is_signed) {
|
|||
return is_signed ? SMax(a, b) : UMax(a, b);
|
||||
}
|
||||
|
||||
U32 IREmitter::SMinTri(const U32& a, const U32& b, const U32& c) {
|
||||
return Inst<U32>(Opcode::SMinTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::UMinTri(const U32& a, const U32& b, const U32& c) {
|
||||
return Inst<U32>(Opcode::UMinTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::IMinTri(const U32& a, const U32& b, const U32& c, bool is_signed) {
|
||||
return is_signed ? SMinTri(a, b, c) : UMinTri(a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::SMaxTri(const U32& a, const U32& b, const U32& c) {
|
||||
return Inst<U32>(Opcode::SMaxTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::UMaxTri(const U32& a, const U32& b, const U32& c) {
|
||||
return Inst<U32>(Opcode::UMaxTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::IMaxTri(const U32& a, const U32& b, const U32& c, bool is_signed) {
|
||||
return is_signed ? SMaxTri(a, b, c) : UMaxTri(a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::SMedTri(const U32& a, const U32& b, const U32& c) {
|
||||
return Inst<U32>(Opcode::SMedTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::UMedTri(const U32& a, const U32& b, const U32& c) {
|
||||
return Inst<U32>(Opcode::UMedTri32, a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::IMedTri(const U32& a, const U32& b, const U32& c, bool is_signed) {
|
||||
return is_signed ? SMedTri(a, b, c) : UMedTri(a, b, c);
|
||||
}
|
||||
|
||||
U32 IREmitter::SClamp(const U32& value, const U32& min, const U32& max) {
|
||||
return Inst<U32>(Opcode::SClamp32, value, min, max);
|
||||
}
|
||||
|
|
|
@ -233,6 +233,9 @@ public:
|
|||
[[nodiscard]] U1 FPUnordered(const F32F64& lhs, const F32F64& rhs);
|
||||
[[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, bool is_legacy = false);
|
||||
[[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs, bool is_legacy = false);
|
||||
[[nodiscard]] F32F64 FPMinTri(const F32F64& a, const F32F64& b, const F32F64& c);
|
||||
[[nodiscard]] F32F64 FPMaxTri(const F32F64& a, const F32F64& b, const F32F64& c);
|
||||
[[nodiscard]] F32F64 FPMedTri(const F32F64& a, const F32F64& b, const F32F64& c);
|
||||
|
||||
[[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] Value IAddCary(const U32& a, const U32& b);
|
||||
|
@ -266,6 +269,15 @@ public:
|
|||
[[nodiscard]] U32 SMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SMinTri(const U32& a, const U32& b, const U32& c);
|
||||
[[nodiscard]] U32 UMinTri(const U32& a, const U32& b, const U32& c);
|
||||
[[nodiscard]] U32 IMinTri(const U32& a, const U32& b, const U32& c, bool is_signed);
|
||||
[[nodiscard]] U32 SMaxTri(const U32& a, const U32& b, const U32& c);
|
||||
[[nodiscard]] U32 UMaxTri(const U32& a, const U32& b, const U32& c);
|
||||
[[nodiscard]] U32 IMaxTri(const U32& a, const U32& b, const U32& c, bool is_signed);
|
||||
[[nodiscard]] U32 SMedTri(const U32& a, const U32& b, const U32& c);
|
||||
[[nodiscard]] U32 UMedTri(const U32& a, const U32& b, const U32& c);
|
||||
[[nodiscard]] U32 IMedTri(const U32& a, const U32& b, const U32& c, bool is_signed);
|
||||
[[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max);
|
||||
[[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max);
|
||||
|
||||
|
|
|
@ -241,6 +241,9 @@ OPCODE(FPMax32, F32, F32,
|
|||
OPCODE(FPMax64, F64, F64, F64, )
|
||||
OPCODE(FPMin32, F32, F32, F32, U1, )
|
||||
OPCODE(FPMin64, F64, F64, F64, )
|
||||
OPCODE(FPMinTri32, F32, F32, F32, F32, )
|
||||
OPCODE(FPMaxTri32, F32, F32, F32, F32, )
|
||||
OPCODE(FPMedTri32, F32, F32, F32, F32, )
|
||||
OPCODE(FPMul32, F32, F32, F32, )
|
||||
OPCODE(FPMul64, F64, F64, F64, )
|
||||
OPCODE(FPDiv32, F32, F32, F32, )
|
||||
|
@ -350,6 +353,12 @@ OPCODE(SMin32, U32, U32,
|
|||
OPCODE(UMin32, U32, U32, U32, )
|
||||
OPCODE(SMax32, U32, U32, U32, )
|
||||
OPCODE(UMax32, U32, U32, U32, )
|
||||
OPCODE(SMinTri32, U32, U32, U32, U32, )
|
||||
OPCODE(UMinTri32, U32, U32, U32, U32, )
|
||||
OPCODE(SMaxTri32, U32, U32, U32, U32, )
|
||||
OPCODE(UMaxTri32, U32, U32, U32, U32, )
|
||||
OPCODE(SMedTri32, U32, U32, U32, U32, )
|
||||
OPCODE(UMedTri32, U32, U32, U32, U32, )
|
||||
OPCODE(SClamp32, U32, U32, U32, U32, )
|
||||
OPCODE(UClamp32, U32, U32, U32, U32, )
|
||||
OPCODE(SLessThan32, U1, U32, U32, )
|
||||
|
|
|
@ -26,6 +26,7 @@ struct Profile {
|
|||
bool support_legacy_vertex_attributes{};
|
||||
bool supports_image_load_store_lod{};
|
||||
bool supports_native_cube_calc{};
|
||||
bool supports_trinary_minmax{};
|
||||
bool supports_robust_buffer_access{};
|
||||
bool has_broken_spirv_clamp{};
|
||||
bool lower_left_origin_mode{};
|
||||
|
|
|
@ -1423,6 +1423,10 @@ struct Liverpool {
|
|||
return num_samples;
|
||||
}
|
||||
|
||||
bool IsClipDisabled() const {
|
||||
return clipper_control.clip_disable || primitive_type == PrimitiveType::RectList;
|
||||
}
|
||||
|
||||
void SetDefaults();
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,13 +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,
|
||||
.depthBiasEnable = key.depth_bias_enable,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
|
@ -123,18 +121,24 @@ GraphicsPipeline::GraphicsPipeline(
|
|||
.pNext = instance.IsDepthClipControlSupported() ? &clip_control : nullptr,
|
||||
};
|
||||
|
||||
boost::container::static_vector<vk::DynamicState, 14> dynamic_states = {
|
||||
vk::DynamicState::eViewportWithCountEXT,
|
||||
vk::DynamicState::eScissorWithCountEXT,
|
||||
vk::DynamicState::eBlendConstants,
|
||||
vk::DynamicState::eDepthBounds,
|
||||
vk::DynamicState::eDepthBias,
|
||||
vk::DynamicState::eStencilReference,
|
||||
vk::DynamicState::eStencilCompareMask,
|
||||
vk::DynamicState::eStencilWriteMask,
|
||||
vk::DynamicState::eStencilOpEXT,
|
||||
boost::container::static_vector<vk::DynamicState, 20> 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::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);
|
||||
}
|
||||
if (instance.IsDynamicColorWriteMaskSupported()) {
|
||||
dynamic_states.push_back(vk::DynamicState::eColorWriteMaskEXT);
|
||||
}
|
||||
|
@ -149,14 +153,6 @@ GraphicsPipeline::GraphicsPipeline(
|
|||
.pDynamicStates = dynamic_states.data(),
|
||||
};
|
||||
|
||||
const vk::PipelineDepthStencilStateCreateInfo depth_info = {
|
||||
.depthTestEnable = key.depth_test_enable,
|
||||
.depthWriteEnable = key.depth_write_enable,
|
||||
.depthCompareOp = key.depth_compare_op,
|
||||
.depthBoundsTestEnable = key.depth_bounds_test_enable,
|
||||
.stencilTestEnable = key.stencil_test_enable,
|
||||
};
|
||||
|
||||
boost::container::static_vector<vk::PipelineShaderStageCreateInfo, MaxShaderStages>
|
||||
shader_stages;
|
||||
auto stage = u32(Shader::LogicalStage::Vertex);
|
||||
|
@ -292,7 +288,6 @@ GraphicsPipeline::GraphicsPipeline(
|
|||
.pViewportState = &viewport_info,
|
||||
.pRasterizationState = &raster_state,
|
||||
.pMultisampleState = &multisampling,
|
||||
.pDepthStencilState = &depth_info,
|
||||
.pColorBlendState = &color_blending,
|
||||
.pDynamicState = &dynamic_info,
|
||||
.layout = *pipeline_layout,
|
||||
|
|
|
@ -39,26 +39,10 @@ struct GraphicsPipelineKey {
|
|||
vk::Format depth_format;
|
||||
vk::Format stencil_format;
|
||||
|
||||
struct {
|
||||
bool clip_disable : 1;
|
||||
bool depth_test_enable : 1;
|
||||
bool depth_write_enable : 1;
|
||||
bool depth_bounds_test_enable : 1;
|
||||
bool depth_bias_enable : 1;
|
||||
bool stencil_test_enable : 1;
|
||||
// Must be named to be zero-initialized.
|
||||
u8 _unused : 2;
|
||||
};
|
||||
vk::CompareOp depth_compare_op;
|
||||
|
||||
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<Liverpool::BlendControl, Liverpool::NumColorBuffers> blend_controls;
|
||||
|
@ -94,20 +78,6 @@ public:
|
|||
return key.mrt_mask;
|
||||
}
|
||||
|
||||
auto IsClipDisabled() const {
|
||||
return key.clip_disable;
|
||||
}
|
||||
|
||||
[[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 <typename Attribute, typename Binding>
|
||||
void GetVertexInputs(VertexInputs<Attribute>& attributes, VertexInputs<Binding>& bindings,
|
||||
|
|
|
@ -247,6 +247,7 @@ bool Instance::CreateDevice() {
|
|||
add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
|
||||
add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
|
||||
add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME);
|
||||
const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
|
||||
|
||||
|
@ -276,6 +277,7 @@ bool Instance::CreateDevice() {
|
|||
shader_stencil_export = add_extension(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
|
||||
image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME);
|
||||
amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME);
|
||||
amd_shader_trinary_minmax = add_extension(VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME);
|
||||
const bool calibrated_timestamps =
|
||||
TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false;
|
||||
|
||||
|
@ -379,6 +381,9 @@ bool Instance::CreateDevice() {
|
|||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{
|
||||
.extendedDynamicState = true,
|
||||
},
|
||||
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{
|
||||
.extendedDynamicState2 = true,
|
||||
},
|
||||
vk::PhysicalDeviceMaintenance4FeaturesKHR{
|
||||
.maintenance4 = true,
|
||||
},
|
||||
|
|
|
@ -84,6 +84,11 @@ public:
|
|||
return features.samplerAnisotropy;
|
||||
}
|
||||
|
||||
/// Returns true if depth bounds testing is supported
|
||||
bool IsDepthBoundsSupported() const {
|
||||
return features.depthBounds;
|
||||
}
|
||||
|
||||
/// Returns true when VK_EXT_custom_border_color is supported
|
||||
bool IsCustomBorderColorSupported() const {
|
||||
return custom_border_color;
|
||||
|
@ -145,6 +150,11 @@ public:
|
|||
return amd_gcn_shader;
|
||||
}
|
||||
|
||||
/// Returns true when VK_AMD_shader_trinary_minmax is supported.
|
||||
bool IsAmdShaderTrinaryMinMaxSupported() const {
|
||||
return amd_shader_trinary_minmax;
|
||||
}
|
||||
|
||||
/// Returns true when geometry shaders are supported by the device
|
||||
bool IsGeometryStageSupported() const {
|
||||
return features.geometryShader;
|
||||
|
@ -282,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();
|
||||
|
@ -333,6 +348,7 @@ private:
|
|||
bool shader_stencil_export{};
|
||||
bool image_load_store_lod{};
|
||||
bool amd_gcn_shader{};
|
||||
bool amd_shader_trinary_minmax{};
|
||||
bool portability_subset{};
|
||||
};
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS
|
|||
info.vs_info.emulate_depth_negative_one_to_one =
|
||||
!instance.IsDepthClipControlSupported() &&
|
||||
regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW;
|
||||
info.vs_info.clip_disable = graphics_key.clip_disable;
|
||||
info.vs_info.clip_disable = regs.IsClipDisabled();
|
||||
if (l_stage == LogicalStage::TessellationEval) {
|
||||
info.vs_info.tess_type = regs.tess_config.type;
|
||||
info.vs_info.tess_topology = regs.tess_config.topology;
|
||||
|
@ -201,6 +201,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
|
|||
.support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(),
|
||||
.supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(),
|
||||
.supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(),
|
||||
.supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(),
|
||||
.supports_robust_buffer_access = instance_.IsRobustBufferAccess2Supported(),
|
||||
.needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() &&
|
||||
instance.GetDriverID() == vk::DriverId::eNvidiaProprietary,
|
||||
|
@ -266,16 +267,6 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
auto& regs = liverpool->regs;
|
||||
auto& key = graphics_key;
|
||||
|
||||
key.clip_disable =
|
||||
regs.clipper_control.clip_disable || regs.primitive_type == AmdGpu::PrimitiveType::RectList;
|
||||
key.depth_test_enable = regs.depth_control.depth_enable;
|
||||
key.depth_write_enable =
|
||||
regs.depth_control.depth_write_enable && !regs.depth_render_control.depth_clear_enable;
|
||||
key.depth_bounds_test_enable = regs.depth_control.depth_bounds_enable;
|
||||
key.depth_bias_enable = regs.polygon_control.NeedsBias();
|
||||
key.depth_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.depth_func);
|
||||
key.stencil_test_enable = regs.depth_control.stencil_enable;
|
||||
|
||||
const auto depth_format = instance.GetSupportedFormat(
|
||||
LiverpoolToVK::DepthFormat(regs.depth_buffer.z_info.format,
|
||||
regs.depth_buffer.stencil_info.format),
|
||||
|
@ -284,22 +275,16 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||
key.depth_format = depth_format;
|
||||
} else {
|
||||
key.depth_format = vk::Format::eUndefined;
|
||||
key.depth_test_enable = false;
|
||||
}
|
||||
if (regs.depth_buffer.StencilValid()) {
|
||||
key.stencil_format = depth_format;
|
||||
} else {
|
||||
key.stencil_format = vk::Format::eUndefined;
|
||||
key.stencil_test_enable = false;
|
||||
}
|
||||
|
||||
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 =
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -946,82 +946,20 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) {
|
|||
mapped_ranges -= boost::icl::interval<VAddr>::right_open(addr, addr + size);
|
||||
}
|
||||
|
||||
void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) {
|
||||
UpdateViewportScissorState(pipeline);
|
||||
void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) const {
|
||||
UpdateViewportScissorState();
|
||||
UpdateDepthStencilState();
|
||||
UpdatePrimitiveState();
|
||||
|
||||
auto& regs = liverpool->regs;
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
cmdbuf.setBlendConstants(®s.blend_constants.red);
|
||||
auto& dynamic_state = scheduler.GetDynamicState();
|
||||
dynamic_state.SetBlendConstants(&liverpool->regs.blend_constants.red);
|
||||
dynamic_state.SetColorWriteMasks(pipeline.GetWriteMasks());
|
||||
|
||||
if (instance.IsDynamicColorWriteMaskSupported()) {
|
||||
cmdbuf.setColorWriteMaskEXT(0, pipeline.GetWriteMasks());
|
||||
}
|
||||
if (regs.depth_control.depth_bounds_enable) {
|
||||
cmdbuf.setDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
// Commit new dynamic state to the command buffer.
|
||||
dynamic_state.Commit(instance, scheduler.CommandBuffer());
|
||||
}
|
||||
|
||||
void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) {
|
||||
void Rasterizer::UpdateViewportScissorState() const {
|
||||
const auto& regs = liverpool->regs;
|
||||
|
||||
const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
|
||||
|
@ -1072,7 +1010,7 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) {
|
|||
|
||||
const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f;
|
||||
const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f;
|
||||
if (pipeline.IsClipDisabled()) {
|
||||
if (regs.IsClipDisabled()) {
|
||||
// In case if clipping is disabled we patch the shader to convert vertex position
|
||||
// from screen space coordinates to NDC by defining a render space as full hardware
|
||||
// window range [0..16383, 0..16383] and setting the viewport to its size.
|
||||
|
@ -1134,9 +1072,84 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) {
|
|||
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() const {
|
||||
const auto& regs = liverpool->regs;
|
||||
auto& dynamic_state = scheduler.GetDynamicState();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.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) {
|
||||
|
|
|
@ -75,8 +75,10 @@ private:
|
|||
void DepthStencilCopy(bool is_depth, bool is_stencil);
|
||||
void EliminateFastClear();
|
||||
|
||||
void UpdateDynamicState(const GraphicsPipeline& pipeline);
|
||||
void UpdateViewportScissorState(const GraphicsPipeline& pipeline);
|
||||
void UpdateDynamicState(const GraphicsPipeline& pipeline) const;
|
||||
void UpdateViewportScissorState() const;
|
||||
void UpdateDepthStencilState() const;
|
||||
void UpdatePrimitiveState() const;
|
||||
|
||||
bool FilterDraw();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <boost/container/static_vector.hpp>
|
||||
#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<vk::Viewport, AmdGpu::Liverpool::NumViewports>;
|
||||
using Scissors = boost::container::static_vector<vk::Rect2D, AmdGpu::Liverpool::NumViewports>;
|
||||
using ColorWriteMasks = std::array<vk::ColorComponentFlags, AmdGpu::Liverpool::NumColorBuffers>;
|
||||
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<PendingOp> pending_ops;
|
||||
RenderState render_state;
|
||||
DynamicState dynamic_state;
|
||||
bool is_rendering = false;
|
||||
tracy::VkCtxScope* profiler_scope{};
|
||||
};
|
||||
|
|