Compare commits

..

16 Commits

Author SHA1 Message Date
Lioncash
e0ec9ffc36 audio_in/out_system: Pass Initialize members by value where applicable
applet_resource_user_id isn't actually modified and is just assigned to
a member variable, so this doesn't need to be a mutable reference.

Similarly, the device name itself isn't modified and is only moved. We
pass by value here, since we can still perform the move, but eliminate a
sneaky set of calls that can unintentionally destroy the original
string. Given how nested the calls are, it's good to get rid of this
potential vector for a use-after-move bug.
2022-10-26 11:21:48 -04:00
Mai
041eb5bf57 Merge pull request #9131 from Morph1984/contiguous
concepts: Use the std::contiguous_iterator concept
2022-10-26 15:12:48 +00:00
Morph
8b4d5aeb4f concepts: Use the std::contiguous_iterator concept
This also covers std::span, which does not have a const iterator.

Also renames IsSTLContainer to IsContiguousContainer to explicitly convey its semantics.
2022-10-26 00:41:54 -04:00
liamwhite
d8e3380ea5 Merge pull request #9128 from abouvier/patch-1
tests: fix for -Wall
2022-10-25 12:36:25 -04:00
liamwhite
e59bd6c335 Merge pull request #9113 from german77/peer_pressure
service: nfp: Allow amiibos without keys
2022-10-25 12:13:31 -04:00
liamwhite
77803d96be Merge pull request #9107 from german77/gidoly_rules
input_common: cache vibration tests
2022-10-25 12:13:18 -04:00
Alexandre Bouvier
fa9b7db76f tests: fix for -Wall
Fix #9123
2022-10-25 15:20:23 +00:00
liamwhite
fa913a702f Merge pull request #9112 from vonchenplus/deferred_draw
video_core: Reimplementing the maxwell drawing trigger mechanism
2022-10-25 09:42:59 -04:00
liamwhite
3c38bd7cf0 Merge pull request #9119 from liamwhite/shutdown-barrier
core: barrier service thread shutdown
2022-10-25 06:45:51 -04:00
Liam
2d90a927c9 core: barrier service thread shutdown 2022-10-23 05:45:45 -04:00
german77
2ccbf5abdd core: hid: Add handheld to nfc devices 2022-10-22 14:05:00 -05:00
FengChen
2f90694797 video_core: Implement maxwell inline_index method 2022-10-22 16:58:23 +08:00
Narr the Reg
3e0aaeba98 service: nfp: Allow amiibos without keys 2022-10-21 23:08:07 -05:00
Narr the Reg
82fdfb33ac service: nfp: remove unnecessary include 2022-10-21 21:54:31 -05:00
FengChen
1f54cd4ac7 video_coare: Reimplementing the maxwell drawing trigger mechanism 2022-10-21 17:09:22 +08:00
german77
7f66050f0c input_common: cache vibration tests 2022-10-21 00:24:08 -05:00
46 changed files with 465 additions and 346 deletions

View File

