Compare commits
94 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f58ee3f15f | ||
|
|
da8e0f6571 | ||
|
|
3fa9702952 | ||
|
|
ae7da0b12d | ||
|
|
214b9fc9a7 | ||
|
|
951c61aeaa | ||
|
|
eb7d361657 | ||
|
|
af89f7683d | ||
|
|
667a8ae163 | ||
|
|
3a89723d97 | ||
|
|
9ee5c4ec56 | ||
|
|
5e58271903 | ||
|
|
8c907c620d | ||
|
|
b84d429c2e | ||
|
|
9f34be5a61 | ||
|
|
14ac0c2923 | ||
|
|
279c7bcc1a | ||
|
|
c7235e67ef | ||
|
|
840d5520d2 | ||
|
|
091463a429 | ||
|
|
f67605e6aa | ||
|
|
9a0648ff0a | ||
|
|
c9e4acc4e2 | ||
|
|
292dfac25e | ||
|
|
37addf7a94 | ||
|
|
640fc1418b | ||
|
|
8a48c4ed1c | ||
|
|
f9e0681d59 | ||
|
|
cbc0f0a66e | ||
|
|
481b210c0d | ||
|
|
4e7a6639d2 | ||
|
|
a0c7d93b84 | ||
|
|
516325eba8 | ||
|
|
e0193e2be5 | ||
|
|
b85f5b1332 | ||
|
|
648c7b4ed6 | ||
|
|
14fc1bec17 | ||
|
|
fa7abafa5f | ||
|
|
36df305b13 | ||
|
|
f1eff447bb | ||
|
|
49e3c073a5 | ||
|
|
6991d447d4 | ||
|
|
b30a1d49ff | ||
|
|
5e24f7ed31 | ||
|
|
cf221ca92d | ||
|
|
59c6f45e7a | ||
|
|
caf38725ae | ||
|
|
ee6d40d414 | ||
|
|
eb4ea7e5c7 | ||
|
|
3074b2eb93 | ||
|
|
39bb6851e4 | ||
|
|
55650c5b75 | ||
|
|
e57b13ad94 | ||
|
|
1a9576fdff | ||
|
|
8e33cf1c2b | ||
|
|
6fb212784e | ||
|
|
2030522d86 | ||
|
|
1490b49fa9 | ||
|
|
212b497d5c | ||
|
|
7feac8ba46 | ||
|
|
4908a07c20 | ||
|
|
6c00151d17 | ||
|
|
04b4f3b051 | ||
|
|
6ebc972c2b | ||
|
|
94652e122d | ||
|
|
e49184e606 | ||
|
|
14d2c77f91 | ||
|
|
6430fc29a9 | ||
|
|
77d06d5df0 | ||
|
|
c73841500a | ||
|
|
11f4bf8a9a | ||
|
|
9b3611eb8d | ||
|
|
e610485cd2 | ||
|
|
c82e6dc810 | ||
|
|
e242f16986 | ||
|
|
333ccf23f8 | ||
|
|
1cdddd17d2 | ||
|
|
7cf74abbf5 | ||
|
|
316f80af87 | ||
|
|
b64d28492a | ||
|
|
1598426493 | ||
|
|
60bdedc7dd | ||
|
|
228a381aed | ||
|
|
c22c4f5d59 | ||
|
|
218d790bd6 | ||
|
|
b8f3e5157b | ||
|
|
35d94dcb2b | ||
|
|
4a13f9eecd | ||
|
|
ad99bbf5fe | ||
|
|
b387a26f30 | ||
|
|
75c4aec8ab | ||
|
|
660c6bec22 | ||
|
|
04fa990b0c | ||
|
|
4dd85f86a8 |
@@ -229,7 +229,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
|
||||
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
@@ -249,7 +249,7 @@ if(ENABLE_QT)
|
||||
# Check for system Qt on Linux, fallback to bundled Qt
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (NOT YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
|
||||
endif()
|
||||
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
||||
# Check for dependencies, then enable bundled Qt download
|
||||
@@ -508,7 +508,7 @@ set(FFmpeg_COMPONENTS
|
||||
avutil
|
||||
swscale)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (UNIX AND NOT APPLE)
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
|
||||
@@ -11,9 +11,15 @@ find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
|
||||
|
||||
# generate git/build information
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
if(NOT GIT_REF_SPEC)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
endif()
|
||||
if(NOT GIT_DESC)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
endif()
|
||||
if (NOT GIT_BRANCH)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
endif()
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
# Generate cpp with Git revision from template
|
||||
|
||||
2
dist/yuzu.desktop
vendored
2
dist/yuzu.desktop
vendored
@@ -8,5 +8,5 @@ Icon=yuzu
|
||||
TryExec=yuzu
|
||||
Exec=yuzu %f
|
||||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Switch;Nintendo;
|
||||
15
dist/yuzu.xml
vendored
15
dist/yuzu.xml
vendored
@@ -15,4 +15,19 @@
|
||||
<glob pattern="*.nso"/>
|
||||
<magic><match value="NSO" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-nx-nsp">
|
||||
<comment>Nintendo Switch Package</comment>
|
||||
<acronym>NSP</acronym>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nsp"/>
|
||||
<magic><match value="PFS" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-nx-xci">
|
||||
<comment>Nintendo Switch Card Image</comment>
|
||||
<acronym>XCI</acronym>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.xci"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
7
externals/CMakeLists.txt
vendored
7
externals/CMakeLists.txt
vendored
@@ -52,11 +52,12 @@ endif()
|
||||
# SDL2
|
||||
if (YUZU_USE_EXTERNAL_SDL2)
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
|
||||
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
||||
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
Atomic Render Power Threads
|
||||
File CPUinfo Filesystem Locale)
|
||||
CPUinfo File Filesystem
|
||||
Locale Power Render)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
option(SDL_${_OPT} "" OFF)
|
||||
|
||||
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 2e9821423a...e2ade2bfc4
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 07c4a37bcf...e005e1f817
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: cce7e4ee5d...28714ee75a
@@ -22,6 +22,11 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
|
||||
-DBUILD_TAG=${BUILD_TAG}
|
||||
-DBUILD_ID=${DISPLAY_VERSION}
|
||||
-DGIT_REF_SPEC=${GIT_REF_SPEC}
|
||||
-DGIT_REV=${GIT_REV}
|
||||
-DGIT_DESC=${GIT_DESC}
|
||||
-DGIT_BRANCH=${GIT_BRANCH}
|
||||
-DBUILD_FULLNAME=${BUILD_FULLNAME}
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
|
||||
DEPENDS
|
||||
|
||||
@@ -597,6 +597,7 @@ struct Values {
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
@@ -187,6 +187,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
@@ -265,8 +267,6 @@ add_library(core STATIC
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
|
||||
@@ -45,26 +45,26 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
if (parameters.allow_pro_controller) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_dual_joycons) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
|
||||
// Assign left joycons to even player indices and right joycons to odd player indices.
|
||||
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
|
||||
// a right Joycon for Player 2 in 2 Player Assist mode.
|
||||
if (index % 2 == 0) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
|
||||
}
|
||||
|
||||
@@ -161,7 +161,10 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
motion.rotation = emulated.GetGyroscope();
|
||||
motion.orientation = emulated.GetOrientation();
|
||||
motion.quaternion = emulated.GetQuaternion();
|
||||
motion.gyro_bias = emulated.GetGyroBias();
|
||||
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
|
||||
// Find what is this value
|
||||
motion.verticalization_error = 0.0f;
|
||||
|
||||
TriggerOnChange(ConsoleTriggerType::Motion);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ struct ConsoleMotion {
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
Common::Quaternion<f32> quaternion{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
f32 verticalization_error{};
|
||||
bool is_at_rest{};
|
||||
};
|
||||
|
||||
|
||||
@@ -843,23 +843,18 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
}
|
||||
|
||||
bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
constexpr Common::Input::VibrationStatus status = {
|
||||
static constexpr VibrationValue test_vibration = {
|
||||
.low_amplitude = 0.001f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.001f,
|
||||
.high_frequency = 320.0f,
|
||||
.type = Common::Input::VibrationAmplificationType::Linear,
|
||||
};
|
||||
return output_devices[device_index]->SetVibration(status) ==
|
||||
Common::Input::VibrationError::None;
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
SetVibration(device_index, test_vibration);
|
||||
|
||||
// Stop any vibration and return the result
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
@@ -891,8 +886,9 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported() const {
|
||||
switch (npad_type) {
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
return supported_style_tag.fullkey;
|
||||
case NpadStyleIndex::Handheld:
|
||||
@@ -920,9 +916,10 @@ bool EmulatedController::IsControllerSupported() const {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect() {
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
|
||||
void EmulatedController::Connect(bool use_temporary_value) {
|
||||
if (!IsControllerSupported(use_temporary_value)) {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
{
|
||||
|
||||
@@ -167,8 +167,11 @@ public:
|
||||
*/
|
||||
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
|
||||
|
||||
/// Sets the connected status to true
|
||||
void Connect();
|
||||
/**
|
||||
* Sets the connected status to true
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
*/
|
||||
void Connect(bool use_temporary_value = false);
|
||||
|
||||
/// Sets the connected status to false
|
||||
void Disconnect();
|
||||
@@ -319,9 +322,10 @@ private:
|
||||
|
||||
/**
|
||||
* Checks the current controller type against the supported_style_tag
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller is supported
|
||||
*/
|
||||
bool IsControllerSupported() const;
|
||||
bool IsControllerSupported(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Updates the button status of the controller
|
||||
|
||||
@@ -496,6 +496,13 @@ struct VibrationValue {
|
||||
};
|
||||
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
|
||||
|
||||
constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
|
||||
.low_amplitude = 0.0f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.0f,
|
||||
.high_frequency = 320.0f,
|
||||
};
|
||||
|
||||
// This is nn::hid::VibrationDeviceInfo
|
||||
struct VibrationDeviceInfo {
|
||||
VibrationDeviceType type{};
|
||||
|
||||
@@ -23,11 +23,11 @@ void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
|
||||
gyro = gyroscope - gyro_drift;
|
||||
gyro = gyroscope - gyro_bias;
|
||||
|
||||
// Auto adjust drift to minimize drift
|
||||
if (!IsMoving(0.1f)) {
|
||||
gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
|
||||
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
|
||||
}
|
||||
|
||||
if (gyro.Length2() < gyro_threshold) {
|
||||
@@ -41,8 +41,8 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
|
||||
quat = quaternion;
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
|
||||
gyro_drift = drift;
|
||||
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
|
||||
gyro_bias = bias;
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroThreshold(f32 threshold) {
|
||||
@@ -192,6 +192,10 @@ Common::Vec3f MotionInput::GetGyroscope() const {
|
||||
return gyro;
|
||||
}
|
||||
|
||||
Common::Vec3f MotionInput::GetGyroBias() const {
|
||||
return gyro_bias;
|
||||
}
|
||||
|
||||
Common::Quaternion<f32> MotionInput::GetQuaternion() const {
|
||||
return quat;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
void SetAcceleration(const Common::Vec3f& acceleration);
|
||||
void SetGyroscope(const Common::Vec3f& gyroscope);
|
||||
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
|
||||
void SetGyroDrift(const Common::Vec3f& drift);
|
||||
void SetGyroBias(const Common::Vec3f& bias);
|
||||
void SetGyroThreshold(f32 threshold);
|
||||
|
||||
void EnableReset(bool reset);
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
|
||||
[[nodiscard]] Common::Vec3f GetAcceleration() const;
|
||||
[[nodiscard]] Common::Vec3f GetGyroscope() const;
|
||||
[[nodiscard]] Common::Vec3f GetGyroBias() const;
|
||||
[[nodiscard]] Common::Vec3f GetRotations() const;
|
||||
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
|
||||
|
||||
@@ -69,7 +70,7 @@ private:
|
||||
Common::Vec3f gyro;
|
||||
|
||||
// Vector to be substracted from gyro measurements
|
||||
Common::Vec3f gyro_drift;
|
||||
Common::Vec3f gyro_bias;
|
||||
|
||||
// Minimum gyro amplitude to detect if the device is moving
|
||||
f32 gyro_threshold = 0.0f;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
const u32 priority = preemption_priorities[core_id];
|
||||
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
|
||||
|
||||
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
|
||||
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
|
||||
// interrupts that may have occurred.
|
||||
kernel.PhysicalCore(core_id).Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
34
src/core/hle/kernel/k_interrupt_manager.cpp
Normal file
34
src/core/hle/kernel/k_interrupt_manager.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel::KInterruptManager {
|
||||
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||
auto* process = kernel.CurrentProcess();
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = kernel.Scheduler(core_id);
|
||||
auto& current_thread = *scheduler.GetCurrentThread();
|
||||
|
||||
// If the user disable count is set, we may need to pin the current thread.
|
||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Pin the current thread.
|
||||
process->PinCurrentThread(core_id);
|
||||
|
||||
// Set the interrupt flag for the thread.
|
||||
scheduler.GetCurrentThread()->SetInterruptFlag();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel::KInterruptManager
|
||||
17
src/core/hle/kernel/k_interrupt_manager.h
Normal file
17
src/core/hle/kernel/k_interrupt_manager.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
namespace KInterruptManager {
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -120,7 +120,7 @@ static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
Mask = static_cast<u8>(~None),
|
||||
All = static_cast<u8>(~None),
|
||||
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
|
||||
@@ -264,9 +264,9 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
|
||||
ASSERT(heap_last < stack_start || stack_last < heap_start);
|
||||
ASSERT(heap_last < kmap_start || kmap_last < heap_start);
|
||||
|
||||
current_heap_addr = heap_region_start;
|
||||
heap_capacity = 0;
|
||||
physical_memory_usage = 0;
|
||||
current_heap_end = heap_region_start;
|
||||
max_heap_size = 0;
|
||||
mapped_physical_memory_size = 0;
|
||||
memory_pool = pool;
|
||||
|
||||
page_table_impl.Resize(address_space_width, PageBits);
|
||||
@@ -306,7 +306,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
|
||||
KMemoryState state{};
|
||||
KMemoryPermission perm{};
|
||||
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
|
||||
KMemoryState::Normal, KMemoryPermission::Mask,
|
||||
KMemoryState::Normal, KMemoryPermission::All,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
@@ -465,7 +465,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
MapPhysicalMemory(page_linked_list, addr, end_addr);
|
||||
|
||||
physical_memory_usage += remaining_size;
|
||||
mapped_physical_memory_size += remaining_size;
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
|
||||
@@ -507,7 +507,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
auto process{system.Kernel().CurrentProcess()};
|
||||
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
|
||||
physical_memory_usage -= mapped_size;
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -554,7 +554,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
if (IsRegionMapped(dst_addr, size)) {
|
||||
@@ -593,7 +593,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::None,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
KMemoryPermission dst_perm{};
|
||||
@@ -784,7 +784,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&state, nullptr, &attribute, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::Mask,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
@@ -806,6 +806,33 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
|
||||
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
|
||||
Svc::MemoryPermission svc_perm) {
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
// Verify we can change the memory permission.
|
||||
KMemoryState old_state;
|
||||
KMemoryPermission old_perm;
|
||||
R_TRY(this->CheckMemoryState(
|
||||
std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
|
||||
KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Determine new perm.
|
||||
const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
|
||||
R_SUCCEED_IF(old_perm == new_perm);
|
||||
|
||||
// Perform mapping operation.
|
||||
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
|
||||
|
||||
// Update the blocks.
|
||||
block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -832,61 +859,125 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
|
||||
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
heap_capacity = new_heap_capacity;
|
||||
|
||||
// Only process page tables are allowed to set heap size.
|
||||
ASSERT(!this->IsKernel());
|
||||
|
||||
max_heap_size = size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
|
||||
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
// Try to perform a reduction in heap, instead of an extension.
|
||||
VAddr cur_address{};
|
||||
std::size_t allocation_size{};
|
||||
{
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
|
||||
if (size > heap_region_end - heap_region_start) {
|
||||
return ResultOutOfMemory;
|
||||
// Validate that setting heap size is possible at all.
|
||||
R_UNLESS(!is_kernel, ResultOutOfMemory);
|
||||
R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start),
|
||||
ResultOutOfMemory);
|
||||
R_UNLESS(size <= max_heap_size, ResultOutOfMemory);
|
||||
|
||||
if (size < GetHeapSize()) {
|
||||
// The size being requested is less than the current size, so we need to free the end of
|
||||
// the heap.
|
||||
|
||||
// Validate memory state.
|
||||
std::size_t num_allocator_blocks;
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
|
||||
heap_region_start + size, GetHeapSize() - size,
|
||||
KMemoryState::All, KMemoryState::Normal,
|
||||
KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Unmap the end of the heap.
|
||||
const auto num_pages = (GetHeapSize() - size) / PageSize;
|
||||
R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap));
|
||||
|
||||
// Release the memory from the resource limit.
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
|
||||
LimitableResource::PhysicalMemory, num_pages * PageSize);
|
||||
|
||||
// Apply the memory block update.
|
||||
block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryAttribute::None);
|
||||
|
||||
// Update the current heap end.
|
||||
current_heap_end = heap_region_start + size;
|
||||
|
||||
// Set the output.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
} else if (size == GetHeapSize()) {
|
||||
// The size requested is exactly the current size.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
// We have to allocate memory. Determine how much to allocate and where while the table
|
||||
// is locked.
|
||||
cur_address = current_heap_end;
|
||||
allocation_size = size - GetHeapSize();
|
||||
}
|
||||
}
|
||||
|
||||
const u64 previous_heap_size{GetHeapSize()};
|
||||
// Reserve memory for the heap extension.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
allocation_size);
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
|
||||
// Allocate pages for the heap extension.
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
|
||||
memory_pool));
|
||||
|
||||
// Increase the heap size
|
||||
// Map the pages.
|
||||
{
|
||||
std::lock_guard lock{page_table_lock};
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
|
||||
const u64 delta{size - previous_heap_size};
|
||||
// Ensure that the heap hasn't changed since we began executing.
|
||||
ASSERT(cur_address == current_heap_end);
|
||||
|
||||
// Reserve memory for the heap extension.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
delta);
|
||||
// Check the memory state.
|
||||
std::size_t num_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end,
|
||||
allocation_size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryAttribute::None));
|
||||
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
|
||||
return ResultLimitReached;
|
||||
// Map the pages.
|
||||
const auto num_pages = allocation_size / PageSize;
|
||||
R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
|
||||
std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0,
|
||||
PageSize);
|
||||
}
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
const std::size_t num_pages{delta / PageSize};
|
||||
|
||||
CASCADE_CODE(
|
||||
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
|
||||
|
||||
if (IsRegionMapped(current_heap_addr, delta)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
CASCADE_CODE(
|
||||
Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
// Succeeded in allocation, commit the resource reservation
|
||||
// We succeeded, so commit our memory reservation.
|
||||
memory_reservation.Commit();
|
||||
|
||||
block_manager->Update(current_heap_addr, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite);
|
||||
// Apply the memory block update.
|
||||
block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
|
||||
|
||||
current_heap_addr = heap_region_start + size;
|
||||
// Update the current heap end.
|
||||
current_heap_end = heap_region_start + size;
|
||||
|
||||
// Set the output.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
return heap_region_start;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
@@ -978,7 +1069,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
@@ -1031,9 +1122,8 @@ ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
|
||||
|
||||
bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
|
||||
return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::Mask, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped)
|
||||
KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
|
||||
.IsError();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,11 @@ public:
|
||||
KMemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
|
||||
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
KMemoryAttribute value);
|
||||
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
|
||||
ResultVal<VAddr> SetHeapSize(std::size_t size);
|
||||
ResultCode SetMaxHeapSize(std::size_t size);
|
||||
ResultCode SetHeapSize(VAddr* out, std::size_t size);
|
||||
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
bool is_map_only, VAddr region_start,
|
||||
std::size_t region_num_pages, KMemoryState state,
|
||||
@@ -182,14 +183,15 @@ public:
|
||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||
return alias_code_region_end - alias_code_region_start;
|
||||
}
|
||||
size_t GetNormalMemorySize() {
|
||||
std::lock_guard lk(page_table_lock);
|
||||
return GetHeapSize() + mapped_physical_memory_size;
|
||||
}
|
||||
constexpr std::size_t GetAddressSpaceWidth() const {
|
||||
return address_space_width;
|
||||
}
|
||||
constexpr std::size_t GetHeapSize() {
|
||||
return current_heap_addr - heap_region_start;
|
||||
}
|
||||
constexpr std::size_t GetTotalHeapSize() {
|
||||
return GetHeapSize() + physical_memory_usage;
|
||||
constexpr std::size_t GetHeapSize() const {
|
||||
return current_heap_end - heap_region_start;
|
||||
}
|
||||
constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
|
||||
return address_space_start <= address && address + size - 1 <= address_space_end - 1;
|
||||
@@ -269,10 +271,8 @@ private:
|
||||
VAddr code_region_end{};
|
||||
VAddr alias_code_region_start{};
|
||||
VAddr alias_code_region_end{};
|
||||
VAddr current_heap_addr{};
|
||||
|
||||
std::size_t heap_capacity{};
|
||||
std::size_t physical_memory_usage{};
|
||||
std::size_t mapped_physical_memory_size{};
|
||||
std::size_t max_heap_size{};
|
||||
std::size_t max_physical_memory_size{};
|
||||
std::size_t address_space_width{};
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -173,7 +172,7 @@ void KProcess::DecrementThreadCount() {
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
|
||||
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
|
||||
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
|
||||
page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size +
|
||||
main_thread_stack_size};
|
||||
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
|
||||
capacity != pool_size) {
|
||||
@@ -190,7 +189,7 @@ u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
|
||||
}
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryUsed() const {
|
||||
return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
|
||||
return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() +
|
||||
GetSystemResourceSize();
|
||||
}
|
||||
|
||||
@@ -221,30 +220,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::PinCurrentThread() {
|
||||
void KProcess::PinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// If the thread isn't terminated, pin it.
|
||||
if (!cur_thread->IsTerminationRequested()) {
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
cur_thread->Pin(core_id);
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::UnpinCurrentThread() {
|
||||
void KProcess::UnpinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// Unpin it.
|
||||
cur_thread->Unpin();
|
||||
@@ -411,8 +408,8 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
resource_limit->Reserve(LimitableResource::Threads, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
|
||||
|
||||
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
|
||||
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
|
||||
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
|
||||
ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError());
|
||||
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
@@ -543,7 +540,6 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
|
||||
}
|
||||
|
||||
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
|
||||
KMemoryPermission permission) {
|
||||
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
|
||||
|
||||
@@ -345,8 +345,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
void PinCurrentThread(s32 core_id);
|
||||
void UnpinCurrentThread(s32 core_id);
|
||||
void UnpinThread(KThread* thread);
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
@@ -53,6 +54,13 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
}
|
||||
cores_pending_reschedule &= ~(1ULL << core);
|
||||
}
|
||||
|
||||
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
|
||||
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
|
||||
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
|
||||
}
|
||||
}
|
||||
|
||||
if (must_context_switch) {
|
||||
auto core_scheduler = kernel.CurrentScheduler();
|
||||
kernel.ExitSVCProfile();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -26,12 +27,14 @@
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
@@ -50,6 +53,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
|
||||
VAddr entry_point, u64 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
|
||||
context.pc = entry_point;
|
||||
context.sp = stack_top;
|
||||
// TODO(merry): Perform a hardware test to determine the below value.
|
||||
@@ -61,6 +65,13 @@ namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ThreadLocalRegion {
|
||||
static constexpr std::size_t MessageBufferSize = 0x100;
|
||||
std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer;
|
||||
std::atomic_uint16_t disable_count;
|
||||
std::atomic_uint16_t interrupt_flag;
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
|
||||
public:
|
||||
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
|
||||
@@ -344,7 +355,7 @@ void KThread::StartTermination() {
|
||||
if (parent != nullptr) {
|
||||
parent->ReleaseUserException(this);
|
||||
if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
|
||||
parent->UnpinCurrentThread();
|
||||
parent->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +381,7 @@ void KThread::StartTermination() {
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void KThread::Pin() {
|
||||
void KThread::Pin(s32 current_core) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Set ourselves as pinned.
|
||||
@@ -387,7 +398,6 @@ void KThread::Pin() {
|
||||
|
||||
// Bind ourselves to this core.
|
||||
const s32 active_core = GetActiveCore();
|
||||
const s32 current_core = GetCurrentCoreId(kernel);
|
||||
|
||||
SetActiveCore(current_core);
|
||||
physical_ideal_core_id = current_core;
|
||||
@@ -480,6 +490,36 @@ void KThread::Unpin() {
|
||||
}
|
||||
}
|
||||
|
||||
u16 KThread::GetUserDisableCount() const {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count));
|
||||
}
|
||||
|
||||
void KThread::SetInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1);
|
||||
}
|
||||
|
||||
void KThread::ClearInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
|
||||
}
|
||||
|
||||
ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
|
||||
@@ -307,6 +307,10 @@ public:
|
||||
return parent != nullptr;
|
||||
}
|
||||
|
||||
u16 GetUserDisableCount() const;
|
||||
void SetInterruptFlag();
|
||||
void ClearInterruptFlag();
|
||||
|
||||
[[nodiscard]] KThread* GetLockOwner() const {
|
||||
return lock_owner;
|
||||
}
|
||||
@@ -490,7 +494,7 @@ public:
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
|
||||
void Pin();
|
||||
void Pin(s32 current_core);
|
||||
|
||||
void Unpin();
|
||||
|
||||
|
||||
@@ -182,7 +182,10 @@ struct KernelCore::Impl {
|
||||
// Shutdown all processes.
|
||||
if (current_process) {
|
||||
current_process->Finalize();
|
||||
current_process->Close();
|
||||
// current_process->Close();
|
||||
// TODO: The current process should be destroyed based on accurate ref counting after
|
||||
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
|
||||
current_process->Destroy();
|
||||
current_process = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/kernel/svc_wrap.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/reporter.h"
|
||||
@@ -136,25 +135,15 @@ enum class ResourceLimitValueType {
|
||||
} // Anonymous namespace
|
||||
|
||||
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
||||
static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
|
||||
static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
|
||||
|
||||
// Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
|
||||
if ((heap_size % 0x200000) != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
|
||||
heap_size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
// Validate size.
|
||||
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
|
||||
R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
|
||||
|
||||
if (heap_size >= 0x200000000) {
|
||||
LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
|
||||
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
|
||||
|
||||
CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size));
|
||||
// Set the heap size.
|
||||
R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -166,9 +155,38 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission::None:
|
||||
case MemoryPermission::Read:
|
||||
case MemoryPermission::ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
|
||||
MemoryPermission perm) {
|
||||
// Validate address / size.
|
||||
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Validate the permission.
|
||||
R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
|
||||
|
||||
// Validate that the region is in range for the current process.
|
||||
auto& page_table = system.Kernel().CurrentProcess()->PageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Set the memory attribute.
|
||||
return page_table.SetMemoryPermission(address, size, perm);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
|
||||
u32 attribute) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
|
||||
size, mask, attribute);
|
||||
@@ -212,7 +230,6 @@ static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 si
|
||||
|
||||
/// Maps a memory range into a different range.
|
||||
static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||
src_addr, size);
|
||||
|
||||
@@ -232,7 +249,6 @@ static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr,
|
||||
|
||||
/// Unmaps a region that was previously mapped with svcMapMemory
|
||||
static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||
src_addr, size);
|
||||
|
||||
@@ -642,7 +658,6 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
|
||||
/// Gets system/memory information for the current process
|
||||
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
|
||||
u64 info_sub_id) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
|
||||
info_sub_id, handle);
|
||||
|
||||
@@ -886,22 +901,17 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||
return ResultSuccess;
|
||||
}
|
||||
case GetInfoType::IdleTickCount: {
|
||||
if (handle == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
|
||||
static_cast<Handle>(handle));
|
||||
return ResultInvalidHandle;
|
||||
}
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
|
||||
info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
// Verify the requested core is valid.
|
||||
const bool core_valid =
|
||||
(info_sub_id == static_cast<u64>(-1ULL)) ||
|
||||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
|
||||
R_UNLESS(core_valid, ResultInvalidCombination);
|
||||
|
||||
const auto& scheduler = *system.Kernel().CurrentScheduler();
|
||||
const auto* const idle_thread = scheduler.GetIdleThread();
|
||||
|
||||
*result = idle_thread->GetCpuTime();
|
||||
// Get the idle tick count.
|
||||
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
||||
return ResultSuccess;
|
||||
}
|
||||
default:
|
||||
@@ -924,7 +934,6 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
|
||||
|
||||
/// Maps memory at a desired address
|
||||
static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
@@ -978,7 +987,6 @@ static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size)
|
||||
|
||||
/// Unmaps memory previously mapped via MapPhysicalMemory
|
||||
static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
@@ -1520,7 +1528,6 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
|
||||
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
|
||||
VAddr page_info_address, Handle process_handle,
|
||||
VAddr address) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
|
||||
@@ -2020,6 +2027,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
|
||||
count);
|
||||
}
|
||||
|
||||
static void SynchronizePreemptionState(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// If the current thread is pinned, unpin it.
|
||||
KProcess* cur_process = system.Kernel().CurrentProcess();
|
||||
const auto core_id = GetCurrentCoreId(kernel);
|
||||
|
||||
if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
|
||||
// Clear the current thread's interrupt flag.
|
||||
GetCurrentThread(kernel).ClearInterruptFlag();
|
||||
|
||||
// Unpin the current thread.
|
||||
cur_process->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
|
||||
s32 value, s32 count) {
|
||||
return SignalToAddress(system, address, signal_type, value, count);
|
||||
@@ -2738,7 +2764,7 @@ static const FunctionDef SVC_Table_32[] = {
|
||||
static const FunctionDef SVC_Table_64[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, nullptr, "SetMemoryPermission"},
|
||||
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
|
||||
@@ -2790,7 +2816,7 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
|
||||
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
|
||||
{0x36, nullptr, "SynchronizePreemptionState"},
|
||||
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/literals.h"
|
||||
|
||||
namespace Kernel {
|
||||
using Handle = u32;
|
||||
@@ -12,9 +13,13 @@ using Handle = u32;
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr s32 ArgumentHandleCountMax = 0x40;
|
||||
constexpr u32 HandleWaitMask{1u << 30};
|
||||
|
||||
constexpr inline std::size_t HeapSizeAlignment = 2_MiB;
|
||||
|
||||
constexpr inline Handle InvalidHandle = Handle(0);
|
||||
|
||||
enum PseudoHandle : Handle {
|
||||
|
||||
@@ -249,6 +249,14 @@ void SvcWrap64(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
|
||||
}
|
||||
|
||||
// Used by SetMemoryPermission
|
||||
template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<Svc::MemoryPermission>(Param(system, 2)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
// Used by MapSharedMemory
|
||||
template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <core/hle/lock.h>
|
||||
|
||||
namespace HLE {
|
||||
std::recursive_mutex g_hle_lock;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace HLE {
|
||||
/*
|
||||
* Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
|
||||
* application thread performs a syscall. It should be acquired by any host threads that read or
|
||||
* modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
|
||||
* to the emulated memory is not protected by this mutex, and should be avoided in any threads other
|
||||
* than the CPU thread.
|
||||
*/
|
||||
extern std::recursive_mutex g_hle_lock;
|
||||
} // namespace HLE
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/bcat/backend/backend.h"
|
||||
|
||||
namespace Service::BCAT {
|
||||
@@ -29,10 +28,6 @@ DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SetNeedHLELock(bool need) {
|
||||
need_hle_lock = need;
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SetTotalSize(u64 size) {
|
||||
impl.total_bytes = size;
|
||||
SignalUpdate();
|
||||
@@ -88,12 +83,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SignalUpdate() {
|
||||
if (need_hle_lock) {
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
update_event->GetWritableEvent().Signal();
|
||||
} else {
|
||||
update_event->GetWritableEvent().Signal();
|
||||
}
|
||||
update_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
|
||||
|
||||
@@ -71,10 +71,6 @@ class ProgressServiceBackend {
|
||||
public:
|
||||
~ProgressServiceBackend();
|
||||
|
||||
// Clients should call this with true if any of the functions are going to be called from a
|
||||
// non-HLE thread and this class need to lock the hle mutex. (default is false)
|
||||
void SetNeedHLELock(bool need);
|
||||
|
||||
// Sets the number of bytes total in the entire download.
|
||||
void SetTotalSize(u64 size);
|
||||
|
||||
@@ -109,7 +105,6 @@ private:
|
||||
|
||||
DeliveryCacheProgressImpl impl{};
|
||||
Kernel::KEvent* update_event;
|
||||
bool need_hle_lock = false;
|
||||
};
|
||||
|
||||
// A class representing an abstract backend for BCAT functionality.
|
||||
|
||||
@@ -33,15 +33,14 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
|
||||
next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
// Try to read sixaxis sensor states
|
||||
const auto motion_status = console->GetMotion();
|
||||
last_global_timestamp = core_timing.GetGlobalTimeNs().count();
|
||||
|
||||
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
|
||||
// This value increments every time the switch goes to sleep
|
||||
next_seven_sixaxis_state.unknown = 1;
|
||||
next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
|
||||
next_seven_sixaxis_state.accel = motion_status.accel;
|
||||
// Zero gyro values as they just mess up with the camera
|
||||
// Note: Probably a correct sensivity setting must be set
|
||||
next_seven_sixaxis_state.gyro = {};
|
||||
next_seven_sixaxis_state.gyro = motion_status.gyro;
|
||||
next_seven_sixaxis_state.quaternion = {
|
||||
{
|
||||
motion_status.quaternion.xyz.y,
|
||||
@@ -52,9 +51,9 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
};
|
||||
|
||||
console_six_axis.sampling_number++;
|
||||
// TODO(German77): Find the purpose of those values
|
||||
console_six_axis.verticalization_error = 0.0f;
|
||||
console_six_axis.gyro_bias = {0.0f, 0.0f, 0.0f};
|
||||
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
console_six_axis.verticalization_error = motion_status.verticalization_error;
|
||||
console_six_axis.gyro_bias = motion_status.gyro_bias;
|
||||
|
||||
// Update console six axis shared memory
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
|
||||
@@ -69,7 +68,6 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
|
||||
}
|
||||
|
||||
void Controller_ConsoleSixAxis::ResetTimestamp() {
|
||||
seven_sixaxis_lifo.buffer_count = 0;
|
||||
seven_sixaxis_lifo.buffer_tail = 0;
|
||||
last_saved_timestamp = last_global_timestamp;
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -39,8 +39,9 @@ public:
|
||||
|
||||
private:
|
||||
struct SevenSixAxisState {
|
||||
INSERT_PADDING_WORDS(4); // unused
|
||||
s64 sampling_number{};
|
||||
INSERT_PADDING_WORDS(2); // unused
|
||||
u64 timestamp{};
|
||||
u64 sampling_number{};
|
||||
u64 unknown{};
|
||||
Common::Vec3f accel{};
|
||||
Common::Vec3f gyro{};
|
||||
@@ -52,9 +53,10 @@ private:
|
||||
struct ConsoleSharedMemory {
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
INSERT_PADDING_BYTES(3); // padding
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
};
|
||||
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
|
||||
|
||||
@@ -64,6 +66,8 @@ private:
|
||||
Core::HID::EmulatedConsole* console;
|
||||
u8* transfer_memory = nullptr;
|
||||
bool is_transfer_memory_set = false;
|
||||
u64 last_saved_timestamp{};
|
||||
u64 last_global_timestamp{};
|
||||
ConsoleSharedMemory console_six_axis{};
|
||||
SevenSixAxisState next_seven_sixaxis_state{};
|
||||
};
|
||||
|
||||
@@ -66,9 +66,9 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
|
||||
auto& controller = controller_data[i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
|
||||
DEFAULT_VIBRATION_VALUE;
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
|
||||
DEFAULT_VIBRATION_VALUE;
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this,
|
||||
i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
|
||||
@@ -781,7 +781,8 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||
Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
|
||||
controller.device->SetVibration(device_index, vibration);
|
||||
// Then reset the vibration value to its default value.
|
||||
controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
|
||||
controller.vibration[device_index].latest_vibration_value =
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -90,13 +90,6 @@ public:
|
||||
Default = 3,
|
||||
};
|
||||
|
||||
static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
|
||||
.low_amplitude = 0.0f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.0f,
|
||||
.high_frequency = 320.0f,
|
||||
};
|
||||
|
||||
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
|
||||
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
|
||||
|
||||
|
||||
@@ -1404,7 +1404,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
.high_frequency = 0.0f,
|
||||
};
|
||||
default:
|
||||
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
|
||||
return Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
}();
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
|
||||
@@ -337,7 +336,6 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
if (buffer.size() < sizeof(AmiiboFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000,
|
||||
kip->GetKernelCapabilities());
|
||||
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
Kernel::CodeSet codeset;
|
||||
Kernel::PhysicalMemory program_image;
|
||||
|
||||
@@ -91,7 +90,14 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
|
||||
codeset.DataSegment().size += kip->GetBSSSize();
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
|
||||
.IsError()) {
|
||||
return {ResultStatus::ErrorNotInitialized, {}};
|
||||
}
|
||||
|
||||
codeset.memory = std::move(program_image);
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
process.LoadModule(std::move(codeset), base_address);
|
||||
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
||||
|
||||
@@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=, this] {
|
||||
u16 min_x{UINT16_MAX};
|
||||
u16 min_y{UINT16_MAX};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{
|
||||
[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
static constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
static constexpr u16 MAX_VALUE = UINT16_MAX;
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
const auto& touchpad_0 = data.touch[0];
|
||||
if (touchpad_0.is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
|
||||
const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
|
||||
const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
|
||||
touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes configuration
|
||||
const u16 max_x = touchpad_0.x;
|
||||
const u16 max_y = touchpad_0.y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
|
||||
@@ -54,6 +54,18 @@ struct Message {
|
||||
template <typename T>
|
||||
constexpr Type GetMessageType();
|
||||
|
||||
template <typename T>
|
||||
Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
|
||||
boost::crc_32_type crc;
|
||||
Header header{
|
||||
magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
|
||||
};
|
||||
Message<T> message{header, data};
|
||||
crc.process_bytes(&message, sizeof(Message<T>));
|
||||
message.header.crc = crc.checksum();
|
||||
return message;
|
||||
}
|
||||
|
||||
namespace Request {
|
||||
|
||||
enum RegisterFlags : u8 {
|
||||
@@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
|
||||
*/
|
||||
template <typename T>
|
||||
Message<T> Create(const T data, const u32 client_id = 0) {
|
||||
boost::crc_32_type crc;
|
||||
Header header{
|
||||
CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
|
||||
};
|
||||
Message<T> message{header, data};
|
||||
crc.process_bytes(&message, sizeof(Message<T>));
|
||||
message.header.crc = crc.checksum();
|
||||
return message;
|
||||
return CreateMessage(CLIENT_MAGIC, data, client_id);
|
||||
}
|
||||
} // namespace Request
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
@@ -112,17 +112,33 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
|
||||
|
||||
@@ -176,7 +176,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
@@ -30,8 +31,9 @@ void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value)
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={};", ret, input);
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,47 +98,50 @@ void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
u32 TexCoordIndex(IR::Attribute attr) {
|
||||
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf8(ctx, inst, binding, offset, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf8(ctx, inst, binding, offset, "ftoi");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf16(ctx, inst, binding, offset, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf16(ctx, inst, binding, offset, "ftoi");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
GetCbuf(ctx, ret, binding, offset, 32);
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
@@ -149,26 +152,26 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16,
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16,
|
||||
OffsetSwizzle(u32_offset + 4));
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));",
|
||||
inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var);
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
|
||||
cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset,
|
||||
swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var,
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
|
||||
swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
|
||||
"xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
@@ -190,18 +193,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
// GLSL only exposes 8 legacy texcoords
|
||||
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
|
||||
TexCoordIndex(attr));
|
||||
ctx.AddF32("{}=0.f;", inst);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
@@ -215,16 +206,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
|
||||
} else {
|
||||
ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
@@ -247,6 +228,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
@@ -264,17 +261,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
// GLSL only exposes 8 legacy texcoords
|
||||
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
|
||||
TexCoordIndex(attr));
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
@@ -312,33 +298,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("gl_FrontColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontSpecularR:
|
||||
case IR::Attribute::ColorFrontSpecularG:
|
||||
case IR::Attribute::ColorFrontSpecularB:
|
||||
case IR::Attribute::ColorFrontSpecularA:
|
||||
ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackDiffuseR:
|
||||
case IR::Attribute::ColorBackDiffuseG:
|
||||
case IR::Attribute::ColorBackDiffuseB:
|
||||
case IR::Attribute::ColorBackDiffuseA:
|
||||
ctx.Add("gl_BackColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackSpecularR:
|
||||
case IR::Attribute::ColorBackSpecularG:
|
||||
case IR::Attribute::ColorBackSpecularB:
|
||||
case IR::Attribute::ColorBackSpecularA:
|
||||
ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::FogCoordinate:
|
||||
ctx.Add("gl_FogFragCoord={};", value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
|
||||
@@ -125,11 +125,11 @@ void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& i
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=-({});", inst, value);
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=-({});", inst, value);
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
|
||||
@@ -60,6 +60,8 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
|
||||
const IR::Value& offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex);
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
std::string_view vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
|
||||
@@ -87,11 +87,11 @@ void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::strin
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(-({}));", inst, value);
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=-({});", inst, value);
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
|
||||
@@ -90,7 +90,9 @@ void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value&
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
ctx.Add("{}={};", phi_reg, val_reg);
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
|
||||
@@ -211,27 +211,6 @@ std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.stores.Legacy()) {
|
||||
return;
|
||||
}
|
||||
if (ctx.info.stores.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_FrontColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
header += "vec4 gl_FrontSecondaryColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
header += "vec4 gl_BackColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
header += "vec4 gl_BackSecondaryColor;";
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!StoresPerVertexAttributes(ctx.stage)) {
|
||||
return;
|
||||
@@ -250,7 +229,6 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
|
||||
header += "int gl_ViewportIndex;";
|
||||
}
|
||||
SetupLegacyOutPerVertex(ctx, header);
|
||||
header += "};";
|
||||
if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
|
||||
header += "out int gl_ViewportIndex;";
|
||||
@@ -282,21 +260,6 @@ void SetupInPerVertex(EmitContext& ctx, std::string& header) {
|
||||
}
|
||||
header += "}gl_in[gl_MaxPatchVertices];";
|
||||
}
|
||||
|
||||
void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.loads.Legacy()) {
|
||||
return;
|
||||
}
|
||||
header += "in gl_PerFragment{";
|
||||
if (ctx.info.loads.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_Color;";
|
||||
}
|
||||
header += "};";
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
@@ -361,7 +324,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
||||
}
|
||||
SetupOutPerVertex(*this, header);
|
||||
SetupInPerVertex(*this, header);
|
||||
SetupLegacyInPerFragment(*this, header);
|
||||
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
|
||||
@@ -466,9 +428,10 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
|
||||
return;
|
||||
}
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
header += fmt::format(
|
||||
"layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
|
||||
const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"};
|
||||
header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, cbuf_type,
|
||||
stage_name, desc.index, 4 * 1024);
|
||||
bindings.uniform_buffer += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,20 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702) // Ignore unreachable code warning
|
||||
#endif
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
template <typename ArgType>
|
||||
ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Id>) {
|
||||
|
||||
@@ -44,14 +44,6 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLegacyAttribute(IR::Attribute attribute) {
|
||||
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) ||
|
||||
attribute == IR::Attribute::FogCoordinate ||
|
||||
(attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
|
||||
if (ctx.stage == Stage::TessellationControl) {
|
||||
@@ -83,17 +75,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
|
||||
}
|
||||
}
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
if (attr == IR::Attribute::FogCoordinate) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
|
||||
ctx.Const(0u));
|
||||
} else {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
|
||||
element_id);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PointSize:
|
||||
return ctx.output_point_size;
|
||||
@@ -327,18 +308,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
}
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
if (attr == IR::Attribute::FogCoordinate) {
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
|
||||
ctx.InputLegacyAttribute(attr), ctx.Const(0u))};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
} else {
|
||||
const Id element_id{ctx.Const(element)};
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
|
||||
ctx.InputLegacyAttribute(attr), element_id)};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
@@ -386,6 +355,31 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.primitive_id);
|
||||
case IR::Attribute::InstanceId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.instance_id);
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
case IR::Attribute::VertexId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Read U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
|
||||
const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
|
||||
if (!output) {
|
||||
|
||||
@@ -53,6 +53,7 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
|
||||
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
enum class Operation {
|
||||
Increment,
|
||||
Decrement,
|
||||
@@ -432,34 +430,6 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
|
||||
return pointer_type;
|
||||
}
|
||||
}
|
||||
|
||||
size_t FindAndSetNextUnusedLocation(std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset) {
|
||||
for (size_t location = start_offset; location < used_locations.size(); ++location) {
|
||||
if (!used_locations.test(location)) {
|
||||
start_offset = location;
|
||||
used_locations.set(location);
|
||||
return location;
|
||||
}
|
||||
}
|
||||
throw RuntimeError("Unable to get an unused location for legacy attribute");
|
||||
}
|
||||
|
||||
Id DefineLegacyInput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset) {
|
||||
const Id id{DefineInput(ctx, ctx.F32[4], true)};
|
||||
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
|
||||
ctx.Decorate(id, spv::Decoration::Location, location);
|
||||
return id;
|
||||
}
|
||||
|
||||
Id DefineLegacyOutput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset, std::optional<u32> invocations) {
|
||||
const Id id{DefineOutput(ctx, ctx.F32[4], invocations)};
|
||||
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
|
||||
ctx.Decorate(id, spv::Decoration::Location, location);
|
||||
return id;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
|
||||
@@ -543,64 +513,6 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
|
||||
return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
|
||||
}
|
||||
|
||||
Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) {
|
||||
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorFrontDiffuseA) {
|
||||
return input_front_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
|
||||
attribute <= IR::Attribute::ColorFrontSpecularA) {
|
||||
return input_front_secondary_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackDiffuseA) {
|
||||
return input_back_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackSpecularR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) {
|
||||
return input_back_secondary_color;
|
||||
}
|
||||
if (attribute == IR::Attribute::FogCoordinate) {
|
||||
return input_fog_frag_coord;
|
||||
}
|
||||
if (attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q) {
|
||||
u32 index =
|
||||
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
return input_fixed_fnc_textures[index];
|
||||
}
|
||||
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
|
||||
}
|
||||
|
||||
Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) {
|
||||
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorFrontDiffuseA) {
|
||||
return output_front_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
|
||||
attribute <= IR::Attribute::ColorFrontSpecularA) {
|
||||
return output_front_secondary_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackDiffuseA) {
|
||||
return output_back_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackSpecularR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) {
|
||||
return output_back_secondary_color;
|
||||
}
|
||||
if (attribute == IR::Attribute::FogCoordinate) {
|
||||
return output_fog_frag_coord;
|
||||
}
|
||||
if (attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q) {
|
||||
u32 index =
|
||||
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
return output_fixed_fnc_textures[index];
|
||||
}
|
||||
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
|
||||
}
|
||||
|
||||
void EmitContext::DefineCommonTypes(const Info& info) {
|
||||
void_id = TypeVoid();
|
||||
|
||||
@@ -1389,7 +1301,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
loads[IR::Attribute::TessellationEvaluationPointV]) {
|
||||
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index)) {
|
||||
@@ -1401,7 +1312,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (input_type == AttributeType::Disabled) {
|
||||
continue;
|
||||
}
|
||||
used_locations.set(index);
|
||||
const Id type{GetAttributeType(*this, input_type)};
|
||||
const Id id{DefineInput(*this, type, true)};
|
||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
||||
@@ -1427,30 +1337,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
input_front_secondary_color =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
input_back_secondary_color =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::FogCoordinate)) {
|
||||
input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
input_fixed_fnc_textures[index] =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
}
|
||||
if (stage == Stage::TessellationEval) {
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
@@ -1501,38 +1387,9 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
|
||||
spv::BuiltIn::ViewportMaskNV);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
DefineGenericOutput(*this, index, invocations);
|
||||
used_locations.set(index);
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
output_front_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
output_front_secondary_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
output_back_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
output_back_secondary_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) {
|
||||
output_fog_frag_coord =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
output_fixed_fnc_textures[index] =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
}
|
||||
switch (stage) {
|
||||
|
||||
@@ -113,9 +113,6 @@ public:
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
Id InputLegacyAttribute(IR::Attribute attribute);
|
||||
Id OutputLegacyAttribute(IR::Attribute attribute);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
@@ -281,22 +278,10 @@ public:
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
Id input_front_color{};
|
||||
Id input_front_secondary_color{};
|
||||
Id input_back_color{};
|
||||
Id input_back_secondary_color{};
|
||||
Id input_fog_frag_coord{};
|
||||
std::array<Id, 10> input_fixed_fnc_textures{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
Id output_front_color{};
|
||||
Id output_front_secondary_color{};
|
||||
Id output_back_color{};
|
||||
Id output_back_secondary_color{};
|
||||
Id output_fog_frag_coord{};
|
||||
std::array<Id, 10> output_fixed_fnc_textures{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
|
||||
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
|
||||
|
||||
virtual void Dump(u64 hash) = 0;
|
||||
|
||||
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
|
||||
return sph;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,8 @@ enum class Attribute : u64 {
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
|
||||
|
||||
@@ -40,6 +40,7 @@ OPCODE(GetCbufU32, U32, U32,
|
||||
OPCODE(GetCbufF32, F32, U32, U32, )
|
||||
OPCODE(GetCbufU32x2, U32x2, U32, U32, )
|
||||
OPCODE(GetAttribute, F32, Attribute, U32, )
|
||||
OPCODE(GetAttributeU32, U32, Attribute, U32, )
|
||||
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
|
||||
OPCODE(GetAttributeIndexed, F32, U32, U32, )
|
||||
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
@@ -127,6 +128,42 @@ void AddNVNStorageBuffers(IR::Program& program) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLegacyAttribute(IR::Attribute attribute) {
|
||||
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) ||
|
||||
attribute == IR::Attribute::FogCoordinate ||
|
||||
(attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q);
|
||||
}
|
||||
|
||||
std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||
const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) {
|
||||
std::map<IR::Attribute, IR::Attribute> mapping;
|
||||
for (size_t index = 0; index < 4; ++index) {
|
||||
auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
|
||||
if (state.AnyComponent(attr)) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mapping.insert({attr + i, ununsed_generics.front() + i});
|
||||
}
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
}
|
||||
if (state[IR::Attribute::FogCoordinate]) {
|
||||
mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()});
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
|
||||
if (state.AnyComponent(attr)) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mapping.insert({attr + i, ununsed_generics.front() + i});
|
||||
}
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
@@ -226,4 +263,62 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
|
||||
auto& stores = program.info.stores;
|
||||
if (stores.Legacy()) {
|
||||
std::queue<IR::Attribute> ununsed_output_generics{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!stores.Generic(index)) {
|
||||
ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4);
|
||||
}
|
||||
}
|
||||
auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics);
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::SetAttribute: {
|
||||
const auto attr = inst.Arg(0).Attribute();
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
stores.Set(mappings[attr], true);
|
||||
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& loads = program.info.loads;
|
||||
if (loads.Legacy()) {
|
||||
std::queue<IR::Attribute> ununsed_input_generics{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
|
||||
input_type == AttributeType::Disabled) {
|
||||
ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4);
|
||||
}
|
||||
}
|
||||
auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics);
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::GetAttribute: {
|
||||
const auto attr = inst.Arg(0).Attribute();
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
loads.Set(mappings[attr], true);
|
||||
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
|
||||
@@ -20,4 +21,7 @@ namespace Shader::Maxwell {
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
||||
Environment& env_vertex_b);
|
||||
|
||||
[[nodiscard]] void ConvertLegacyToGeneric(IR::Program& program,
|
||||
const Shader::RuntimeInfo& runtime_info);
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -389,6 +389,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
info.uses_demote_to_helper_invocation = true;
|
||||
break;
|
||||
case IR::Opcode::GetAttribute:
|
||||
case IR::Opcode::GetAttributeU32:
|
||||
info.loads.mask[static_cast<size_t>(inst.Arg(0).Attribute())] = true;
|
||||
break;
|
||||
case IR::Opcode::SetAttribute:
|
||||
|
||||
@@ -505,6 +505,29 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (op == IR::Opcode::BitCastU32F32) {
|
||||
// Workaround for new NVIDIA driver bug, where:
|
||||
// uint attr = ftou(itof(gl_InstanceID));
|
||||
// always returned 0.
|
||||
// We can instead manually optimize this and work around the driver bug:
|
||||
// uint attr = uint(gl_InstanceID);
|
||||
if (arg_inst->GetOpcode() == IR::Opcode::GetAttribute) {
|
||||
const IR::Attribute attr{arg_inst->Arg(0).Attribute()};
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
case IR::Attribute::InstanceId:
|
||||
case IR::Attribute::VertexId:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
// Replace the bitcasts with an integer attribute get
|
||||
inst.ReplaceOpcode(IR::Opcode::GetAttributeU32);
|
||||
inst.SetArg(0, arg_inst->Arg(0));
|
||||
inst.SetArg(1, arg_inst->Arg(1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {
|
||||
|
||||
@@ -65,6 +65,10 @@ struct Profile {
|
||||
bool has_gl_component_indexing_bug{};
|
||||
/// The precise type qualifier is broken in the fragment stage of some drivers
|
||||
bool has_gl_precise_bug{};
|
||||
/// Some drivers do not properly support floatBitsToUint when used on cbufs
|
||||
bool has_gl_cbuf_ftou_bug{};
|
||||
/// Some drivers poorly optimize boolean variable references
|
||||
bool has_gl_bool_ref_bug{};
|
||||
/// Ignores SPIR-V ordered vs unordered using GLSL semantics
|
||||
bool ignore_nan_fp_comparisons{};
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ struct VaryingState {
|
||||
return AnyComponent(IR::Attribute::ColorFrontDiffuseR) ||
|
||||
AnyComponent(IR::Attribute::ColorFrontSpecularR) ||
|
||||
AnyComponent(IR::Attribute::ColorBackDiffuseR) ||
|
||||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture();
|
||||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture() ||
|
||||
mask[static_cast<size_t>(IR::Attribute::FogCoordinate)];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool FixedFunctionTexture() const noexcept {
|
||||
|
||||
@@ -10,11 +10,12 @@ add_executable(tests
|
||||
core/network/network.cpp
|
||||
tests.cpp
|
||||
video_core/buffer_base.cpp
|
||||
input_common/calibration_configuration_job.cpp
|
||||
)
|
||||
|
||||
create_target_directory_groups(tests)
|
||||
|
||||
target_link_libraries(tests PRIVATE common core)
|
||||
target_link_libraries(tests PRIVATE common core input_common)
|
||||
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
|
||||
|
||||
add_test(NAME tests COMMAND tests)
|
||||
|
||||
136
src/tests/input_common/calibration_configuration_job.cpp
Normal file
136
src/tests/input_common/calibration_configuration_job.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/crc.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
class FakeCemuhookServer {
|
||||
public:
|
||||
FakeCemuhookServer()
|
||||
: socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {}
|
||||
|
||||
~FakeCemuhookServer() {
|
||||
is_running = false;
|
||||
boost::system::error_code error_code;
|
||||
socket.shutdown(boost::asio::socket_base::shutdown_both, error_code);
|
||||
socket.close();
|
||||
if (handler.joinable()) {
|
||||
handler.join();
|
||||
}
|
||||
}
|
||||
|
||||
u16 GetPort() {
|
||||
return socket.local_endpoint().port();
|
||||
}
|
||||
|
||||
std::string GetHost() {
|
||||
return socket.local_endpoint().address().to_string();
|
||||
}
|
||||
|
||||
void Run(const std::vector<InputCommon::CemuhookUDP::Response::TouchPad> touch_movement_path) {
|
||||
constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header);
|
||||
constexpr size_t PadDataSize =
|
||||
sizeof(InputCommon::CemuhookUDP::Message<InputCommon::CemuhookUDP::Response::PadData>);
|
||||
|
||||
REQUIRE(touch_movement_path.size() > 0);
|
||||
is_running = true;
|
||||
handler = std::thread([touch_movement_path, this]() {
|
||||
auto current_touch_position = touch_movement_path.begin();
|
||||
while (is_running) {
|
||||
boost::asio::ip::udp::endpoint sender_endpoint;
|
||||
boost::system::error_code error_code;
|
||||
auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer),
|
||||
sender_endpoint, 0, error_code);
|
||||
|
||||
if (received_size < HeaderSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InputCommon::CemuhookUDP::Header header{};
|
||||
std::memcpy(&header, receive_buffer.data(), HeaderSize);
|
||||
switch (header.type) {
|
||||
case InputCommon::CemuhookUDP::Type::PadData: {
|
||||
InputCommon::CemuhookUDP::Response::PadData pad_data{};
|
||||
pad_data.touch[0] = *current_touch_position;
|
||||
const auto pad_message = InputCommon::CemuhookUDP::CreateMessage(
|
||||
InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0);
|
||||
std::memcpy(send_buffer.data(), &pad_message, PadDataSize);
|
||||
socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint,
|
||||
0, error_code);
|
||||
|
||||
bool can_advance =
|
||||
std::next(current_touch_position) != touch_movement_path.end();
|
||||
if (can_advance) {
|
||||
std::advance(current_touch_position, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InputCommon::CemuhookUDP::Type::PortInfo:
|
||||
case InputCommon::CemuhookUDP::Type::Version:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::udp::socket socket;
|
||||
std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> send_buffer;
|
||||
std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> receive_buffer;
|
||||
bool is_running = false;
|
||||
std::thread handler;
|
||||
};
|
||||
|
||||
TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") {
|
||||
Common::Event complete_event;
|
||||
FakeCemuhookServer server;
|
||||
server.Run({{
|
||||
.is_active = 1,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
},
|
||||
{
|
||||
.is_active = 1,
|
||||
.x = 200,
|
||||
.y = 200,
|
||||
}});
|
||||
|
||||
InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{};
|
||||
u16 min_x{};
|
||||
u16 min_y{};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
InputCommon::CemuhookUDP::CalibrationConfigurationJob job(
|
||||
server.GetHost(), server.GetPort(),
|
||||
[&status,
|
||||
&complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) {
|
||||
status = status_;
|
||||
if (status ==
|
||||
InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) {
|
||||
complete_event.Set();
|
||||
}
|
||||
},
|
||||
[&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
|
||||
min_x = min_x_;
|
||||
min_y = min_y_;
|
||||
max_x = max_x_;
|
||||
max_y = max_y_;
|
||||
});
|
||||
|
||||
complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10));
|
||||
REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed);
|
||||
REQUIRE(min_x == 0);
|
||||
REQUIRE(min_y == 0);
|
||||
REQUIRE(max_x == 200);
|
||||
REQUIRE(max_y == 200);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ constexpr std::array PREFERRED_GPU_DECODERS = {
|
||||
#ifdef _WIN32
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
AV_HWDEVICE_TYPE_DXVA2,
|
||||
#elif defined(__linux__)
|
||||
#elif defined(__unix__)
|
||||
AV_HWDEVICE_TYPE_VAAPI,
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
#endif
|
||||
|
||||
@@ -182,17 +182,13 @@ Device::Device() {
|
||||
shader_backend = Settings::ShaderBackend::GLSL;
|
||||
}
|
||||
|
||||
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia &&
|
||||
!Settings::values.renderer_debug) {
|
||||
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia) {
|
||||
const std::string_view driver_version = version.substr(13);
|
||||
const int version_major =
|
||||
std::atoi(driver_version.substr(0, driver_version.find(".")).data());
|
||||
|
||||
if (version_major >= 495) {
|
||||
LOG_WARNING(Render_OpenGL, "NVIDIA drivers 495 and later causes significant problems "
|
||||
"with yuzu. Forcing GLASM as a mitigation.");
|
||||
shader_backend = Settings::ShaderBackend::GLASM;
|
||||
use_assembly_shaders = true;
|
||||
has_cbuf_ftou_bug = true;
|
||||
has_bool_ref_bug = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,14 @@ public:
|
||||
return need_fastmath_off;
|
||||
}
|
||||
|
||||
bool HasCbufFtouBug() const {
|
||||
return has_cbuf_ftou_bug;
|
||||
}
|
||||
|
||||
bool HasBoolRefBug() const {
|
||||
return has_bool_ref_bug;
|
||||
}
|
||||
|
||||
Settings::ShaderBackend GetShaderBackend() const {
|
||||
return shader_backend;
|
||||
}
|
||||
@@ -200,6 +208,8 @@ private:
|
||||
bool has_sparse_texture_2{};
|
||||
bool warp_size_potentially_larger_than_guest{};
|
||||
bool need_fastmath_off{};
|
||||
bool has_cbuf_ftou_bug{};
|
||||
bool has_bool_ref_bug{};
|
||||
|
||||
std::string vendor_name;
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace {
|
||||
using Shader::Backend::GLASM::EmitGLASM;
|
||||
using Shader::Backend::GLSL::EmitGLSL;
|
||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||
using Shader::Maxwell::TranslateProgram;
|
||||
using VideoCommon::ComputeEnvironment;
|
||||
@@ -213,6 +214,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.has_broken_fp16_float_controls = false,
|
||||
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
|
||||
.has_gl_precise_bug = device.HasPreciseBug(),
|
||||
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
|
||||
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
|
||||
.ignore_nan_fp_comparisons = true,
|
||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||
},
|
||||
@@ -422,6 +425,11 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.unique_hashes[index]);
|
||||
}
|
||||
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
@@ -462,12 +470,14 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)};
|
||||
switch (device.GetShaderBackend()) {
|
||||
case Settings::ShaderBackend::GLSL:
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding);
|
||||
break;
|
||||
case Settings::ShaderBackend::GLASM:
|
||||
sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding);
|
||||
break;
|
||||
case Settings::ShaderBackend::SPIRV:
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding);
|
||||
break;
|
||||
}
|
||||
@@ -506,8 +516,12 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.Hash());
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
|
||||
Shader::RuntimeInfo info;
|
||||
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
||||
|
||||
@@ -1047,7 +1047,7 @@ bool Image::ScaleDown(bool ignore) {
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
|
||||
ImageId image_id_, Image& image)
|
||||
ImageId image_id_, Image& image, const SlotVector<Image>&)
|
||||
: VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} {
|
||||
const Device& device = runtime.device;
|
||||
if (True(image.flags & ImageFlagBits::Converted)) {
|
||||
|
||||
@@ -36,6 +36,7 @@ using VideoCommon::ImageViewType;
|
||||
using VideoCommon::NUM_RT;
|
||||
using VideoCommon::Region2D;
|
||||
using VideoCommon::RenderTargets;
|
||||
using VideoCommon::SlotVector;
|
||||
|
||||
struct ImageBufferMap {
|
||||
~ImageBufferMap();
|
||||
@@ -92,7 +93,7 @@ public:
|
||||
|
||||
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
|
||||
|
||||
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) {
|
||||
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
@@ -234,7 +235,8 @@ class ImageView : public VideoCommon::ImageViewBase {
|
||||
friend Image;
|
||||
|
||||
public:
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
|
||||
const SlotVector<Image>&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
|
||||
const VideoCommon::ImageViewInfo&, GPUVAddr);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
|
||||
|
||||
@@ -437,39 +437,29 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
|
||||
glBindTextureUnit(0, fxaa_texture.handle);
|
||||
}
|
||||
|
||||
// Set projection matrix
|
||||
const std::array ortho_matrix =
|
||||
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
|
||||
|
||||
GLuint fragment_handle;
|
||||
const auto filter = Settings::values.scaling_filter.GetValue();
|
||||
switch (filter) {
|
||||
case Settings::ScalingFilter::NearestNeighbor:
|
||||
fragment_handle = present_bilinear_fragment.handle;
|
||||
break;
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
fragment_handle = present_bilinear_fragment.handle;
|
||||
break;
|
||||
case Settings::ScalingFilter::Bicubic:
|
||||
fragment_handle = present_bicubic_fragment.handle;
|
||||
break;
|
||||
case Settings::ScalingFilter::Gaussian:
|
||||
fragment_handle = present_gaussian_fragment.handle;
|
||||
break;
|
||||
case Settings::ScalingFilter::ScaleForce:
|
||||
fragment_handle = present_scaleforce_fragment.handle;
|
||||
break;
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
LOG_WARNING(
|
||||
Render_OpenGL,
|
||||
"FidelityFX FSR Super Sampling is not supported in OpenGL, changing to ScaleForce");
|
||||
fragment_handle = present_scaleforce_fragment.handle;
|
||||
break;
|
||||
default:
|
||||
fragment_handle = present_bilinear_fragment.handle;
|
||||
break;
|
||||
}
|
||||
const auto fragment_handle = [this]() {
|
||||
switch (Settings::values.scaling_filter.GetValue()) {
|
||||
case Settings::ScalingFilter::NearestNeighbor:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
return present_bilinear_fragment.handle;
|
||||
case Settings::ScalingFilter::Bicubic:
|
||||
return present_bicubic_fragment.handle;
|
||||
case Settings::ScalingFilter::Gaussian:
|
||||
return present_gaussian_fragment.handle;
|
||||
case Settings::ScalingFilter::ScaleForce:
|
||||
return present_scaleforce_fragment.handle;
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
LOG_WARNING(
|
||||
Render_OpenGL,
|
||||
"FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
|
||||
return present_scaleforce_fragment.handle;
|
||||
default:
|
||||
return present_bilinear_fragment.handle;
|
||||
}
|
||||
}();
|
||||
program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
|
||||
glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
|
||||
ortho_matrix.data());
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
|
||||
@@ -335,6 +336,17 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
|
||||
cmdbuf.SetScissor(0, scissor);
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
|
||||
}
|
||||
|
||||
VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
|
||||
const auto& resolution = Settings::values.resolution_info;
|
||||
const bool is_rescaled = src_image_view.IsRescaled();
|
||||
u32 width = src_image_view.size.width;
|
||||
u32 height = src_image_view.size.height;
|
||||
return VkExtent2D{
|
||||
.width = is_rescaled ? resolution.ScaleUp(width) : width,
|
||||
.height = is_rescaled ? resolution.ScaleUp(height) : height,
|
||||
};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
|
||||
@@ -425,108 +437,52 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, u32 up_scale,
|
||||
u32 down_shift) {
|
||||
const ImageView& src_image_view) {
|
||||
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass());
|
||||
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
|
||||
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, u32 up_scale,
|
||||
u32 down_shift) {
|
||||
const ImageView& src_image_view) {
|
||||
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass());
|
||||
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
|
||||
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, u32 up_scale,
|
||||
u32 down_shift) {
|
||||
const ImageView& src_image_view) {
|
||||
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass());
|
||||
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
|
||||
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, u32 up_scale,
|
||||
u32 down_shift) {
|
||||
const ImageView& src_image_view) {
|
||||
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass());
|
||||
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
|
||||
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
|
||||
const ImageView& src_image_view) {
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
|
||||
convert_abgr8_to_d24s8_frag, true);
|
||||
ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
|
||||
down_shift);
|
||||
convert_abgr8_to_d24s8_frag);
|
||||
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
|
||||
ImageView& src_image_view) {
|
||||
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
|
||||
convert_d24s8_to_abgr8_frag, false);
|
||||
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view, up_scale,
|
||||
down_shift);
|
||||
convert_d24s8_to_abgr8_frag);
|
||||
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, u32 up_scale, u32 down_shift) {
|
||||
const ImageView& src_image_view) {
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkExtent2D extent{
|
||||
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
|
||||
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
|
||||
};
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
|
||||
this](vk::CommandBuffer cmdbuf) {
|
||||
const VkOffset2D offset{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
};
|
||||
const VkViewport viewport{
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(extent.width),
|
||||
.height = static_cast<float>(extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 0.0f,
|
||||
};
|
||||
const VkRect2D scissor{
|
||||
.offset = offset,
|
||||
.extent = extent,
|
||||
};
|
||||
const PushConstants push_constants{
|
||||
.tex_scale = {viewport.width, viewport.height},
|
||||
.tex_offset = {0.0f, 0.0f},
|
||||
};
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||
const VkExtent2D extent = GetConversionExtent(src_image_view);
|
||||
|
||||
// TODO: Barriers
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, scissor);
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkImageView src_view = src_image_view.ColorView();
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkExtent2D extent{
|
||||
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
|
||||
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
|
||||
};
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
|
||||
this](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) {
|
||||
const VkOffset2D offset{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
@@ -563,18 +519,16 @@ void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_f
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
|
||||
ImageView& src_image_view) {
|
||||
const VkPipelineLayout layout = *two_textures_pipeline_layout;
|
||||
const VkImageView src_depth_view = src_image_view.DepthView();
|
||||
const VkImageView src_stencil_view = src_image_view.StencilView();
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkExtent2D extent{
|
||||
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
|
||||
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
|
||||
};
|
||||
const VkExtent2D extent = GetConversionExtent(src_image_view);
|
||||
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, up_scale,
|
||||
down_shift, this](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent,
|
||||
this](vk::CommandBuffer cmdbuf) {
|
||||
const VkOffset2D offset{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
@@ -695,11 +649,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
|
||||
return *blit_depth_stencil_pipelines.back();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
||||
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
bool is_target_depth) {
|
||||
if (pipeline) {
|
||||
return;
|
||||
}
|
||||
const std::array stages = MakeStages(*full_screen_vert, *convert_depth_to_float_frag);
|
||||
VkShaderModule frag_shader =
|
||||
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
|
||||
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -712,8 +669,9 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
|
||||
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
|
||||
.pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
|
||||
: &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *one_texture_pipeline_layout,
|
||||
.renderPass = renderpass,
|
||||
@@ -723,37 +681,17 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
|
||||
});
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
||||
ConvertPipeline(pipeline, renderpass, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
||||
if (pipeline) {
|
||||
return;
|
||||
}
|
||||
const std::array stages = MakeStages(*full_screen_vert, *convert_float_to_depth_frag);
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *one_texture_pipeline_layout,
|
||||
.renderPass = renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
ConvertPipeline(pipeline, renderpass, true);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
vk::ShaderModule& module, bool is_target_depth,
|
||||
bool single_texture) {
|
||||
vk::ShaderModule& module, bool single_texture,
|
||||
bool is_target_depth) {
|
||||
if (pipeline) {
|
||||
return;
|
||||
}
|
||||
@@ -782,13 +720,13 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
vk::ShaderModule& module, bool single_texture) {
|
||||
ConvertPipelineEx(pipeline, renderpass, module, false, single_texture);
|
||||
vk::ShaderModule& module) {
|
||||
ConvertPipelineEx(pipeline, renderpass, module, false, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
vk::ShaderModule& module, bool single_texture) {
|
||||
ConvertPipelineEx(pipeline, renderpass, module, true, single_texture);
|
||||
vk::ShaderModule& module) {
|
||||
ConvertPipelineEx(pipeline, renderpass, module, true, true);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -44,50 +44,43 @@ public:
|
||||
const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
|
||||
Tegra::Engines::Fermi2D::Operation operation);
|
||||
|
||||
void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
u32 up_scale, u32 down_shift);
|
||||
void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
u32 up_scale, u32 down_shift);
|
||||
void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
u32 up_scale, u32 down_shift);
|
||||
void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
u32 up_scale, u32 down_shift);
|
||||
void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||
u32 up_scale, u32 down_shift);
|
||||
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||
u32 up_scale, u32 down_shift);
|
||||
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
|
||||
|
||||
private:
|
||||
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, u32 up_scale, u32 down_shift);
|
||||
|
||||
void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift);
|
||||
const ImageView& src_image_view);
|
||||
|
||||
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift);
|
||||
ImageView& src_image_view);
|
||||
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
|
||||
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
|
||||
|
||||
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
|
||||
|
||||
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
|
||||
|
||||
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
|
||||
|
||||
void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
vk::ShaderModule& module, bool is_target_depth, bool single_texture);
|
||||
vk::ShaderModule& module, bool single_texture, bool is_target_depth);
|
||||
|
||||
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
vk::ShaderModule& module, bool single_texture);
|
||||
vk::ShaderModule& module);
|
||||
|
||||
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
vk::ShaderModule& module, bool single_texture);
|
||||
vk::ShaderModule& module);
|
||||
|
||||
const Device& device;
|
||||
VKScheduler& scheduler;
|
||||
|
||||
@@ -391,28 +391,23 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||
.offset = {0, 0},
|
||||
.extent = size,
|
||||
};
|
||||
const auto filter = Settings::values.scaling_filter.GetValue();
|
||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||
switch (filter) {
|
||||
case Settings::ScalingFilter::NearestNeighbor:
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
|
||||
break;
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
|
||||
break;
|
||||
case Settings::ScalingFilter::Bicubic:
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline);
|
||||
break;
|
||||
case Settings::ScalingFilter::Gaussian:
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *gaussian_pipeline);
|
||||
break;
|
||||
case Settings::ScalingFilter::ScaleForce:
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline);
|
||||
break;
|
||||
default:
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
|
||||
break;
|
||||
}
|
||||
auto graphics_pipeline = [this]() {
|
||||
switch (Settings::values.scaling_filter.GetValue()) {
|
||||
case Settings::ScalingFilter::NearestNeighbor:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
return *bilinear_pipeline;
|
||||
case Settings::ScalingFilter::Bicubic:
|
||||
return *bicubic_pipeline;
|
||||
case Settings::ScalingFilter::Gaussian:
|
||||
return *gaussian_pipeline;
|
||||
case Settings::ScalingFilter::ScaleForce:
|
||||
return *scaleforce_pipeline;
|
||||
default:
|
||||
return *bilinear_pipeline;
|
||||
}
|
||||
}();
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, scissor);
|
||||
|
||||
|
||||
@@ -605,7 +605,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.flags = 0,
|
||||
.topology = input_assembly_topology,
|
||||
.primitiveRestartEnable = key.state.primitive_restart_enable != 0 &&
|
||||
SupportsPrimitiveRestart(input_assembly_topology),
|
||||
((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
|
||||
device.IsTopologyListPrimitiveRestartSupported()) ||
|
||||
SupportsPrimitiveRestart(input_assembly_topology) ||
|
||||
(input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
|
||||
device.IsPatchListPrimitiveRestartSupported())),
|
||||
};
|
||||
const VkPipelineTessellationStateCreateInfo tessellation_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
|
||||
@@ -613,7 +617,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.flags = 0,
|
||||
.patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
|
||||
};
|
||||
|
||||
std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
|
||||
std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
|
||||
const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
|
||||
@@ -748,8 +751,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_COPY,
|
||||
.logicOpEnable = key.state.logic_op_enable != 0,
|
||||
.logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()),
|
||||
.attachmentCount = static_cast<u32>(cb_attachments.size()),
|
||||
.pAttachments = cb_attachments.data(),
|
||||
.blendConstants = {},
|
||||
|
||||
@@ -48,6 +48,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
|
||||
|
||||
namespace {
|
||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||
using Shader::Maxwell::TranslateProgram;
|
||||
using VideoCommon::ComputeEnvironment;
|
||||
@@ -516,6 +517,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.unique_hashes[index]);
|
||||
}
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
@@ -543,6 +547,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
infos[stage_index] = &program.info;
|
||||
|
||||
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
|
||||
ConvertLegacyToGeneric(program, runtime_info);
|
||||
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)};
|
||||
device.SaveShader(code);
|
||||
modules[stage_index] = BuildShader(device, code);
|
||||
@@ -611,6 +616,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
|
||||
// Dump it before error.
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.Hash());
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
const std::vector<u32> code{EmitSPIRV(profile, program)};
|
||||
device.SaveShader(code);
|
||||
|
||||
@@ -1057,37 +1057,37 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
|
||||
});
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view,
|
||||
bool rescaled) {
|
||||
const u32 up_scale = rescaled ? resolution.up_scale : 1;
|
||||
const u32 down_shift = rescaled ? resolution.down_shift : 0;
|
||||
void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
|
||||
switch (dst_view.format) {
|
||||
case PixelFormat::R16_UNORM:
|
||||
if (src_view.format == PixelFormat::D16_UNORM) {
|
||||
return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift);
|
||||
return blit_image_helper.ConvertD16ToR16(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
|
||||
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift);
|
||||
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::R32_FLOAT:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift);
|
||||
return blit_image_helper.ConvertD32ToR32(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::D16_UNORM:
|
||||
if (src_view.format == PixelFormat::R16_UNORM) {
|
||||
return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift);
|
||||
return blit_image_helper.ConvertR16ToD16(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::S8_UINT_D24_UNORM:
|
||||
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift);
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_UNORM) {
|
||||
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::D32_FLOAT:
|
||||
if (src_view.format == PixelFormat::R32_FLOAT) {
|
||||
return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift);
|
||||
return blit_image_helper.ConvertR32ToD32(dst, src_view);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -1329,6 +1329,10 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
|
||||
}
|
||||
}
|
||||
|
||||
bool Image::IsRescaled() const noexcept {
|
||||
return True(flags & ImageFlagBits::Rescaled);
|
||||
}
|
||||
|
||||
bool Image::ScaleUp(bool ignore) {
|
||||
if (True(flags & ImageFlagBits::Rescaled)) {
|
||||
return false;
|
||||
@@ -1340,7 +1344,6 @@ bool Image::ScaleUp(bool ignore) {
|
||||
return false;
|
||||
}
|
||||
has_scaled = true;
|
||||
const auto& device = runtime->device;
|
||||
if (!scaled_image) {
|
||||
const bool is_2d = info.type == ImageType::e2D;
|
||||
const u32 scaled_width = resolution.ScaleUp(info.size.width);
|
||||
@@ -1348,7 +1351,7 @@ bool Image::ScaleUp(bool ignore) {
|
||||
auto scaled_info = info;
|
||||
scaled_info.size.width = scaled_width;
|
||||
scaled_info.size.height = scaled_height;
|
||||
scaled_image = MakeImage(device, scaled_info);
|
||||
scaled_image = MakeImage(runtime->device, scaled_info);
|
||||
auto& allocator = runtime->memory_allocator;
|
||||
scaled_commit = MemoryCommit(allocator.Commit(scaled_image, MemoryUsage::DeviceLocal));
|
||||
ignore = false;
|
||||
@@ -1357,18 +1360,13 @@ bool Image::ScaleUp(bool ignore) {
|
||||
if (ignore) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aspect_mask == 0) {
|
||||
aspect_mask = ImageAspectMask(info.format);
|
||||
}
|
||||
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
|
||||
const PixelFormat format = StorageFormat(info.format);
|
||||
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
|
||||
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
||||
if (device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT)) {
|
||||
BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution);
|
||||
} else {
|
||||
if (NeedsScaleHelper()) {
|
||||
return BlitScaleHelper(true);
|
||||
} else {
|
||||
BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1390,15 +1388,10 @@ bool Image::ScaleDown(bool ignore) {
|
||||
if (aspect_mask == 0) {
|
||||
aspect_mask = ImageAspectMask(info.format);
|
||||
}
|
||||
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
|
||||
const PixelFormat format = StorageFormat(info.format);
|
||||
const auto& device = runtime->device;
|
||||
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
|
||||
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
||||
if (device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT)) {
|
||||
BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false);
|
||||
} else {
|
||||
if (NeedsScaleHelper()) {
|
||||
return BlitScaleHelper(false);
|
||||
} else {
|
||||
BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1466,10 +1459,24 @@ bool Image::BlitScaleHelper(bool scale_up) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::NeedsScaleHelper() const {
|
||||
const auto& device = runtime->device;
|
||||
const bool needs_msaa_helper = info.num_samples > 1 && device.CantBlitMSAA();
|
||||
if (needs_msaa_helper) {
|
||||
return true;
|
||||
}
|
||||
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
|
||||
const PixelFormat format = StorageFormat(info.format);
|
||||
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
|
||||
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
||||
const bool needs_blit_helper = !device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT);
|
||||
return needs_blit_helper;
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
|
||||
ImageId image_id_, Image& image)
|
||||
: VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device},
|
||||
image_handle{image.Handle()}, samples{ConvertSampleCount(image.info.num_samples)} {
|
||||
image_handle{image.Handle()}, samples(ConvertSampleCount(image.info.num_samples)) {
|
||||
using Shader::TextureType;
|
||||
|
||||
const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
|
||||
@@ -1552,6 +1559,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
}
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
|
||||
ImageId image_id_, Image& image, const SlotVector<Image>& slot_imgs)
|
||||
: ImageView{runtime, info, image_id_, image} {
|
||||
slot_images = &slot_imgs;
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
|
||||
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
|
||||
: VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
|
||||
@@ -1607,6 +1620,15 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
|
||||
return *view;
|
||||
}
|
||||
|
||||
bool ImageView::IsRescaled() const noexcept {
|
||||
if (!slot_images) {
|
||||
return false;
|
||||
}
|
||||
const auto& slots = *slot_images;
|
||||
const auto& src_image = slots[image_id];
|
||||
return src_image.IsRescaled();
|
||||
}
|
||||
|
||||
vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) {
|
||||
return device->GetLogical().CreateImageView({
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
|
||||
@@ -23,6 +23,7 @@ using VideoCommon::ImageId;
|
||||
using VideoCommon::NUM_RT;
|
||||
using VideoCommon::Region2D;
|
||||
using VideoCommon::RenderTargets;
|
||||
using VideoCommon::SlotVector;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
class ASTCDecoderPass;
|
||||
@@ -65,7 +66,7 @@ public:
|
||||
|
||||
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
|
||||
|
||||
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled);
|
||||
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view);
|
||||
|
||||
bool CanAccelerateImageUpload(Image&) const noexcept {
|
||||
return false;
|
||||
@@ -139,6 +140,8 @@ public:
|
||||
return std::exchange(initialized, true);
|
||||
}
|
||||
|
||||
bool IsRescaled() const noexcept;
|
||||
|
||||
bool ScaleUp(bool ignore = false);
|
||||
|
||||
bool ScaleDown(bool ignore = false);
|
||||
@@ -146,6 +149,8 @@ public:
|
||||
private:
|
||||
bool BlitScaleHelper(bool scale_up);
|
||||
|
||||
bool NeedsScaleHelper() const;
|
||||
|
||||
VKScheduler* scheduler{};
|
||||
TextureCacheRuntime* runtime{};
|
||||
|
||||
@@ -168,6 +173,8 @@ private:
|
||||
class ImageView : public VideoCommon::ImageViewBase {
|
||||
public:
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
|
||||
const SlotVector<Image>&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
|
||||
const VideoCommon::ImageViewInfo&, GPUVAddr);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
|
||||
@@ -189,6 +196,8 @@ public:
|
||||
[[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
|
||||
Shader::ImageFormat image_format);
|
||||
|
||||
[[nodiscard]] bool IsRescaled() const noexcept;
|
||||
|
||||
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
|
||||
return *image_views[static_cast<size_t>(texture_type)];
|
||||
}
|
||||
@@ -222,6 +231,8 @@ private:
|
||||
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
|
||||
|
||||
const Device* device = nullptr;
|
||||
const SlotVector<Image>* slot_images = nullptr;
|
||||
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
|
||||
std::unique_ptr<StorageViews> storage_views;
|
||||
vk::ImageView depth_view;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string_view StageToPrefix(Shader::Stage stage) {
|
||||
switch (stage) {
|
||||
case Shader::Stage::VertexB:
|
||||
return "VB";
|
||||
case Shader::Stage::TessellationControl:
|
||||
return "TC";
|
||||
case Shader::Stage::TessellationEval:
|
||||
return "TE";
|
||||
case Shader::Stage::Geometry:
|
||||
return "GS";
|
||||
case Shader::Stage::Fragment:
|
||||
return "FS";
|
||||
case Shader::Stage::Compute:
|
||||
return "CS";
|
||||
case Shader::Stage::VertexA:
|
||||
return "VA";
|
||||
default:
|
||||
return "UK";
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest,
|
||||
u32 initial_offset, Shader::Stage stage) {
|
||||
const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
|
||||
const auto base_dir{shader_dir / "shaders"};
|
||||
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
|
||||
return;
|
||||
}
|
||||
const auto prefix = StageToPrefix(stage);
|
||||
const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)};
|
||||
const size_t real_size = read_highest - read_lowest + initial_offset;
|
||||
const size_t padding_needed = ((32 - (real_size % 32)) % 32);
|
||||
std::fstream shader_file(name, std::ios::out | std::ios::binary);
|
||||
const size_t jump_index = initial_offset / sizeof(u64);
|
||||
shader_file.write(reinterpret_cast<const char*>(code + jump_index), real_size);
|
||||
for (size_t i = 0; i < padding_needed; i++) {
|
||||
shader_file.put(0);
|
||||
}
|
||||
}
|
||||
|
||||
GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
|
||||
u32 start_address_)
|
||||
: gpu_memory{&gpu_memory_}, program_base{program_base_} {
|
||||
@@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const {
|
||||
return Common::CityHash64(data.get(), size);
|
||||
}
|
||||
|
||||
void GenericEnvironment::Dump(u64 hash) {
|
||||
DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage);
|
||||
}
|
||||
|
||||
void GenericEnvironment::Serialize(std::ofstream& file) const {
|
||||
const u64 code_size{static_cast<u64>(CachedSize())};
|
||||
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
|
||||
@@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
|
||||
u32 start_address_)
|
||||
: GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
|
||||
gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
|
||||
initial_offset = sizeof(sph);
|
||||
gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask;
|
||||
switch (program) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
@@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
|
||||
if (stage == Shader::Stage::Compute) {
|
||||
file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
|
||||
.read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
|
||||
initial_offset = 0;
|
||||
} else {
|
||||
file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
|
||||
initial_offset = sizeof(sph);
|
||||
if (stage == Shader::Stage::Geometry) {
|
||||
file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileEnvironment::Dump(u64 [[maybe_unused]] hash) {
|
||||
DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage);
|
||||
}
|
||||
|
||||
u64 FileEnvironment::ReadInstruction(u32 address) {
|
||||
if (address < read_lowest || address > read_highest) {
|
||||
throw Shader::LogicError("Out of bounds address {}", address);
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
|
||||
[[nodiscard]] u64 CalculateHash() const;
|
||||
|
||||
void Dump(u64 hash) override;
|
||||
|
||||
void Serialize(std::ofstream& file) const;
|
||||
|
||||
protected:
|
||||
@@ -82,6 +84,7 @@ protected:
|
||||
|
||||
u32 cached_lowest = std::numeric_limits<u32>::max();
|
||||
u32 cached_highest = 0;
|
||||
u32 initial_offset = 0;
|
||||
|
||||
bool has_unbound_instructions = false;
|
||||
};
|
||||
@@ -149,6 +152,8 @@ public:
|
||||
|
||||
[[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
|
||||
|
||||
void Dump(u64 hash) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<u64[]> code;
|
||||
std::unordered_map<u32, Shader::TextureType> texture_types;
|
||||
@@ -159,6 +164,7 @@ private:
|
||||
u32 texture_bound{};
|
||||
u32 read_lowest{};
|
||||
u32 read_highest{};
|
||||
u32 initial_offset{};
|
||||
};
|
||||
|
||||
void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
|
||||
|
||||
@@ -1137,8 +1137,13 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
|
||||
} while (has_deleted_images);
|
||||
const ImageBase& src_image = slot_images[src_id];
|
||||
const ImageBase& dst_image = slot_images[dst_id];
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
if (GetFormatType(dst_info.format) != GetFormatType(dst_image.info.format) ||
|
||||
GetFormatType(src_info.format) != GetFormatType(src_image.info.format)) {
|
||||
GetFormatType(src_info.format) != GetFormatType(src_image.info.format) ||
|
||||
!VideoCore::Surface::IsViewCompatible(dst_info.format, dst_image.info.format, false,
|
||||
native_bgr) ||
|
||||
!VideoCore::Surface::IsViewCompatible(src_info.format, src_image.info.format, false,
|
||||
native_bgr)) {
|
||||
// Make sure the images match the expected format.
|
||||
do {
|
||||
has_deleted_images = false;
|
||||
@@ -1392,7 +1397,8 @@ ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const Imag
|
||||
if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
|
||||
return image_view_id;
|
||||
}
|
||||
const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image);
|
||||
const ImageViewId image_view_id =
|
||||
slot_image_views.insert(runtime, info, image_id, image, slot_images);
|
||||
image.InsertView(info, image_view_id);
|
||||
return image_view_id;
|
||||
}
|
||||
@@ -1850,9 +1856,20 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
|
||||
.height = std::min(dst_view.size.height, src_view.size.height),
|
||||
.depth = std::min(dst_view.size.depth, src_view.size.depth),
|
||||
};
|
||||
UNIMPLEMENTED_IF(copy.extent != expected_size);
|
||||
const Extent3D scaled_extent = [is_rescaled, expected_size]() {
|
||||
if (!is_rescaled) {
|
||||
return expected_size;
|
||||
}
|
||||
const auto& resolution = Settings::values.resolution_info;
|
||||
return Extent3D{
|
||||
.width = resolution.ScaleUp(expected_size.width),
|
||||
.height = resolution.ScaleUp(expected_size.height),
|
||||
.depth = expected_size.depth,
|
||||
};
|
||||
}();
|
||||
UNIMPLEMENTED_IF(copy.extent != scaled_extent);
|
||||
|
||||
runtime.ConvertImage(dst_framebuffer, dst_view, src_view, is_rescaled);
|
||||
runtime.ConvertImage(dst_framebuffer, dst_view, src_view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,14 +364,14 @@ template <u32 GOB_EXTENT>
|
||||
|
||||
[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress2D(
|
||||
const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
|
||||
const u32 layer_stride = new_info.layer_stride;
|
||||
const s32 new_size = layer_stride * new_info.resources.layers;
|
||||
const s32 diff = static_cast<s32>(overlap.gpu_addr - gpu_addr);
|
||||
const u64 layer_stride = new_info.layer_stride;
|
||||
const u64 new_size = layer_stride * new_info.resources.layers;
|
||||
const u64 diff = overlap.gpu_addr - gpu_addr;
|
||||
if (diff > new_size) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const s32 base_layer = diff / layer_stride;
|
||||
const s32 mip_offset = diff % layer_stride;
|
||||
const s32 base_layer = static_cast<s32>(diff / layer_stride);
|
||||
const s32 mip_offset = static_cast<s32>(diff % layer_stride);
|
||||
const std::array offsets = CalculateMipLevelOffsets(new_info);
|
||||
const auto end = offsets.begin() + new_info.resources.levels;
|
||||
const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset));
|
||||
|
||||
@@ -271,7 +271,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
.tessellationShader = true,
|
||||
.sampleRateShading = true,
|
||||
.dualSrcBlend = true,
|
||||
.logicOp = false,
|
||||
.logicOp = true,
|
||||
.multiDrawIndirect = false,
|
||||
.drawIndirectFirstInstance = false,
|
||||
.depthClamp = true,
|
||||
@@ -433,6 +433,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
|
||||
}
|
||||
|
||||
VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart;
|
||||
if (is_topology_list_restart_supported || is_patch_list_restart_supported) {
|
||||
primitive_topology_list_restart = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.primitiveTopologyListRestart = is_topology_list_restart_supported,
|
||||
.primitiveTopologyPatchListRestart = is_patch_list_restart_supported,
|
||||
};
|
||||
SetNext(next, primitive_topology_list_restart);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support list topology primitive restart");
|
||||
}
|
||||
|
||||
VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
|
||||
if (ext_transform_feedback) {
|
||||
transform_feedback = {
|
||||
@@ -625,15 +638,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_vertex_input_dynamic_state && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
||||
const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
|
||||
if (ext_vertex_input_dynamic_state && is_intel_windows) {
|
||||
LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
|
||||
ext_vertex_input_dynamic_state = false;
|
||||
}
|
||||
if (is_float16_supported && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
||||
if (is_float16_supported && is_intel_windows) {
|
||||
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
|
||||
LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math");
|
||||
is_float16_supported = false;
|
||||
}
|
||||
if (is_intel_windows) {
|
||||
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
|
||||
cant_blit_msaa = true;
|
||||
}
|
||||
|
||||
supports_d24_depth =
|
||||
IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
@@ -891,6 +909,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
bool has_ext_provoking_vertex{};
|
||||
bool has_ext_vertex_input_dynamic_state{};
|
||||
bool has_ext_line_rasterization{};
|
||||
bool has_ext_primitive_topology_list_restart{};
|
||||
for (const std::string& extension : supported_extensions) {
|
||||
const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||
bool push) {
|
||||
@@ -915,6 +934,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
|
||||
test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
|
||||
test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
|
||||
test(has_ext_primitive_topology_list_restart,
|
||||
VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, true);
|
||||
test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true);
|
||||
test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
|
||||
true);
|
||||
@@ -1113,6 +1134,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
khr_pipeline_executable_properties = true;
|
||||
}
|
||||
}
|
||||
if (has_ext_primitive_topology_list_restart) {
|
||||
VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart{};
|
||||
primitive_topology_list_restart.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
|
||||
primitive_topology_list_restart.pNext = nullptr;
|
||||
features.pNext = &primitive_topology_list_restart;
|
||||
physical.GetFeatures2KHR(features);
|
||||
|
||||
is_topology_list_restart_supported =
|
||||
primitive_topology_list_restart.primitiveTopologyListRestart;
|
||||
is_patch_list_restart_supported =
|
||||
primitive_topology_list_restart.primitiveTopologyPatchListRestart;
|
||||
}
|
||||
if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
|
||||
extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
|
||||
|
||||
@@ -238,6 +238,16 @@ public:
|
||||
return khr_workgroup_memory_explicit_layout;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
|
||||
bool IsTopologyListPrimitiveRestartSupported() const {
|
||||
return is_topology_list_restart_supported;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
|
||||
bool IsPatchListPrimitiveRestartSupported() const {
|
||||
return is_patch_list_restart_supported;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_index_type_uint8.
|
||||
bool IsExtIndexTypeUint8Supported() const {
|
||||
return ext_index_type_uint8;
|
||||
@@ -340,6 +350,10 @@ public:
|
||||
return supports_d24_depth;
|
||||
}
|
||||
|
||||
bool CantBlitMSAA() const {
|
||||
return cant_blit_msaa;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Checks if the physical device is suitable.
|
||||
void CheckSuitability(bool requires_swapchain) const;
|
||||
@@ -401,6 +415,9 @@ private:
|
||||
bool is_shader_int16_supported{}; ///< Support for int16.
|
||||
bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
|
||||
bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
|
||||
bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
|
||||
///< topologies.
|
||||
bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
|
||||
bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
|
||||
bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
|
||||
bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
|
||||
@@ -430,6 +447,7 @@ private:
|
||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
||||
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
|
||||
@@ -251,6 +251,9 @@ target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
||||
if (NOT WIN32)
|
||||
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(yuzu PRIVATE Qt5::DBus)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(yuzu PRIVATE
|
||||
# Use QStringBuilder for string concatenation to reduce
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
@@ -34,7 +33,7 @@ void UpdateController(Core::HID::EmulatedController* controller,
|
||||
}
|
||||
controller->SetNpadStyleIndex(controller_type);
|
||||
if (connected) {
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,7 +663,5 @@ void QtControllerSelector::ReconfigureControllers(
|
||||
}
|
||||
|
||||
void QtControllerSelector::MainWindowReconfigureFinished() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDateTime>
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/qt_error.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
@@ -57,7 +56,5 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
|
||||
}
|
||||
|
||||
void QtErrorDisplay::MainWindowFinishedError() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/qt_profile_select.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
@@ -170,7 +169,5 @@ void QtProfileSelector::SelectProfile(
|
||||
}
|
||||
|
||||
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
callback(uuid);
|
||||
}
|
||||
|
||||
@@ -776,6 +776,7 @@ void Config::ReadUIGamelistValues() {
|
||||
ReadBasicSetting(UISettings::values.row_1_text_id);
|
||||
ReadBasicSetting(UISettings::values.row_2_text_id);
|
||||
ReadBasicSetting(UISettings::values.cache_game_list);
|
||||
ReadBasicSetting(UISettings::values.favorites_expanded);
|
||||
const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
|
||||
for (int i = 0; i < favorites_size; i++) {
|
||||
qt_config->setArrayIndex(i);
|
||||
@@ -1300,6 +1301,7 @@ void Config::SaveUIGamelistValues() {
|
||||
WriteBasicSetting(UISettings::values.row_1_text_id);
|
||||
WriteBasicSetting(UISettings::values.row_2_text_id);
|
||||
WriteBasicSetting(UISettings::values.cache_game_list);
|
||||
WriteBasicSetting(UISettings::values.favorites_expanded);
|
||||
qt_config->beginWriteArray(QStringLiteral("favorites"));
|
||||
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
|
||||
qt_config->setArrayIndex(i);
|
||||
|
||||
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
|
||||
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
|
||||
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
|
||||
ui->dump_shaders->setEnabled(runtime_lock);
|
||||
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
|
||||
ui->disable_macro_jit->setEnabled(runtime_lock);
|
||||
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
|
||||
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
|
||||
@@ -73,6 +75,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
|
||||
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
|
||||
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
|
||||
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
|
||||
Settings::values.disable_shader_loop_safety_checks =
|
||||
ui->disable_loop_safety_checks->isChecked();
|
||||
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
|
||||
|
||||
@@ -105,6 +105,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="dump_shaders">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Game Shaders</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="disable_macro_jit">
|
||||
<property name="enabled">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user