diff options
Diffstat (limited to 'src')
103 files changed, 1486 insertions, 943 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 919da4a53..790193b00 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -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 diff --git a/src/common/settings.h b/src/common/settings.h index 313f1fa7f..d01c0448c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -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"}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 49bed614a..b1a746727 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/core.cpp b/src/core/core.cpp index aa96f709b..3f9a7f44b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -317,6 +317,8 @@ struct System::Impl { is_powered_on = false; exit_lock = false; + gpu_core->NotifyShutdown(); + services.reset(); service_manager.reset(); cheat_engine.reset(); diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 6dbd38ffa..e1033b634 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -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!"); } diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index 685ec080c..08f8af551 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -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); } diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 3afd284d5..707419102 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -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{}; }; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 93372445b..71fc05807 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -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() { @@ -884,15 +879,42 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) if (!is_connected) { return; } - if (!IsControllerSupported()) { - LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", - npad_type); - Disconnect(); + if (IsControllerSupported()) { + return; } + + Disconnect(); + + // Fallback fullkey controllers to Pro controllers + if (IsControllerFullkey() && supported_style_tag.fullkey) { + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); + SetNpadStyleIndex(NpadStyleIndex::ProController); + Connect(); + return; + } + + LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", + npad_type); } -bool EmulatedController::IsControllerSupported() const { - switch (npad_type) { +bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { + const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; + switch (type) { + case NpadStyleIndex::ProController: + case NpadStyleIndex::GameCube: + case NpadStyleIndex::NES: + case NpadStyleIndex::SNES: + case NpadStyleIndex::N64: + case NpadStyleIndex::SegaGenesis: + return true; + default: + return false; + } +} + +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 +942,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; } { diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index e42aafebc..c0994ab4d 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -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(); @@ -318,10 +321,17 @@ private: void LoadTASParams(); /** + * @param use_temporary_value If true tmp_npad_type will be used + * @return true if the controller style is fullkey + */ + bool IsControllerFullkey(bool use_temporary_value = false) const; + + /** * 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 diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 7c12f01fc..4eca68533 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -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{}; diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp index c25fea966..a23f192d7 100644 --- a/src/core/hid/motion_input.cpp +++ b/src/core/hid/motion_input.cpp @@ -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; } diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h index 5b5b420bb..bca4520fa 100644 --- a/src/core/hid/motion_input.h +++ b/src/core/hid/motion_input.h @@ -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; diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 4f4e338e3..baad2c5d6 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -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(); } } diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp new file mode 100644 index 000000000..e5dd39751 --- /dev/null +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -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 diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h new file mode 100644 index 000000000..05924801e --- /dev/null +++ b/src/core/hle/kernel/k_interrupt_manager.h @@ -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 diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index fd491146f..9e51c33ce 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -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, diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 99982e5a3..4da509224 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -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; - return ResultSuccess; -} -ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) { + // Only process page tables are allowed to set heap size. + ASSERT(!this->IsKernel()); - if (size > heap_region_end - heap_region_start) { - return ResultOutOfMemory; - } + max_heap_size = size; - const u64 previous_heap_size{GetHeapSize()}; - - UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented"); + return ResultSuccess; +} - // Increase the heap 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{}; { - std::lock_guard lock{page_table_lock}; - - const u64 delta{size - previous_heap_size}; - - // Reserve memory for the heap extension. - KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, - delta); - - if (!memory_reservation.Succeeded()) { - LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta); - return ResultLimitReached; + // Lock the table. + std::lock_guard lk(page_table_lock); + + // 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(); } + } - KPageLinkedList page_linked_list; - const std::size_t num_pages{delta / PageSize}; + // Reserve memory for the heap extension. + KScopedResourceReservation memory_reservation( + system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, + allocation_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); - CASCADE_CODE( - system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool)); + // Allocate pages for the heap extension. + KPageLinkedList page_linked_list; + R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, + memory_pool)); - if (IsRegionMapped(current_heap_addr, delta)) { - return ResultInvalidCurrentMemory; + // Map the pages. + { + // Lock the table. + std::lock_guard lk(page_table_lock); + + // Ensure that the heap hasn't changed since we began executing. + ASSERT(cur_address == current_heap_end); + + // 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)); + + // 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); } - 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; - return heap_region_start; + // Set the output. + *out = heap_region_start; + return ResultSuccess; + } } 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(); } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index d784aa67e..564410dca 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -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{}; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 90dda40dc..bf98a51e2 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -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); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index cb93c7e24..e7c8b5838 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -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() { diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 277201de4..31cec990e 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -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(); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 752592e2e..71e029a3f 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -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}; diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index c8a08bd71..83dfde69b 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -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(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 2e4e4cb1c..1225e1fba 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -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; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a9f7438ea..250ef9042 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -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); - - // 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; - } - - if (heap_size >= 0x200000000) { - LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size); - return ResultInvalidSize; - } +static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { + LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); - auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; + // Validate size. + R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); + R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); - 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; - } - - 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 input handle is invalid. + R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); - const auto& scheduler = *system.Kernel().CurrentScheduler(); - const auto* const idle_thread = scheduler.GetIdleThread(); + // 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); - *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"}, diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 60ea2c405..25de6e437 100644 --- a/src/core/hle/kernel/svc_common.h +++ b/src/core/hle/kernel/svc_common.h @@ -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 { diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 86255fe6d..a60adfcab 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -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) { diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp deleted file mode 100644 index be4bfce3b..000000000 --- a/src/core/hle/lock.cpp +++ /dev/null @@ -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; -} diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h deleted file mode 100644 index 5c99fe996..000000000 --- a/src/core/hle/lock.h +++ /dev/null @@ -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 diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index 4c7d3bb6e..ee49edbb9 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -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)) {} diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 59c6d4740..63833c927 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -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. diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp index f0f3105dc..a727b3582 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp @@ -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 diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h index 279241858..26d153f0c 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/console_sixaxis.h @@ -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{}; }; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 2705e9dcb..e5c951e06 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -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; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 63281cb35..6b2872bad 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -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; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 7163e1a4e..6e12381fb 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -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; } }(); diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 693ffc71a..761d0d3c6 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -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; } diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index b4c3a6099..5fead6d1b 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -186,6 +186,10 @@ u32 BufferQueue::Query(QueryType type) { case QueryType::NativeWindowWidth: case QueryType::NativeWindowHeight: break; + case QueryType::NativeWindowMinUndequeuedBuffers: + return 0; + case QueryType::NativeWindowConsumerUsageBits: + return 0; } UNIMPLEMENTED_MSG("Unimplemented query type={}", type); return 0; diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 7b7baeaea..f2a579133 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -57,6 +57,11 @@ public: NativeWindowWidth = 0, NativeWindowHeight = 1, NativeWindowFormat = 2, + /// The minimum number of buffers that must remain un-dequeued after a buffer has been + /// queued + NativeWindowMinUndequeuedBuffers = 3, + /// The consumer gralloc usage bits currently set by the consumer + NativeWindowConsumerUsageBits = 10, }; explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index a22811ec1..01e69de30 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -100,9 +100,6 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { LOG_DEBUG(Service, "Opening \"{}\" display", name); - // TODO(Subv): Currently we only support the Default display. - ASSERT(name == "Default"); - const auto itr = std::find_if(displays.begin(), displays.end(), [&](const VI::Display& display) { return display.GetName() == name; }); diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 3ae9e6e0e..99ed34b00 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -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); diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 4ab991a7d..a1ce4525d 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -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; - - 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); - - complete_event.Set(); - } - }}; + 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); + } + 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(); + } + }}; Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h index bcba12c58..2d5d54ddb 100644 --- a/src/input_common/helpers/udp_protocol.h +++ b/src/input_common/helpers/udp_protocol.h @@ -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 diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index 081b2c8e0..7434a1f92 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -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: @@ -113,19 +113,35 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle); break; case IR::Attribute::InstanceId: - ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name); + ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name); break; case IR::Attribute::VertexId: - ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name); + ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name); break; case IR::Attribute::FrontFace: - ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name); + 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; + default: + throw NotImplementedException("Get U32 attribute {}", attr); + } +} + void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, [[maybe_unused]] ScalarU32 vertex) { const u32 element{static_cast<u32>(attr) % 4}; diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index 1f343bff5..b48007856 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h @@ -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); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp index 78b2eeaa2..b6b17a330 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp @@ -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 ""; diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp index 0f2668d9e..e0ead7a53 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp @@ -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); } } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 1920047f4..0c1fbc7b1 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -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: diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp index b765a251b..474189d87 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp @@ -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) { diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index f86502e4c..6cabbc717 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h @@ -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, diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp index 44060df33..b0d85be99 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp @@ -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) { diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp index b8ddafe48..fcf620b79 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp @@ -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) { diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 1de017e76..bb7f1a0fd 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -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; } } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 6ce7ed12a..50918317f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -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>) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index ad84966b5..8ea730c80 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -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) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 6cd22dd3e..887112deb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -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); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 4b6f792bf..d3ba66569 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -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) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 63f8185d9..f87138f7e 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -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{}; diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index b4df73e8a..db16429d4 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -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; } diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h index ca1199494..3bbd38a03 100644 --- a/src/shader_recompiler/frontend/ir/attribute.h +++ b/src/shader_recompiler/frontend/ir/attribute.h @@ -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); diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 6929919df..b94ce7406 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc @@ -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, ) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 267ebe4af..248ad3ced 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -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 diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index a84814811..cd535f20d 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -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 diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 1e476d83d..a78c469be 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp @@ -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: diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp index d089fdd12..c134a12bc 100644 --- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp @@ -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) { diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index f0c3b3b17..dc4c806ff 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -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{}; diff --git a/src/shader_recompiler/varying_state.h b/src/shader_recompiler/varying_state.h index 9d7b24a76..bc4f273c8 100644 --- a/src/shader_recompiler/varying_state.h +++ b/src/shader_recompiler/varying_state.h @@ -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 { diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c4c012f3d..4a20c0768 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -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) diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp new file mode 100644 index 000000000..8c77d81e9 --- /dev/null +++ b/src/tests/input_common/calibration_configuration_job.cpp @@ -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); +} diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 2a532b883..04d0f3a2f 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp @@ -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 @@ -130,6 +130,12 @@ bool Codec::CreateGpuAvDevice() { } if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { av_codec_ctx->pix_fmt = config->pix_fmt; + if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { + // skip zero-copy decoders, we don't currently support them + LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", + av_hwdevice_get_type_name(type), config->methods); + continue; + } LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); return true; } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8788f5148..705765c99 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -17,7 +17,6 @@ #include "core/frontend/emu_window.h" #include "core/hardware_interrupt_manager.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/perf_stats.h" #include "video_core/cdma_pusher.h" #include "video_core/dma_pusher.h" @@ -312,6 +311,12 @@ struct GPU::Impl { cpu_context->MakeCurrent(); } + void NotifyShutdown() { + std::unique_lock lk{sync_mutex}; + shutting_down.store(true, std::memory_order::relaxed); + sync_cv.notify_all(); + } + /// Obtain the CPU Context void ObtainContext() { cpu_context->MakeCurrent(); @@ -859,6 +864,10 @@ void GPU::Start() { impl->Start(); } +void GPU::NotifyShutdown() { + impl->NotifyShutdown(); +} + void GPU::ObtainContext() { impl->ObtainContext(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 500411176..3188b83ed 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -232,6 +232,9 @@ public: /// core timing events. void Start(); + /// Performs any additional necessary steps to shutdown GPU emulation. + void NotifyShutdown(); + /// Obtain the CPU Context void ObtainContext(); diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 0764ea6e0..e62912a22 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -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; } } diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index de9e41659..95c2e8d38 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -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; }; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 42ef67628..f71e01a34 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -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(); diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 14e6522f2..3c1f79a27 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -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)) { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 37d5e6a6b..7f425631f 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -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, diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 28daacd82..f81c1b233 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -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()); diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 9a38b6b34..2c3914459 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -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); - - // 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(); -} + const VkExtent2D extent = GetConversionExtent(src_image_view); -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 diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index b1a717090..85e7dca5b 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -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; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 1e447e621..c71a1f44d 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -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); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 616a7b457..d514b71d0 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -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 = {}, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index eb8b4e08b..a633b73e5 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -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); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 197cba8e3..0ba56ff1e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -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, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 753e3e8a1..c81130dd2 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -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; diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 05850afd0..7d3ae0de4 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -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); diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 6640e53d0..aae762b27 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -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, diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 5aaeb16ca..b494152b8 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1397,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; } @@ -1855,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); } } diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 7bd31b211..d8e19cb2f 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -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)); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 7bf5b6578..3d78efddc 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -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); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 10653ac6b..37d140ebd 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -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. diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 732e8c276..30902101d 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -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 diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index c5685db2e..4239c17f5 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -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); } } @@ -401,36 +400,66 @@ void QtControllerSelectorDialog::SetSupportedControllers() { } void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) { + const auto npad_style_set = system.HIDCore().GetSupportedStyleTag(); auto& pairs = index_controller_type_pairs[player_index]; pairs.clear(); emulated_controllers[player_index]->clear(); - pairs.emplace_back(emulated_controllers[player_index]->count(), - Core::HID::NpadStyleIndex::ProController); - emulated_controllers[player_index]->addItem(tr("Pro Controller")); + const auto add_item = [&](Core::HID::NpadStyleIndex controller_type, + const QString& controller_name) { + pairs.emplace_back(emulated_controllers[player_index]->count(), controller_type); + emulated_controllers[player_index]->addItem(controller_name); + }; - pairs.emplace_back(emulated_controllers[player_index]->count(), - Core::HID::NpadStyleIndex::JoyconDual); - emulated_controllers[player_index]->addItem(tr("Dual Joycons")); + if (npad_style_set.fullkey == 1) { + add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); + } - pairs.emplace_back(emulated_controllers[player_index]->count(), - Core::HID::NpadStyleIndex::JoyconLeft); - emulated_controllers[player_index]->addItem(tr("Left Joycon")); + if (npad_style_set.joycon_dual == 1) { + add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons")); + } - pairs.emplace_back(emulated_controllers[player_index]->count(), - Core::HID::NpadStyleIndex::JoyconRight); - emulated_controllers[player_index]->addItem(tr("Right Joycon")); + if (npad_style_set.joycon_left == 1) { + add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon")); + } - if (player_index == 0) { - pairs.emplace_back(emulated_controllers[player_index]->count(), - Core::HID::NpadStyleIndex::Handheld); - emulated_controllers[player_index]->addItem(tr("Handheld")); + if (npad_style_set.joycon_right == 1) { + add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon")); } - pairs.emplace_back(emulated_controllers[player_index]->count(), - Core::HID::NpadStyleIndex::GameCube); - emulated_controllers[player_index]->addItem(tr("GameCube Controller")); + if (player_index == 0 && npad_style_set.handheld == 1) { + add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld")); + } + + if (npad_style_set.gamecube == 1) { + add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller")); + } + + // Disable all unsupported controllers + if (!Settings::values.enable_all_controllers) { + return; + } + + if (npad_style_set.palma == 1) { + add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus")); + } + + if (npad_style_set.lark == 1) { + add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller")); + } + + if (npad_style_set.lucia == 1) { + add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller")); + } + + if (npad_style_set.lagoon == 1) { + add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller")); + } + + if (npad_style_set.lager == 1) { + add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis")); + } } Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex( @@ -664,7 +693,5 @@ void QtControllerSelector::ReconfigureControllers( } void QtControllerSelector::MainWindowReconfigureFinished() { - // Acquire the HLE mutex - std::lock_guard lock(HLE::g_hle_lock); callback(); } diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp index 45cf64603..879e73660 100644 --- a/src/yuzu/applets/qt_error.cpp +++ b/src/yuzu/applets/qt_error.cpp @@ -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(); } diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index 7b19f1f8d..5b32da923 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp @@ -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); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 463d500c2..0f679c37e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -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); diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 633fc295b..c1cf4050c 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -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(); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 0f3b51c8d..4dd870855 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -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"> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 8a8be8e40..8c6249fc2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -599,11 +599,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i if (is_connected) { if (type == Core::HID::NpadStyleIndex::Handheld) { emulated_controller_p1->Disconnect(); - emulated_controller_handheld->Connect(); + emulated_controller_handheld->Connect(true); emulated_controller = emulated_controller_handheld; } else { emulated_controller_handheld->Disconnect(); - emulated_controller_p1->Connect(); + emulated_controller_p1->Connect(true); emulated_controller = emulated_controller_p1; } } @@ -718,7 +718,7 @@ void ConfigureInputPlayer::LoadConfiguration() { void ConfigureInputPlayer::ConnectPlayer(bool connected) { ui->groupConnectedController->setChecked(connected); if (connected) { - emulated_controller->Connect(); + emulated_controller->Connect(true); } else { emulated_controller->Disconnect(); } @@ -907,78 +907,63 @@ void ConfigureInputPlayer::UpdateUI() { } void ConfigureInputPlayer::SetConnectableControllers() { - Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag(); + const auto npad_style_set = hid_core.GetSupportedStyleTag(); index_controller_type_pairs.clear(); ui->comboControllerType->clear(); + const auto add_item = [&](Core::HID::NpadStyleIndex controller_type, + const QString& controller_name) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), controller_type); + ui->comboControllerType->addItem(controller_name); + }; + if (npad_style_set.fullkey == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::ProController); - ui->comboControllerType->addItem(tr("Pro Controller")); + add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); } if (npad_style_set.joycon_dual == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::JoyconDual); - ui->comboControllerType->addItem(tr("Dual Joycons")); + add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons")); } if (npad_style_set.joycon_left == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::JoyconLeft); - ui->comboControllerType->addItem(tr("Left Joycon")); + add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon")); } if (npad_style_set.joycon_right == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::JoyconRight); - ui->comboControllerType->addItem(tr("Right Joycon")); + add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon")); } if (player_index == 0 && npad_style_set.handheld == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::Handheld); - ui->comboControllerType->addItem(tr("Handheld")); + add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld")); } if (npad_style_set.gamecube == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::GameCube); - ui->comboControllerType->addItem(tr("GameCube Controller")); + add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller")); } // Disable all unsupported controllers if (!Settings::values.enable_all_controllers) { return; } + if (npad_style_set.palma == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::Pokeball); - ui->comboControllerType->addItem(tr("Poke Ball Plus")); + add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus")); } if (npad_style_set.lark == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::NES); - ui->comboControllerType->addItem(tr("NES Controller")); + add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller")); } if (npad_style_set.lucia == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::SNES); - ui->comboControllerType->addItem(tr("SNES Controller")); + add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller")); } if (npad_style_set.lagoon == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::N64); - ui->comboControllerType->addItem(tr("N64 Controller")); + add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller")); } if (npad_style_set.lager == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::SegaGenesis); - ui->comboControllerType->addItem(tr("Sega Genesis")); + add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis")); } } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 1a5e41588..8b5c4a10a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -173,13 +173,17 @@ void GameList::OnItemExpanded(const QModelIndex& item) { const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir; - - if (!is_dir) { + const bool is_fave = type == GameListItemType::Favorites; + if (!is_dir && !is_fave) { return; } - - UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = - tree_view->isExpanded(item); + const bool is_expanded = tree_view->isExpanded(item); + if (is_fave) { + UISettings::values.favorites_expanded = is_expanded; + return; + } + const int item_dir_index = item.data(GameListDir::GameDirRole).toInt(); + UISettings::values.game_dirs[item_dir_index].expanded = is_expanded; } // Event in order to filter the gamelist after editing the searchfield @@ -458,10 +462,13 @@ void GameList::DonePopulating(const QStringList& watch_list) { emit ShowList(!IsEmpty()); item_model->invisibleRootItem()->appendRow(new GameListAddDir()); + + // Add favorites row item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), UISettings::values.favorited_ids.size() == 0); - tree_view->expand(item_model->invisibleRootItem()->child(0)->index()); + tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(), + UISettings::values.favorites_expanded.GetValue()); for (const auto id : UISettings::values.favorited_ids) { AddFavorite(id); } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b7bb43348..53f11a9ac 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1236,11 +1236,58 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } } +#ifdef __linux__ +static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) { + if (!QDBusConnection::sessionBus().isConnected()) { + return {}; + } + // reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit + QDBusInterface xdp(QString::fromLatin1("org.freedesktop.portal.Desktop"), + QString::fromLatin1("/org/freedesktop/portal/desktop"), + QString::fromLatin1("org.freedesktop.portal.Inhibit")); + if (!xdp.isValid()) { + LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint"); + return {}; + } + QVariantMap options = {}; + //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the + //: computer from sleeping + options.insert(QString::fromLatin1("reason"), + QCoreApplication::translate("GMainWindow", "yuzu is running a game")); + // 0x4: Suspend lock; 0x8: Idle lock + QDBusReply<QDBusObjectPath> reply = + xdp.call(QString::fromLatin1("Inhibit"), + QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options); + + if (reply.isValid()) { + return reply.value(); + } + LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}", + reply.error().message().toStdString()); + return {}; +} + +static void ReleaseWakeLockLinux(QDBusObjectPath lock) { + if (!QDBusConnection::sessionBus().isConnected()) { + return; + } + QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(), + QString::fromLatin1("org.freedesktop.portal.Request")); + unlocker.call(QString::fromLatin1("Close")); +} +#endif // __linux__ + void GMainWindow::PreventOSSleep() { #ifdef _WIN32 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); #elif defined(HAVE_SDL2) SDL_DisableScreenSaver(); +#ifdef __linux__ + auto reply = HoldWakeLockLinux(winId()); + if (reply) { + wake_lock = std::move(reply.value()); + } +#endif #endif } @@ -1249,6 +1296,11 @@ void GMainWindow::AllowOSSleep() { SetThreadExecutionState(ES_CONTINUOUS); #elif defined(HAVE_SDL2) SDL_EnableScreenSaver(); +#ifdef __linux__ + if (!wake_lock.path().isEmpty()) { + ReleaseWakeLockLinux(wake_lock); + } +#endif #endif } @@ -1495,6 +1547,8 @@ void GMainWindow::ShutdownGame() { emu_thread->wait(); emu_thread = nullptr; + emulation_running = false; + discord_rpc->Update(); // The emulation is stopped, so closing the window or not does not matter anymore @@ -1533,8 +1587,6 @@ void GMainWindow::ShutdownGame() { emu_frametime_label->setVisible(false); renderer_status_button->setEnabled(true); - emulation_running = false; - game_path.clear(); // When closing the game, destroy the GLWindow to clear the context after the game is closed @@ -2546,39 +2598,30 @@ void GMainWindow::ToggleFullscreen() { } void GMainWindow::ShowFullscreen() { + const auto show_fullscreen = [](QWidget* window) { + if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + window->showFullScreen(); + return; + } + window->hide(); + window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); + const auto screen_geometry = QApplication::desktop()->screenGeometry(window); + window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), + screen_geometry.height() + 1); + window->raise(); + window->showNormal(); + }; + if (ui->action_Single_Window_Mode->isChecked()) { UISettings::values.geometry = saveGeometry(); ui->menubar->hide(); statusBar()->hide(); - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { - showFullScreen(); - return; - } - - hide(); - setWindowFlags(windowFlags() | Qt::FramelessWindowHint); - const auto screen_geometry = QApplication::desktop()->screenGeometry(this); - setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), - screen_geometry.height() + 1); - raise(); - showNormal(); + show_fullscreen(this); } else { UISettings::values.renderwindow_geometry = render_window->saveGeometry(); - - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { - render_window->showFullScreen(); - return; - } - - render_window->hide(); - render_window->setWindowFlags(windowFlags() | Qt::FramelessWindowHint); - const auto screen_geometry = QApplication::desktop()->screenGeometry(this); - render_window->setGeometry(screen_geometry.x(), screen_geometry.y(), - screen_geometry.width(), screen_geometry.height() + 1); - render_window->raise(); - render_window->showNormal(); + show_fullscreen(render_window); } } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 0fd41ed4f..7870bb963 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -17,6 +17,12 @@ #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" +#ifdef __linux__ +#include <QVariant> +#include <QtDBus/QDBusInterface> +#include <QtDBus/QtDBus> +#endif + class Config; class EmuThread; class GameList; @@ -394,6 +400,9 @@ private: // Applets QtSoftwareKeyboardDialog* software_keyboard = nullptr; +#ifdef __linux__ + QDBusObjectPath wake_lock{}; +#endif protected: void dropEvent(QDropEvent* event) override; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 936914ef3..a610e7e25 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -74,7 +74,6 @@ struct Values { QString game_dir_deprecated; bool game_dir_deprecated_deepscan; QVector<UISettings::GameDir> game_dirs; - QVector<u64> favorited_ids; QStringList recent_files; QString language; @@ -96,6 +95,8 @@ struct Values { Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; std::atomic_bool is_game_list_reload_pending{false}; Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; + Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"}; + QVector<u64> favorited_ids; bool configuration_applied; bool reset_to_defaults; |