@@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name,
return ResultSuccess;
}
Result System::Initialize(std::string& device_name, const AudioInParameter& in_params,
Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
const u32 handle_, const u64 applet_resource_user_id_) {
auto result{IsConfigValid(device_name, in_params)};
if (result.IsError()) {

View File

@@ -97,7 +97,7 @@ public:
* @param applet_resource_user_id - Unused.
* @return Result code.
*/
Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle,
Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle,
u64 applet_resource_user_id);
/**

View File

@@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name,
return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
}
Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_,
u64& applet_resource_user_id_) {
Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_,
u64 applet_resource_user_id_) {
auto result = IsConfigValid(device_name, in_params);
if (result.IsError()) {
return result;

View File

@@ -88,8 +88,8 @@ public:
* @param applet_resource_user_id - Unused.
* @return Result code.
*/
Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle,
u64& applet_resource_user_id);
Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle,
u64 applet_resource_user_id);
/**
* Start this system.

View File

@@ -3,24 +3,14 @@
#pragma once
#include <iterator>
#include <type_traits>
namespace Common {
// Check if type is like an STL container
// Check if type satisfies the ContiguousContainer named requirement.
template <typename T>
concept IsSTLContainer = requires(T t) {
typename T::value_type;
typename T::iterator;
typename T::const_iterator;
// TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
t.begin();
t.end();
t.cbegin();
t.cend();
t.data();
t.size();
};
concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
// TODO: Replace with std::derived_from when the <concepts> header
// is available on all supported platforms.

View File

@@ -209,8 +209,8 @@ public:
/**
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
* If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
* ReadObject and T must be a trivially copyable object.
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
* calls ReadObject and T must be a trivially copyable object.
*
* See ReadSpan for more details if T is a contiguous container.
* See ReadObject for more details if T is a trivially copyable object.
@@ -223,7 +223,7 @@ public:
*/
template <typename T>
[[nodiscard]] size_t Read(T& data) const {
if constexpr (IsSTLContainer<T>) {
if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
@@ -235,8 +235,8 @@ public:
/**
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
* If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
* WriteObject and T must be a trivially copyable object.
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
* calls WriteObject and T must be a trivially copyable object.
*
* See WriteSpan for more details if T is a contiguous container.
* See WriteObject for more details if T is a trivially copyable object.
@@ -249,7 +249,7 @@ public:
*/
template <typename T>
[[nodiscard]] size_t Write(const T& data) const {
if constexpr (IsSTLContainer<T>) {
if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");

View File

@@ -100,7 +100,6 @@ enum class CameraError {
enum class VibrationAmplificationType {
Linear,
Exponential,
Test,
};
// Analog properties for calibration
@@ -325,6 +324,10 @@ public:
return VibrationError::NotSupported;
}
virtual bool IsVibrationEnabled() {
return false;
}
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
return PollingError::NotSupported;
}

View File

@@ -384,6 +384,7 @@ struct System::Impl {
kernel.ShutdownCores();
cpu_manager.Shutdown();
debugger.reset();
services->KillNVNFlinger();
kernel.CloseServices();
services.reset();
service_manager.reset();

View File

@@ -970,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
Common::Input::VibrationError::None;
}
bool EmulatedController::TestVibration(std::size_t device_index) {
if (device_index >= output_devices.size()) {
return false;
}
if (!output_devices[device_index]) {
return false;
}
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
@@ -985,31 +978,15 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
return false;
}
const Common::Input::VibrationStatus test_vibration = {
.low_amplitude = 0.001f,
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = 0.001f,
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
.type = Common::Input::VibrationAmplificationType::Test,
};
if (device_index >= output_devices.size()) {
return false;
}
const Common::Input::VibrationStatus zero_vibration = {
.low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
.type = Common::Input::VibrationAmplificationType::Test,
};
if (!output_devices[device_index]) {
return false;
}
// Send a slight vibration to test for rumble support
output_devices[device_index]->SetVibration(test_vibration);
// Wait for about 15ms to ensure the controller is ready for the stop command
std::this_thread::sleep_for(std::chrono::milliseconds(15));
// Stop any vibration and return the result
return output_devices[device_index]->SetVibration(zero_vibration) ==
Common::Input::VibrationError::None;
return output_devices[device_index]->IsVibrationEnabled();
}
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
@@ -1048,6 +1025,7 @@ bool EmulatedController::HasNfc() const {
case NpadStyleIndex::JoyconRight:
case NpadStyleIndex::JoyconDual:
case NpadStyleIndex::ProController:
case NpadStyleIndex::Handheld:
break;
default:
return false;
@@ -1234,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const {
return is_connected;
}
bool EmulatedController::IsVibrationEnabled() const {
const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
return player.vibration_enabled;
}
NpadIdType EmulatedController::GetNpadIdType() const {
std::scoped_lock lock{mutex};
return npad_id_type;

View File

@@ -206,9 +206,6 @@ public:
*/
bool IsConnected(bool get_temporary_value = false) const;
/// Returns true if vibration is enabled
bool IsVibrationEnabled() const;
/// Removes all callbacks created from input devices
void UnloadInput();
@@ -339,7 +336,7 @@ public:
* Sends a small vibration to the output device
* @return true if SetVibration was successfull
*/
bool TestVibration(std::size_t device_index);
bool IsVibrationEnabled(std::size_t device_index);
/**
* Sets the desired data to be polled from a controller

View File

@@ -304,7 +304,7 @@ public:
*/
template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
if constexpr (Common::IsSTLContainer<T>) {
if constexpr (Common::IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Container to WriteBuffer must contain trivially copyable objects");

View File

@@ -48,8 +48,8 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system_, KernelCore& kernel_)
: time_manager{system_},
service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {}
: time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
service_thread_barrier{2}, system{system_} {}
void SetMulticore(bool is_multi) {
is_multicore = is_multi;
@@ -737,7 +737,12 @@ struct KernelCore::Impl {
}
void ClearServiceThreads() {
service_threads_manager.QueueWork([this]() { service_threads.clear(); });
service_threads_manager.QueueWork([this] {
service_threads.clear();
default_service_thread.reset();
service_thread_barrier.Sync();
});
service_thread_barrier.Sync();
}
std::mutex server_objects_lock;
@@ -802,6 +807,7 @@ struct KernelCore::Impl {
std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
std::weak_ptr<ServiceThread> default_service_thread;
Common::ThreadWorker service_threads_manager;
Common::Barrier service_thread_barrier;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};

View File

@@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn;
class IAudioIn final : public ServiceFramework<IAudioIn> {
public:
explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
std::string& device_name, const AudioInParameter& in_params, u32 handle,
const std::string& device_name, const AudioInParameter& in_params, u32 handle,
u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioIn"},
service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},

View File

@@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut;
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
size_t session_id, std::string& device_name,
size_t session_id, const std::string& device_name,
const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(

View File

@@ -868,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
return false;
}
if (!controller.device->IsVibrationEnabled()) {
if (!controller.device->IsVibrationEnabled(device_index)) {
if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
// Send an empty vibration to stop any vibrations.
@@ -1001,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
}
controller.vibration[device_index].device_mounted =
controller.device->TestVibration(device_index);
controller.device->IsVibrationEnabled(device_index);
}
void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {

View File

@@ -9,6 +9,7 @@
#include <mbedtls/hmac_drbg.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/hle/service/mii/mii_manager.h"
@@ -279,7 +280,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
Common::FS::FileType::BinaryFile};
if (!keys_file.IsOpen()) {
LOG_ERROR(Service_NFP, "No keys detected");
LOG_ERROR(Service_NFP, "Failed to open key file");
return false;
}
@@ -295,6 +296,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
return true;
}
bool IsKeyAvailable() {
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin");
}
bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
InternalKey locked_secret{};
InternalKey unfixed_info{};

View File

@@ -91,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
/// Loads both amiibo keys from key_retail.bin
bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
/// Returns true if key_retail.bin exist
bool IsKeyAvailable();
/// Decodes encripted amiibo data returns true if output is valid
bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);

View File

@@ -17,6 +17,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_device.h"
@@ -233,6 +234,14 @@ Result NfpDevice::Mount(MountTarget mount_target_) {
return NotAnAmiibo;
}
// Mark amiibos as read only when keys are missing
if (!AmiiboCrypto::IsKeyAvailable()) {
LOG_ERROR(Service_NFP, "No keys detected");
device_state = DeviceState::TagMounted;
mount_target = MountTarget::Rom;
return ResultSuccess;
}
if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
return CorruptedData;

View File

@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"

View File

@@ -17,11 +17,6 @@ enum class ServiceType : u32 {
System,
};
enum class State : u32 {
NonInitialized,
Initialized,
};
enum class DeviceState : u32 {
Initialized,
SearchingForTag,

View File

@@ -6,12 +6,9 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/nfp/nfp_user.h"

View File

@@ -4,8 +4,7 @@
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Service::NFP {
class NfpDevice;
@@ -15,6 +14,11 @@ public:
explicit IUser(Core::System& system_);
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
void Initialize(Kernel::HLERequestContext& ctx);
void Finalize(Kernel::HLERequestContext& ctx);
void ListDevices(Kernel::HLERequestContext& ctx);

View File

@@ -102,15 +102,19 @@ NVFlinger::~NVFlinger() {
system.CoreTiming().UnscheduleEvent(single_composition_event, {});
}
ShutdownLayers();
if (nvdrv) {
nvdrv->Close(disp_fd);
}
}
void NVFlinger::ShutdownLayers() {
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).Core().NotifyShutdown();
}
}
if (nvdrv) {
nvdrv->Close(disp_fd);
}
}
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {

View File

@@ -48,6 +48,8 @@ public:
explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
~NVFlinger();
void ShutdownLayers();
/// Sets the NVDrv module instance to use to send buffers to the GPU.
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);

View File

@@ -303,4 +303,8 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Services::~Services() = default;
void Services::KillNVNFlinger() {
nv_flinger->ShutdownLayers();
}
} // namespace Service

View File

@@ -238,6 +238,8 @@ public:
explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
~Services();
void KillNVNFlinger();
private:
std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;

View File

@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
return true;
}
Common::Input::VibrationError GCAdapter::SetRumble(
Common::Input::VibrationError GCAdapter::SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
const auto processed_amplitude =
@@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble(
return Common::Input::VibrationError::None;
}
bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
return rumble_enabled;
}
void GCAdapter::UpdateVibrations() {
// Use 8 states to keep the switching between on/off fast enough for
// a human to feel different vibration strenght

View File

@@ -25,9 +25,11 @@ public:
explicit GCAdapter(std::string input_engine_);
~GCAdapter() override;
Common::Input::VibrationError SetRumble(
Common::Input::VibrationError SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
/// Used for automapping features
std::vector<Common::ParamPackage> GetInputDevices() const override;
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;

View File

@@ -114,6 +114,20 @@ public:
}
return false;
}
void EnableVibration(bool is_enabled) {
has_vibration = is_enabled;
is_vibration_tested = true;
}
bool HasVibration() const {
return has_vibration;
}
bool IsVibrationTested() const {
return is_vibration_tested;
}
/**
* The Pad identifier of the joystick
*/
@@ -236,6 +250,8 @@ private:
u64 last_motion_update{};
bool has_gyro{false};
bool has_accel{false};
bool has_vibration{false};
bool is_vibration_tested{false};
BasicMotion motion;
};
@@ -517,7 +533,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
return devices;
}
Common::Input::VibrationError SDLDriver::SetRumble(
Common::Input::VibrationError SDLDriver::SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
const auto joystick =
GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -546,13 +562,6 @@ Common::Input::VibrationError SDLDriver::SetRumble(
.type = Common::Input::VibrationAmplificationType::Exponential,
};
if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
if (!joystick->RumblePlay(new_vibration)) {
return Common::Input::VibrationError::Unknown;
}
return Common::Input::VibrationError::None;
}
vibration_queue.Push(VibrationRequest{
.identifier = identifier,
.vibration = new_vibration,
@@ -561,6 +570,45 @@ Common::Input::VibrationError SDLDriver::SetRumble(
return Common::Input::VibrationError::None;
}
bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
const auto joystick =
GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
constexpr Common::Input::VibrationStatus test_vibration{
.low_amplitude = 1,
.low_frequency = 160.0f,
.high_amplitude = 1,
.high_frequency = 320.0f,
.type = Common::Input::VibrationAmplificationType::Exponential,
};
constexpr Common::Input::VibrationStatus zero_vibration{
.low_amplitude = 0,
.low_frequency = 160.0f,
.high_amplitude = 0,
.high_frequency = 320.0f,
.type = Common::Input::VibrationAmplificationType::Exponential,
};
if (joystick->IsVibrationTested()) {
return joystick->HasVibration();
}
// First vibration might fail
joystick->RumblePlay(test_vibration);
// Wait for about 15ms to ensure the controller is ready for the stop command
std::this_thread::sleep_for(std::chrono::milliseconds(15));
if (!joystick->RumblePlay(zero_vibration)) {
joystick->EnableVibration(false);
return false;
}
joystick->EnableVibration(true);
return true;
}
void SDLDriver::SendVibrations() {
while (!vibration_queue.Empty()) {
VibrationRequest request;

View File

@@ -61,9 +61,11 @@ public:
bool IsStickInverted(const Common::ParamPackage& params) override;
Common::Input::VibrationError SetRumble(
Common::Input::VibrationError SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
private:
struct VibrationRequest {
PadIdentifier identifier;

View File

@@ -108,12 +108,17 @@ public:
[[maybe_unused]] const Common::Input::LedStatus& led_status) {}
// Sets rumble to a controller
virtual Common::Input::VibrationError SetRumble(
virtual Common::Input::VibrationError SetVibration(
[[maybe_unused]] const PadIdentifier& identifier,
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
return Common::Input::VibrationError::NotSupported;
}
// Returns true if device supports vibrations
virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
return false;
}
// Sets polling mode to a controller
virtual Common::Input::PollingError SetPollingMode(
[[maybe_unused]] const PadIdentifier& identifier,

View File

@@ -763,7 +763,11 @@ public:
Common::Input::VibrationError SetVibration(
const Common::Input::VibrationStatus& vibration_status) override {
return input_engine->SetRumble(identifier, vibration_status);
return input_engine->SetVibration(identifier, vibration_status);
}
bool IsVibrationEnabled() override {
return input_engine->IsVibrationEnabled(identifier);
}
Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {

View File

@@ -325,6 +325,11 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
phi_args.emplace_back(predecessor, value);
}
void Inst::ErasePhiOperand(size_t index) {
const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
phi_args.erase(operand_it);
}
void Inst::OrderPhiArgs() {
if (op != Opcode::Phi) {
throw LogicError("{} is not a Phi instruction", op);

View File

@@ -178,9 +178,13 @@ public:
/// Get a pointer to the block of a phi argument.
[[nodiscard]] Block* PhiBlock(size_t index) const;
/// Add phi operand to a phi instruction.
void AddPhiOperand(Block* predecessor, const Value& value);
// Erase the phi operand at the given index.
void ErasePhiOperand(size_t index);
/// Orders the Phi arguments from farthest away to nearest.
void OrderPhiArgs();

View File

@@ -1,25 +1,105 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <boost/container/small_vector.hpp>
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/ir_opt/passes.h"
namespace Shader::Optimization {
void DeadCodeEliminationPass(IR::Program& program) {
namespace {
template <bool TEST_USES>
void DeadInstElimination(IR::Block* const block) {
// We iterate over the instructions in reverse order.
// This is because removing an instruction reduces the number of uses for earlier instructions.
for (IR::Block* const block : program.post_order_blocks) {
auto it{block->end()};
while (it != block->begin()) {
--it;
if (!it->HasUses() && !it->MayHaveSideEffects()) {
it->Invalidate();
it = block->Instructions().erase(it);
auto it{block->end()};
while (it != block->begin()) {
--it;
if constexpr (TEST_USES) {
if (it->HasUses() || it->MayHaveSideEffects()) {
continue;
}
}
it->Invalidate();
it = block->Instructions().erase(it);
}
}
void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
for (IR::Block* const block : program.blocks) {
for (IR::Inst& phi : *block) {
if (!IR::IsPhi(phi)) {
continue;
}
for (size_t i = 0; i < phi.NumArgs(); ++i) {
if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
continue;
}
// Phi operand at this index is an unreachable block
phi.ErasePhiOperand(i);
--i;
}
}
}
}
void DeadBranchElimination(IR::Program& program) {
boost::container::small_vector<const IR::Block*, 3> dead_blocks;
const auto begin_it{program.syntax_list.begin()};
for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
continue;
}
IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
const IR::U1 cond{cond_ref->Arg(0)};
if (!cond.IsImmediate()) {
continue;
}
if (cond.U1()) {
continue;
}
// False immediate condition. Remove condition ref, erase the entire branch.
cond_ref->Invalidate();
// Account for nested if-statements within the if(false) branch
u32 nested_ifs{1u};
while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
node_it = program.syntax_list.erase(node_it);
switch (node_it->type) {
case IR::AbstractSyntaxNode::Type::If:
++nested_ifs;
break;
case IR::AbstractSyntaxNode::Type::EndIf:
--nested_ifs;
break;
case IR::AbstractSyntaxNode::Type::Block: {
IR::Block* const block{node_it->data.block};
DeadInstElimination<false>(block);
dead_blocks.push_back(block);
break;
}
default:
break;
}
}
// Erase EndIf node of the if(false) branch
node_it = program.syntax_list.erase(node_it);
// Account for loop increment
--node_it;
}
if (!dead_blocks.empty()) {
DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
}
}
} // namespace
void DeadCodeEliminationPass(IR::Program& program) {
DeadBranchElimination(program);
for (IR::Block* const block : program.post_order_blocks) {
DeadInstElimination<true>(block);
}
}
} // namespace Shader::Optimization

View File

@@ -44,7 +44,7 @@ public:
[[nodiscard]] unsigned Count() const noexcept {
unsigned count = 0;
for (const auto [index, value] : page_table) {
for (const auto& [index, value] : page_table) {
count += value;
}
return count;

View File

@@ -117,10 +117,18 @@ void Maxwell3D::InitializeRegisterDefaults() {
shadow_state = regs;
mme_inline[MAXWELL3D_REG_INDEX(draw.end)] = true;
mme_inline[MAXWELL3D_REG_INDEX(draw.begin)] = true;
mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
mme_inline[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true;
draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true;
draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true;
draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer32_first)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer16_first)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer8_first)] = true;
draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
}
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -208,25 +216,6 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
return ProcessCBBind(3);
case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
return ProcessCBBind(4);
case MAXWELL3D_REG_INDEX(draw.end):
return DrawArrays();
case MAXWELL3D_REG_INDEX(index_buffer32_first):
regs.index_buffer.count = regs.index_buffer32_first.count;
regs.index_buffer.first = regs.index_buffer32_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
return DrawArrays();
case MAXWELL3D_REG_INDEX(index_buffer16_first):
regs.index_buffer.count = regs.index_buffer16_first.count;
regs.index_buffer.first = regs.index_buffer16_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
return DrawArrays();
case MAXWELL3D_REG_INDEX(index_buffer8_first):
regs.index_buffer.count = regs.index_buffer8_first.count;
regs.index_buffer.first = regs.index_buffer8_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
// a macro calls this one over and over, should it increase instancing?
// Used by Hades and likely other Vulkan games.
return DrawArrays();
case MAXWELL3D_REG_INDEX(topology_override):
use_topology_override = true;
return;
@@ -261,14 +250,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
// Execute the current macro.
macro_engine->Execute(macro_positions[entry], parameters);
if (mme_draw.current_mode != MMEDrawMode::Undefined) {
FlushMMEInlineDraw();
}
ProcessDeferredDraw();
}
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
// It is an error to write to a register other than the current macro's ARG register before it
// has finished execution.
// It is an error to write to a register other than the current macro's ARG register before
// it has finished execution.
if (executing_macro != 0) {
ASSERT(method == executing_macro + 1);
}
@@ -283,9 +271,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid Maxwell3D register, increase the size of the Regs structure");
const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument);
ProcessMethodCall(method, argument, method_argument, is_last_call);
if (draw_command[method]) {
regs.reg_array[method] = method_argument;
deferred_draw_method.push_back(method);
auto u32_to_u8 = [&](const u32 argument) {
inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
};
if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
u32_to_u8(method_argument);
} else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
u32_to_u8(regs.inline_index_2x16.even);
u32_to_u8(regs.inline_index_2x16.odd);
} else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
u32_to_u8(regs.inline_index_4x8.index0);
u32_to_u8(regs.inline_index_4x8.index1);
u32_to_u8(regs.inline_index_4x8.index2);
u32_to_u8(regs.inline_index_4x8.index3);
}
} else {
ProcessDeferredDraw();
const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument);
ProcessMethodCall(method, argument, method_argument, is_last_call);
}
}
void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
@@ -326,55 +338,6 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
}
}
void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
if (mme_draw.current_mode == MMEDrawMode::Undefined) {
if (mme_draw.gl_begin_consume) {
mme_draw.current_mode = expected_mode;
mme_draw.current_count = count;
mme_draw.instance_count = 1;
mme_draw.gl_begin_consume = false;
mme_draw.gl_end_count = 0;
}
return;
} else {
if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
mme_draw.instance_mode && mme_draw.gl_begin_consume) {
mme_draw.instance_count++;
mme_draw.gl_begin_consume = false;
return;
} else {
FlushMMEInlineDraw();
}
}
// Tail call in case it needs to retry.
StepInstance(expected_mode, count);
}
void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
if (mme_inline[method]) {
regs.reg_array[method] = method_argument;
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
? MMEDrawMode::Array
: MMEDrawMode::Indexed;
StepInstance(expected_mode, method_argument);
} else if (method == MAXWELL3D_REG_INDEX(draw.begin)) {
mme_draw.instance_mode =
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged);
mme_draw.gl_begin_consume = true;
} else {
mme_draw.gl_end_count++;
}
} else {
if (mme_draw.current_mode != MMEDrawMode::Undefined) {
FlushMMEInlineDraw();
}
CallMethod(method, method_argument, true);
}
}
void Maxwell3D::ProcessTopologyOverride() {
using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
@@ -404,41 +367,6 @@ void Maxwell3D::ProcessTopologyOverride() {
}
}
void Maxwell3D::FlushMMEInlineDraw() {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
// Both instance configuration registers can not be set at the same time.
ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
"Illegal combination of instancing parameters");
ProcessTopologyOverride();
const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
if (ShouldExecute()) {
rasterizer->Draw(is_indexed, true);
}
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
// it's possible that it is incorrect and that there is some other register used to specify the
// drawing mode.
if (is_indexed) {
regs.index_buffer.count = 0;
} else {
regs.vertex_buffer.count = 0;
}
mme_draw.current_mode = MMEDrawMode::Undefined;
mme_draw.current_count = 0;
mme_draw.instance_count = 0;
mme_draw.instance_mode = false;
mme_draw.gl_begin_consume = false;
mme_draw.gl_end_count = 0;
}
void Maxwell3D::ProcessMacroUpload(u32 data) {
macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
}
@@ -573,42 +501,6 @@ void Maxwell3D::ProcessSyncPoint() {
rasterizer->SignalSyncPoint(sync_point);
}
void Maxwell3D::DrawArrays() {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
// Both instance configuration registers can not be set at the same time.
ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
"Illegal combination of instancing parameters");
ProcessTopologyOverride();
if (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) {
// Increment the current instance *before* drawing.
state.current_instance++;
} else if (regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged) {
// Reset the current instance to 0.
state.current_instance = 0;
}
const bool is_indexed{regs.index_buffer.count && !regs.vertex_buffer.count};
if (ShouldExecute()) {
rasterizer->Draw(is_indexed, false);
}
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
// it's possible that it is incorrect and that there is some other register used to specify the
// drawing mode.
if (is_indexed) {
regs.index_buffer.count = 0;
} else {
regs.vertex_buffer.count = 0;
}
}
std::optional<u64> Maxwell3D::GetQueryResult() {
switch (regs.report_semaphore.query.report) {
case Regs::ReportSemaphore::Report::Payload:
@@ -691,4 +583,95 @@ void Maxwell3D::ProcessClearBuffers() {
rasterizer->Clear();
}
void Maxwell3D::ProcessDeferredDraw() {
if (deferred_draw_method.empty()) {
return;
}
enum class DrawMode {
Undefined,
General,
Instance,
};
DrawMode draw_mode{DrawMode::Undefined};
u32 instance_count = 1;
auto first_method = deferred_draw_method[0];
if (MAXWELL3D_REG_INDEX(draw.begin) == first_method) {
// The minimum number of methods for drawing must be greater than or equal to
// 3[draw.begin->vertex(index)count->draw.end] to avoid errors in index mode drawing
if (deferred_draw_method.size() < 3) {
return;
}
draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
? DrawMode::Instance
: DrawMode::General;
} else if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method ||
MAXWELL3D_REG_INDEX(index_buffer16_first) == first_method ||
MAXWELL3D_REG_INDEX(index_buffer8_first) == first_method) {
draw_mode = DrawMode::General;
}
// Drawing will only begin with draw.begin or index_buffer method, other methods directly
// clear
if (draw_mode == DrawMode::Undefined) {
deferred_draw_method.clear();
return;
}
if (draw_mode == DrawMode::Instance) {
ASSERT_MSG(deferred_draw_method.size() % 4 == 0, "Instance mode method size error");
instance_count = static_cast<u32>(deferred_draw_method.size()) / 4;
} else {
if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method) {
regs.index_buffer.count = regs.index_buffer32_first.count;
regs.index_buffer.first = regs.index_buffer32_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
} else if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method) {
regs.index_buffer.count = regs.index_buffer16_first.count;
regs.index_buffer.first = regs.index_buffer16_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
} else if (MAXWELL3D_REG_INDEX(index_buffer32_first) == first_method) {
regs.index_buffer.count = regs.index_buffer8_first.count;
regs.index_buffer.first = regs.index_buffer8_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
} else {
auto second_method = deferred_draw_method[1];
if (MAXWELL3D_REG_INDEX(draw_inline_index) == second_method ||
MAXWELL3D_REG_INDEX(inline_index_2x16.even) == second_method ||
MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == second_method) {
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
}
}
}
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
// Both instance configuration registers can not be set at the same time.
ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
"Illegal combination of instancing parameters");
ProcessTopologyOverride();
const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
if (ShouldExecute()) {
rasterizer->Draw(is_indexed, instance_count);
}
if (is_indexed) {
regs.index_buffer.count = 0;
} else {
regs.vertex_buffer.count = 0;
}
deferred_draw_method.clear();
inline_index_draw_indexes.clear();
}
} // namespace Tegra::Engines

View File

@@ -1739,14 +1739,11 @@ public:
Footprint_1x1_Virtual = 2,
};
struct InlineIndex4x8Align {
struct InlineIndex4x8 {
union {
BitField<0, 30, u32> count;
BitField<30, 2, u32> start;
};
};
struct InlineIndex4x8Index {
union {
BitField<0, 8, u32> index0;
BitField<8, 8, u32> index1;
@@ -2836,8 +2833,7 @@ public:
u32 depth_write_enabled; ///< 0x12E8
u32 alpha_test_enabled; ///< 0x12EC
INSERT_PADDING_BYTES_NOINIT(0x10);
InlineIndex4x8Align inline_index_4x8_align; ///< 0x1300
InlineIndex4x8Index inline_index_4x8_index; ///< 0x1304
InlineIndex4x8 inline_index_4x8; ///< 0x1300
D3DCullMode d3d_cull_mode; ///< 0x1308
ComparisonOp depth_test_func; ///< 0x130C
f32 alpha_test_ref; ///< 0x1310
@@ -3048,8 +3044,6 @@ public:
};
std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering.
};
State state{};
@@ -3064,11 +3058,6 @@ public:
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
u32 methods_pending) override;
/// Write the value to the register identified by method.
void CallMethodFromMME(u32 method, u32 method_argument);
void FlushMMEInlineDraw();
bool ShouldExecute() const {
return execute_on;
}
@@ -3081,21 +3070,6 @@ public:
return *rasterizer;
}
enum class MMEDrawMode : u32 {
Undefined,
Array,
Indexed,
};
struct MMEDrawState {
MMEDrawMode current_mode{MMEDrawMode::Undefined};
u32 current_count{};
u32 instance_count{};
bool instance_mode{};
bool gl_begin_consume{};
u32 gl_end_count{};
} mme_draw;
struct DirtyState {
using Flags = std::bitset<std::numeric_limits<u8>::max()>;
using Table = std::array<u8, Regs::NUM_REGS>;
@@ -3105,6 +3079,8 @@ public:
Tables tables{};
} dirty;
std::vector<u8> inline_index_draw_indexes;
private:
void InitializeRegisterDefaults();
@@ -3164,14 +3140,10 @@ private:
/// Handles a write to the CB_BIND register.
void ProcessCBBind(size_t stage_index);
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
void DrawArrays();
/// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
void ProcessTopologyOverride();
// Handles a instance drawcall from MME
void StepInstance(MMEDrawMode expected_mode, u32 count);
void ProcessDeferredDraw();
/// Returns a query's value or an empty object if the value will be deferred through a cache.
std::optional<u64> GetQueryResult();
@@ -3184,8 +3156,6 @@ private:
/// Start offsets of each macro in macro_memory
std::array<u32, 0x80> macro_positions{};
std::array<bool, Regs::NUM_REGS> mme_inline{};
/// Macro method that is currently being executed / being fed parameters.
u32 executing_macro = 0;
/// Parameters that have been submitted to the macro call so far.
@@ -3198,6 +3168,9 @@ private:
bool execute_on{true};
bool use_topology_override{false};
std::array<bool, Regs::NUM_REGS> draw_command{};
std::vector<u32> deferred_draw_method;
};
#define ASSERT_REG_POSITION(field_name, position) \
@@ -3402,8 +3375,7 @@ ASSERT_REG_POSITION(alpha_to_coverage_dither, 0x12E0);
ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4);
ASSERT_REG_POSITION(depth_write_enabled, 0x12E8);
ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC);
ASSERT_REG_POSITION(inline_index_4x8_align, 0x1300);
ASSERT_REG_POSITION(inline_index_4x8_index, 0x1304);
ASSERT_REG_POSITION(inline_index_4x8, 0x1300);
ASSERT_REG_POSITION(d3d_cull_mode, 0x1308);
ASSERT_REG_POSITION(depth_test_func, 0x130C);
ASSERT_REG_POSITION(alpha_test_ref, 0x1310);

View File

@@ -22,35 +22,29 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
maxwell3d.regs.global_base_instance_index = parameters[5];
maxwell3d.mme_draw.instance_count = instance_count;
maxwell3d.regs.global_base_vertex_index = parameters[3];
maxwell3d.regs.index_buffer.count = parameters[1];
maxwell3d.regs.index_buffer.first = parameters[4];
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, true);
maxwell3d.Rasterizer().Draw(true, instance_count);
}
maxwell3d.regs.index_buffer.count = 0;
maxwell3d.mme_draw.instance_count = 0;
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
}
void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
maxwell3d.regs.vertex_buffer.first = parameters[3];
maxwell3d.regs.vertex_buffer.count = parameters[1];
maxwell3d.regs.global_base_instance_index = parameters[4];
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
maxwell3d.mme_draw.instance_count = count;
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(false, true);
maxwell3d.Rasterizer().Draw(false, instance_count);
}
maxwell3d.regs.vertex_buffer.count = 0;
maxwell3d.mme_draw.instance_count = 0;
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
}
void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
@@ -63,24 +57,21 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.regs.global_base_vertex_index = element_base;
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.mme_draw.instance_count = instance_count;
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
maxwell3d.CallMethodFromMME(0x8e4, element_base);
maxwell3d.CallMethodFromMME(0x8e5, base_instance);
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, element_base, true);
maxwell3d.CallMethod(0x8e5, base_instance, true);
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, true);
maxwell3d.Rasterizer().Draw(true, instance_count);
}
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.index_buffer.count = 0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
maxwell3d.mme_draw.instance_count = 0;
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
maxwell3d.CallMethodFromMME(0x8e4, 0x0);
maxwell3d.CallMethodFromMME(0x8e5, 0x0);
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, 0x0, true);
maxwell3d.CallMethod(0x8e5, 0x0, true);
}
// Multidraw Indirect
@@ -91,11 +82,9 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.regs.index_buffer.count = 0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
maxwell3d.mme_draw.instance_count = 0;
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
maxwell3d.CallMethodFromMME(0x8e4, 0x0);
maxwell3d.CallMethodFromMME(0x8e5, 0x0);
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, 0x0, true);
maxwell3d.CallMethod(0x8e5, 0x0, true);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
});
const u32 start_indirect = parameters[0];
@@ -127,15 +116,13 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.regs.index_buffer.count = num_vertices;
maxwell3d.regs.global_base_vertex_index = base_vertex;
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.mme_draw.instance_count = instance_count;
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
maxwell3d.CallMethodFromMME(0x8e4, base_vertex);
maxwell3d.CallMethodFromMME(0x8e5, base_instance);
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, base_vertex, true);
maxwell3d.CallMethod(0x8e5, base_instance, true);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, true);
maxwell3d.Rasterizer().Draw(true, instance_count);
}
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
}
}

View File

@@ -335,7 +335,7 @@ void MacroInterpreterImpl::SetMethodAddress(u32 address) {
}
void MacroInterpreterImpl::Send(u32 value) {
maxwell3d.CallMethodFromMME(method_address.address, value);
maxwell3d.CallMethod(method_address.address, value, true);
// Increment the method address by the method increment.
method_address.address.Assign(method_address.address.Value() +
method_address.increment.Value());

View File

@@ -346,7 +346,7 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
}
void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
maxwell3d->CallMethodFromMME(method_address.address, value);
maxwell3d->CallMethod(method_address.address, value, true);
}
void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {

View File

@@ -40,7 +40,7 @@ public:
virtual ~RasterizerInterface() = default;
/// Dispatches a draw invocation
virtual void Draw(bool is_indexed, bool is_instanced) = 0;
virtual void Draw(bool is_indexed, u32 instance_count) = 0;
/// Clear the current framebuffer
virtual void Clear() = 0;

View File

@@ -205,7 +205,7 @@ void RasterizerOpenGL::Clear() {
++num_queued_commands;
}
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
MICROPROFILE_SCOPE(OpenGL_Drawing);
SCOPE_EXIT({ gpu.TickWork(); });
@@ -222,14 +222,15 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->Configure(is_indexed);
BindInlineIndexBuffer();
SyncState();
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
BeginTransformFeedback(pipeline, primitive_mode);
const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index);
const GLsizei num_instances =
static_cast<GLsizei>(is_instanced ? maxwell3d->mme_draw.instance_count : 1);
const GLsizei num_instances = static_cast<GLsizei>(instance_count);
if (is_indexed) {
const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index);
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count);
@@ -1129,6 +1130,16 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
void RasterizerOpenGL::BindInlineIndexBuffer() {
if (maxwell3d->inline_index_draw_indexes.empty()) {
return;
}
const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count);
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count);
}
AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {

View File

@@ -68,7 +68,7 @@ public:
StateTracker& state_tracker_);
~RasterizerOpenGL() override;
void Draw(bool is_indexed, bool is_instanced) override;
void Draw(bool is_indexed, u32 instance_count) override;
void Clear() override;
void DispatchCompute() override;
void ResetCounter(VideoCore::QueryType type) override;
@@ -199,6 +199,8 @@ private:
/// End a transform feedback
void EndTransformFeedback();
void BindInlineIndexBuffer();
Tegra::GPU& gpu;
const Device& device;

View File

@@ -127,11 +127,10 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
return scissor;
}
DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced,
bool is_indexed) {
DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_indexed) {
DrawParams params{
.base_instance = regs.global_base_instance_index,
.num_instances = is_instanced ? num_instances : 1,
.num_instances = num_instances,
.base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first,
.num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count,
.first_index = is_indexed ? regs.index_buffer.first : 0,
@@ -177,7 +176,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
RasterizerVulkan::~RasterizerVulkan() = default;
void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
MICROPROFILE_SCOPE(Vulkan_Drawing);
SCOPE_EXIT({ gpu.TickWork(); });
@@ -194,13 +193,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->Configure(is_indexed);
BindInlineIndexBuffer();
BeginTransformFeedback();
UpdateDynamicStates();
const auto& regs{maxwell3d->regs};
const u32 num_instances{maxwell3d->mme_draw.instance_count};
const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_instanced, is_indexed)};
const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_indexed)};
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
if (draw_params.is_indexed) {
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
@@ -1009,4 +1010,17 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
void RasterizerVulkan::BindInlineIndexBuffer() {
if (maxwell3d->inline_index_draw_indexes.empty()) {
return;
}
const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count);
std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count);
buffer_cache_runtime.BindIndexBuffer(
maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer,
static_cast<u32>(buffer.offset), data_count);
}
} // namespace Vulkan

View File

@@ -64,7 +64,7 @@ public:
StateTracker& state_tracker_, Scheduler& scheduler_);
~RasterizerVulkan() override;
void Draw(bool is_indexed, bool is_instanced) override;
void Draw(bool is_indexed, u32 instance_count) override;
void Clear() override;
void DispatchCompute() override;
void ResetCounter(VideoCore::QueryType type) override;
@@ -141,6 +141,8 @@ private:
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
void BindInlineIndexBuffer();
Tegra::GPU& gpu;
ScreenInfo& screen_info;