Compare commits

..

33 Commits

Author SHA1 Message Date
Liam
3b1172c10f video_core: tune garbage collection aggressiveness 2023-05-23 12:55:14 -04:00
Narr the Reg
e8a025b4f8 Merge pull request #10386 from german77/high
input_common: Map motion with relative values not absolute ones
2023-05-22 13:13:50 -06:00
liamwhite
f82efe9f65 Merge pull request #10392 from danilaml/update-cubeb-again
externals: update cubeb (again)
2023-05-21 00:24:12 -04:00
Danila Malyutin
e5c2ec223a externals: update cubeb 2023-05-21 03:02:26 +04:00
german77
dbcdb3523b input_common: Map motion with relative values not absolute ones 2023-05-19 21:19:29 -06:00
bunnei
1e398e6c36 Merge pull request #10344 from german77/pro-amiibo
input_common: Fix pro controller amiibo support
2023-05-18 18:10:38 -07:00
Danila Malyutin
55d740fffa externals: update cubeb (#10362) 2023-05-19 00:24:45 +02:00
Mai
f6c5507873 Merge pull request #10377 from liamwhite/constexpr
renderer_vulkan: remove wrong constexpr
2023-05-18 18:04:07 -04:00
Liam
de7c92d7c4 renderer_vulkan: remove wrong constexpr 2023-05-18 18:01:01 -04:00
Matías Locatti
f35c14fb73 Merge pull request #10365 from lat9nq/anv_push_descriptor
vulkan_device: Disable VK_KHR_push_descriptor on ANV
2023-05-18 01:38:24 -03:00
lat9nq
c1a8a508bc vulkan_device: Disable VK_KHR_push_descriptor on ANV
Mesa commit ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc breaks
VK_KHR_push_descriptor usage on ANV drivers 22.3.0, so disable it
and allow games to boot.
2023-05-17 22:19:57 -04:00
bunnei
9d4a2de72b Merge pull request #10262 from liamwhite/depth-clamp
vulkan_common: disable depth clamp dynamic state for older radv
2023-05-17 12:19:03 -07:00
Narr the Reg
5693434b8a input_common: Fix pro controller amiibo support 2023-05-16 18:37:22 -06:00
liamwhite
47c5c37bed Merge pull request #10217 from Kelebek1/clear_value
Use the rendertarget format of the correct RT rather than the first valid
2023-05-16 10:06:30 -04:00
liamwhite
a540d248f3 Merge pull request #10107 from grimkor/allow-fully-customised-hotkeys
Allow fully customised controller hotkeys
2023-05-16 10:06:15 -04:00
liamwhite
bbb6b58aa4 Merge pull request #10181 from lat9nq/intel-compute-toggle
configure_graphics: Add option to enable compute pipelines for Intel proprietary
2023-05-15 12:05:24 -04:00
liamwhite
736a31e4ff Merge pull request #10234 from Kelebek1/clouds_depth
Fix Tears of the Kingdom flickering clouds and depths geometry.
2023-05-15 12:03:31 -04:00
liamwhite
6060685609 Merge pull request #10249 from FernandoS27/sorry-i-am-late
Buffer Cache: Clear sync code.
2023-05-15 12:03:25 -04:00
liamwhite
5b85925469 Merge pull request #10254 from danilaml/fix-h264-decode
Fix missing pic_order_present_flag in h264 header
2023-05-15 12:03:14 -04:00
liamwhite
033aa264cf Merge pull request #10265 from german77/amiibo-lag
input_common: Make amiibo scanning less demanding
2023-05-15 12:03:08 -04:00
liamwhite
9087fe10e9 Merge pull request #10294 from german77/vibration_span
service: hid: Use span instead of vector reference
2023-05-15 12:03:00 -04:00
german77
6e54615b16 service: hid: Use span instead of vector reference 2023-05-14 18:07:04 -06:00
Fernando Sahmkow
c4bfbc6d25 Buffer Cache: Clear sync code. 2023-05-15 01:50:21 +02:00
german77
ac531aa15f input_common: Make amiibo scanning less demanding 2023-05-14 09:04:37 -06:00
Liam
856838f7ce vulkan_common: disable depth clamp dynamic state for older radv 2023-05-13 00:37:17 -04:00
Danila Malyutin
9367769fe7 Fix missing pic_order_present_flag in h264 header
Fixes #9635
2023-05-12 22:30:59 +04:00
Kelebek1
e42b4a16b6 Fix Tears of the Kingdom flickering clouds and depths. 2023-05-11 19:25:24 +01:00
grimkor
ceb65c259a Allow fully customisable controller hotkeys 2023-05-10 19:28:11 +01:00
Kelebek1
a386003b64 Use the rendertarget format of the correct RT rather than the first valid 2023-05-09 22:13:15 +01:00
lat9nq
40f3e2fbf1 configure_graphics_advanced: Hide input compute toggle a little later
SetColoredTristate causes the setting to become visible as it calls
`show()` on it.
2023-05-08 12:32:56 -04:00
lat9nq
55c77dd25b yuzu-qt/config: Add option to disable compute on Intel
This option is only visible if an Intel GPU using the proprietary
driver is found during Vulkan device enumeration.

configure_graphics: More directly get driver id

Vulkan::Device does quite a bit more than we need just to see the
driver ID here.
2023-05-07 01:06:22 -04:00
lat9nq
6ed6e6e18e vk_pipeline_cache: Use setting to disable intel compute 2023-05-07 01:06:22 -04:00
lat9nq
725aacb4bc settings: Add enable compute pipelines
For the Intel proprietary driver's deficiencies.

settings: Restore compute option global state
2023-05-07 01:06:20 -04:00
40 changed files with 378 additions and 430 deletions

View File

@@ -232,6 +232,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
values.enable_compute_pipelines.SetGlobal(true);
// System
values.language_index.SetGlobal(true);

View File

@@ -472,6 +472,7 @@ struct Values {
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
"use_vulkan_driver_pipeline_cache"};
SwitchableSetting<bool> enable_compute_pipelines{false, "enable_compute_pipelines"};
SwitchableSetting<u8> bg_red{0, "bg_red"};
SwitchableSetting<u8> bg_green{0, "bg_green"};

View File

@@ -979,8 +979,8 @@ void Controller_NPad::VibrateController(
}
void Controller_NPad::VibrateControllers(
const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
const std::vector<Core::HID::VibrationValue>& vibration_values) {
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}

View File

@@ -112,8 +112,8 @@ public:
const Core::HID::VibrationValue& vibration_value);
void VibrateControllers(
const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
const std::vector<Core::HID::VibrationValue>& vibration_values);
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values);
Core::HID::VibrationValue GetLastVibration(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;

View File

@@ -1601,16 +1601,16 @@ void Hid::SendVibrationValues(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto handles = ctx.ReadBuffer(0);
const auto vibrations = ctx.ReadBuffer(1);
const auto handle_data = ctx.ReadBuffer(0);
const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
const auto vibration_data = ctx.ReadBuffer(1);
const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
sizeof(Core::HID::VibrationValue));
std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
auto vibration_device_handles =
std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
handle_count);
auto vibration_values = std::span(
reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateControllers(vibration_device_handles, vibration_values);

View File

@@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
OnMotionUpdate(port, type, id, value);
}},
.on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
.on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
OnAmiiboUpdate(port, amiibo_data);
.on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) {
OnAmiiboUpdate(port, type, amiibo_data);
}},
.on_camera_data = {[this, port](const std::vector<u8>& camera_data,
Joycon::IrsResolution format) {
@@ -398,8 +398,9 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
SetAxis(identifier, 100, ring_data);
}
void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
const std::vector<u8>& amiibo_data) {
const auto identifier = GetIdentifier(port, type);
const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
: Common::Input::NfcState::NewAmiibo;
SetNfc(identifier, {nfc_state, amiibo_data});

View File

@@ -81,7 +81,8 @@ private:
void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
const Joycon::MotionData& value);
void OnRingConUpdate(f32 ring_data);
void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
const std::vector<u8>& amiibo_data);
void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
Joycon::IrsResolution format);

View File

@@ -265,7 +265,7 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCom
DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
MCUCommandResponse output{};
constexpr std::size_t MaxTries{8};
constexpr std::size_t MaxTries{16};
std::size_t tries{};
do {

View File

@@ -394,6 +394,7 @@ enum class DriverResult {
InvalidHandle,
NotSupported,
Disabled,
Delayed,
Unknown,
};
@@ -576,8 +577,8 @@ static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is a
struct NFCRequestState {
NFCReadCommand command_argument;
u8 packet_id;
INSERT_PADDING_BYTES(0x1);
u8 packet_id;
MCUPacketFlag packet_flag;
u8 data_length;
union {

View File

@@ -64,6 +64,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
if (result == DriverResult::Success) {
result = WaitUntilNfcIsReady();
}
if (result == DriverResult::Success) {
MCUCommandResponse output{};
result = SendStopPollingRequest(output);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIsReady();
}
if (result == DriverResult::Success) {
MCUCommandResponse output{};
result = SendStartPollingRequest(output);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIsPolling();
}
if (result == DriverResult::Success) {
is_enabled = true;
}
@@ -72,24 +86,26 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
}
DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
LOG_DEBUG(Input, "Start NFC pooling Mode");
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
return DriverResult::Delayed;
}
update_counter = 0;
LOG_DEBUG(Input, "Scan for amiibos");
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagFoundData tag_data{};
if (result == DriverResult::Success) {
result = StartPolling(tag_data);
}
if (result == DriverResult::Success) {
result = ReadTag(tag_data);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIsReady();
}
if (result == DriverResult::Success) {
result = StartPolling(tag_data);
result = IsTagInRange(tag_data);
}
if (result == DriverResult::Success) {
std::string uuid_string;
for (auto& content : tag_data.uuid) {
uuid_string += fmt::format(" {:02x}", content);
}
LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
result = GetAmiiboData(data);
}
@@ -97,12 +113,17 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
}
bool NfcProtocol::HasAmiibo() {
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
return true;
}
update_counter = 0;
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagFoundData tag_data{};
if (result == DriverResult::Success) {
result = StartPolling(tag_data);
result = IsTagInRange(tag_data, 7);
}
return result == DriverResult::Success;
@@ -114,7 +135,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
std::size_t tries = 0;
do {
auto result = SendStartWaitingRecieveRequest(output);
auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
@@ -129,14 +150,14 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
return DriverResult::Success;
}
DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
LOG_DEBUG(Input, "Start Polling for tag");
constexpr std::size_t timeout_limit = 7;
DriverResult NfcProtocol::WaitUntilNfcIsPolling() {
constexpr std::size_t timeout_limit = 10;
MCUCommandResponse output{};
std::size_t tries = 0;
do {
const auto result = SendStartPollingRequest(output);
auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
}
@@ -145,7 +166,26 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
}
} while (output.mcu_report != MCUReport::NFCState ||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
output.mcu_data[6] != 0x09);
output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x01);
return DriverResult::Success;
}
DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_limit) {
MCUCommandResponse output{};
std::size_t tries = 0;
do {
const auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
}
if (tries++ > timeout_limit) {
return DriverResult::Timeout;
}
} while (output.mcu_report != MCUReport::NFCState ||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
(output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));
data.type = output.mcu_data[12];
data.uuid.resize(output.mcu_data[14]);
@@ -154,85 +194,22 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
return DriverResult::Success;
}
DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
constexpr std::size_t timeout_limit = 10;
MCUCommandResponse output{};
std::size_t tries = 0;
std::string uuid_string;
for (auto& content : data.uuid) {
uuid_string += fmt::format(" {:02x}", content);
}
LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
tries = 0;
NFCPages ntag_pages = NFCPages::Block0;
// Read Tag data
while (true) {
auto result = SendReadAmiiboRequest(output, ntag_pages);
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if (result != DriverResult::Success) {
return result;
}
if ((output.mcu_report == MCUReport::NFCReadData ||
output.mcu_report == MCUReport::NFCState) &&
nfc_status == NFCStatus::TagLost) {
return DriverResult::ErrorReadingData;
}
if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
output.mcu_data[2] == 0x01) {
if (data.type != 2) {
continue;
}
switch (output.mcu_data[24]) {
case 0:
ntag_pages = NFCPages::Block135;
break;
case 3:
ntag_pages = NFCPages::Block45;
break;
case 4:
ntag_pages = NFCPages::Block231;
break;
default:
return DriverResult::ErrorReadingData;
}
continue;
}
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
// finished
SendStopPollingRequest(output);
return DriverResult::Success;
}
// Ignore other state reports
if (output.mcu_report == MCUReport::NFCState) {
continue;
}
if (tries++ > timeout_limit) {
return DriverResult::Timeout;
}
}
return DriverResult::Success;
}
DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
constexpr std::size_t timeout_limit = 10;
constexpr std::size_t timeout_limit = 60;
MCUCommandResponse output{};
std::size_t tries = 0;
NFCPages ntag_pages = NFCPages::Block135;
u8 package_index = 0;
std::size_t ntag_buffer_pos = 0;
auto result = SendReadAmiiboRequest(output, NFCPages::Block135);
if (result != DriverResult::Success) {
return result;
}
// Read Tag data
while (true) {
auto result = SendReadAmiiboRequest(output, ntag_pages);
while (tries++ < timeout_limit) {
result = SendNextPackageRequest(output, package_index);
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if (result != DriverResult::Success) {
@@ -255,6 +232,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
payload_size);
}
package_index++;
continue;
}
@@ -262,18 +240,9 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
LOG_INFO(Input, "Finished reading amiibo");
return DriverResult::Success;
}
// Ignore other state reports
if (output.mcu_report == MCUReport::NFCState) {
continue;
}
if (tries++ > timeout_limit) {
return DriverResult::Timeout;
}
}
return DriverResult::Success;
return DriverResult::Timeout;
}
DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
@@ -317,10 +286,10 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
output);
}
DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
NFCRequestState request{
.command_argument = NFCReadCommand::StartWaitingRecieve,
.packet_id = 0x0,
.packet_id = packet_id,
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = 0,
.raw_data = {},

View File

@@ -32,6 +32,9 @@ public:
bool IsEnabled() const;
private:
// Number of times the function will be delayed until it outputs valid data
static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
struct TagFoundData {
u8 type;
std::vector<u8> uuid;
@@ -39,9 +42,9 @@ private:
DriverResult WaitUntilNfcIsReady();
DriverResult StartPolling(TagFoundData& data);
DriverResult WaitUntilNfcIsPolling();
DriverResult ReadTag(const TagFoundData& data);
DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
DriverResult GetAmiiboData(std::vector<u8>& data);
@@ -49,13 +52,14 @@ private:
DriverResult SendStopPollingRequest(MCUCommandResponse& output);
DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id);
DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
bool is_enabled{};
std::size_t update_counter{};
};
} // namespace InputCommon::Joycon

View File

@@ -380,13 +380,16 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
if (!configuring || !mapping_callback.on_data) {
return;
}
const auto old_value = GetMotion(identifier, motion);
bool is_active = false;
if (std::abs(value.accel_x) > 1.5f || std::abs(value.accel_y) > 1.5f ||
std::abs(value.accel_z) > 1.5f) {
if (std::abs(value.accel_x - old_value.accel_x) > 1.5f ||
std::abs(value.accel_y - old_value.accel_y) > 1.5f ||
std::abs(value.accel_z - old_value.accel_z) > 1.5f) {
is_active = true;
}
if (std::abs(value.gyro_x) > 0.6f || std::abs(value.gyro_y) > 0.6f ||
std::abs(value.gyro_z) > 0.6f) {
if (std::abs(value.gyro_x - old_value.gyro_x) > 0.6f ||
std::abs(value.gyro_y - old_value.gyro_y) > 0.6f ||
std::abs(value.gyro_z - old_value.gyro_z) > 0.6f) {
is_active = true;
}
if (!is_active) {

View File

@@ -339,9 +339,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpBitcast(ctx.F32[1], 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.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_index));
}
case IR::Attribute::BaseInstance:
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_instance));
@@ -386,9 +384,7 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
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);
return ctx.OpLoad(ctx.U32[1], ctx.vertex_index);
}
case IR::Attribute::BaseInstance:
return ctx.OpLoad(ctx.U32[1], ctx.base_instance);

View File

@@ -102,12 +102,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
}
IR::F32 value{v.ir.CompositeExtract(sample, element)};
if (element < 2) {
IR::U32 casted_value;
if (element == 0) {
casted_value = v.ir.ConvertFToU(32, value);
} else {
casted_value = v.ir.ConvertFToS(16, value);
}
IR::U32 casted_value = v.ir.ConvertFToU(32, value);
v.X(dest_reg, v.ir.ShiftLeftLogical(casted_value, v.ir.Imm32(8)));
} else {
v.F(dest_reg, value);

View File

@@ -23,94 +23,42 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
common_ranges.clear();
inline_buffer_id = NULL_BUFFER_ID;
if (!runtime.CanReportMemoryUsage()) {
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
return;
}
const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const u64 device_mem_per = device_memory / 100;
minimum_memory = device_mem_per * 25;
expected_memory = device_mem_per * 50;
critical_memory = device_mem_per * 80;
LOG_INFO(HW_GPU, "Buffer cache device memory limits: min {} expected {} critical {}",
minimum_memory, expected_memory, critical_memory);
const s64 min_spacing_expected = device_memory - 1_GiB;
const s64 min_spacing_critical = device_memory - 512_MiB;
const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
minimum_memory = static_cast<u64>(
std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
critical_memory = static_cast<u64>(
std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
}
template <class P>
void BufferCache<P>::RunGarbageCollector() {
if (total_used_memory < minimum_memory) {
return;
}
bool is_expected = total_used_memory >= expected_memory;
bool is_critical = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = is_critical ? 60ULL : is_expected ? 120ULL : 240ULL;
size_t num_iterations = is_critical ? 40 : (is_expected ? 20 : 10);
boost::container::small_vector<std::pair<BufferId, VideoCommon::BufferCopies>, 40> to_delete;
u64 total_size{0};
const auto clean_up = [&](BufferId buffer_id) {
const bool aggressive_gc = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
if (num_iterations == 0) {
return true;
}
--num_iterations;
auto& buffer = slot_buffers[buffer_id];
auto buffer_copies = FullDownloadCopies(buffer, buffer.CpuAddr(), buffer.SizeBytes());
total_size += buffer_copies.total_size;
to_delete.push_back({buffer_id, std::move(buffer_copies)});
DownloadBufferMemory(buffer);
DeleteBuffer(buffer_id);
return false;
};
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
if (total_size > 0) {
if constexpr (USE_MEMORY_MAPS) {
auto map = runtime.DownloadStagingBuffer(Common::AlignUp(total_size, 1024));
auto base_offset = map.offset;
for (auto& [buffer_id, buffer_copies] : to_delete) {
if (buffer_copies.total_size == 0) {
continue;
}
for (auto& copy : buffer_copies.copies) {
copy.dst_offset += map.offset;
}
auto& buffer = slot_buffers[buffer_id];
runtime.CopyBuffer(map.buffer, buffer, buffer_copies.copies);
map.offset += buffer_copies.total_size;
}
runtime.Finish();
for (auto& [buffer_id, buffer_copies] : to_delete) {
if (buffer_copies.total_size > 0) {
auto& buffer = slot_buffers[buffer_id];
for (const auto& copy : buffer_copies.copies) {
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
const u8* copy_mapped_memory =
map.mapped_span.data() + copy.dst_offset - base_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
}
}
DeleteBuffer(buffer_id);
}
} else {
for (auto& [buffer_id, buffer_copies] : to_delete) {
if (buffer_copies.total_size == 0) {
continue;
}
const std::span<u8> immediate_buffer = ImmediateBuffer(buffer_copies.total_size);
auto& buffer = slot_buffers[buffer_id];
for (const BufferCopy& copy : buffer_copies.copies) {
buffer.ImmediateDownload(copy.src_offset,
immediate_buffer.subspan(0, copy.size));
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
}
DeleteBuffer(buffer_id);
}
}
} else {
for (auto& [buffer_id, buffer_copies] : to_delete) {
DeleteBuffer(buffer_id);
}
}
}
template <class P>
@@ -129,10 +77,12 @@ void BufferCache<P>::TickFrame() {
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
// If we can obtain the memory info, use it instead of the estimate.
if (runtime.CanReportMemoryUsage() && frame_tick % 60 == 0) {
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
}
RunGarbageCollector();
if (total_used_memory >= minimum_memory) {
RunGarbageCollector();
}
++frame_tick;
delayed_destruction_ring.Tick();
@@ -181,33 +131,15 @@ std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(VA
template <class P>
void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
WaitOnAsyncFlushes(cpu_addr, size);
ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
DownloadBufferMemory(buffer, cpu_addr, size);
});
}
template <class P>
void BufferCache<P>::WaitOnAsyncFlushes(VAddr cpu_addr, u64 size) {
bool must_wait = false;
ForEachInOverlapCounter(async_downloads, cpu_addr, size,
[&](VAddr, VAddr, int) { must_wait = true; });
bool must_release = false;
ForEachInRangeSet(pending_ranges, cpu_addr, size, [&](VAddr, VAddr) { must_release = true; });
if (must_release) {
std::function<void()> tmp([]() {});
rasterizer.SignalFence(std::move(tmp));
}
if (must_wait || must_release) {
rasterizer.ReleaseFences();
}
}
template <class P>
void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
uncommitted_ranges.subtract(subtract_interval);
pending_ranges.subtract(subtract_interval);
for (auto& interval_set : committed_ranges) {
interval_set.subtract(subtract_interval);
}
@@ -227,7 +159,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
}
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
WaitOnAsyncFlushes(*cpu_src_address, static_cast<u32>(amount));
ClearDownload(subtract_interval);
BufferId buffer_a;
@@ -255,7 +186,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
const IntervalType add_interval{new_base_address, new_base_address + size};
tmp_intervals.push_back(add_interval);
uncommitted_ranges.add(add_interval);
pending_ranges.add(add_interval);
};
ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
// This subtraction in this order is important for overlapping copies.
@@ -542,7 +472,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
pending_ranges.clear();
auto it = committed_ranges.begin();
while (it != committed_ranges.end()) {
auto& current_intervals = *it;
@@ -1282,7 +1211,6 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
const IntervalType base_interval{cpu_addr, cpu_addr + size};
common_ranges.add(base_interval);
uncommitted_ranges.add(base_interval);
pending_ranges.add(base_interval);
}
template <class P>
@@ -1606,13 +1534,17 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
}
template <class P>
VideoCommon::BufferCopies BufferCache<P>::FullDownloadCopies(Buffer& buffer, VAddr cpu_addr,
u64 size, bool clear) {
boost::container::small_vector<BufferCopy, 16> copies;
void BufferCache<P>::DownloadBufferMemory(Buffer& buffer) {
DownloadBufferMemory(buffer, buffer.CpuAddr(), buffer.SizeBytes());
}
template <class P>
void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 size) {
boost::container::small_vector<BufferCopy, 1> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
memory_tracker.ForEachDownloadRange(
cpu_addr, size, clear, [&](u64 cpu_addr_out, u64 range_size) {
memory_tracker.ForEachDownloadRangeAndClear(
cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
const VAddr buffer_addr = buffer.CpuAddr();
const auto add_download = [&](VAddr start, VAddr end) {
const u64 new_offset = start - buffer_addr;
@@ -1636,35 +1568,22 @@ VideoCommon::BufferCopies BufferCache<P>::FullDownloadCopies(Buffer& buffer, VAd
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
});
return {total_size_bytes, largest_copy, std::move(copies)};
}
template <class P>
void BufferCache<P>::DownloadBufferMemory(Buffer& buffer) {
DownloadBufferMemory(buffer, buffer.CpuAddr(), buffer.SizeBytes());
}
template <class P>
void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 size) {
auto buffer_copies = FullDownloadCopies(buffer, cpu_addr, size);
if (buffer_copies.total_size == 0) {
if (total_size_bytes == 0) {
return;
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(buffer_copies.total_size);
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
const u8* const mapped_memory = download_staging.mapped_span.data();
const std::span<BufferCopy> copies_span(buffer_copies.copies.data(),
buffer_copies.copies.size());
for (BufferCopy& copy : buffer_copies.copies) {
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
for (BufferCopy& copy : copies) {
// Modify copies to have the staging offset in mind
copy.dst_offset += download_staging.offset;
}
runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
runtime.Finish();
for (const BufferCopy& copy : buffer_copies.copies) {
for (const BufferCopy& copy : copies) {
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
@@ -1672,8 +1591,8 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, copy_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(buffer_copies.largest_copy);
for (const BufferCopy& copy : buffer_copies.copies) {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const BufferCopy& copy : copies) {
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(copy_cpu_addr, immediate_buffer.data(), copy.size);
@@ -1736,14 +1655,15 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
const bool is_nvn_cbuf = cbuf_index == 0;
// The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
if (is_nvn_cbuf) {
return gpu_memory->Read<u32>(ssbo_addr + 8);
const u32 ssbo_size = gpu_memory->Read<u32>(ssbo_addr + 8);
if (ssbo_size != 0) {
return ssbo_size;
}
}
// Other titles (notably Doom Eternal) may use STG/LDG on buffer addresses in custom defined
// cbufs, which do not store the sizes adjacent to the addresses, so use the fully
// mapped buffer size for now.
const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
LOG_INFO(HW_GPU, "Binding storage buffer for cbuf index {}, MemoryLayoutSize 0x{:X}",
cbuf_index, memory_layout_size);
return memory_layout_size;
}();
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);

View File

@@ -57,6 +57,8 @@ MICROPROFILE_DECLARE(GPU_PrepareBuffers);
MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
MICROPROFILE_DECLARE(GPU_DownloadMemory);
using BufferId = SlotId;
using VideoCore::Surface::PixelFormat;
using namespace Common::Literals;
@@ -379,8 +381,6 @@ private:
void RunGarbageCollector();
void WaitOnAsyncFlushes(VAddr cpu_addr, u64 size);
void BindHostIndexBuffer();
void BindHostVertexBuffers();
@@ -464,9 +464,6 @@ private:
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
[[nodiscard]] VideoCommon::BufferCopies FullDownloadCopies(Buffer& buffer, VAddr cpu_addr,
u64 size, bool clear = true);
void DownloadBufferMemory(Buffer& buffer_id);
void DownloadBufferMemory(Buffer& buffer_id, VAddr cpu_addr, u64 size);
@@ -548,7 +545,6 @@ private:
IntervalSet uncommitted_ranges;
IntervalSet common_ranges;
IntervalSet cached_ranges;
IntervalSet pending_ranges;
std::deque<IntervalSet> committed_ranges;
// Async Buffers
@@ -570,7 +566,6 @@ private:
u64 frame_tick = 0;
u64 total_used_memory = 0;
u64 minimum_memory = 0;
u64 expected_memory = 0;
u64 critical_memory = 0;
BufferId inline_buffer_id;

View File

@@ -111,7 +111,7 @@ const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegist
writer.WriteUe(0);
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
writer.WriteBit(false);
writer.WriteBit(context.h264_parameter_set.pic_order_present_flag != 0);
writer.WriteUe(0);
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
@@ -129,7 +129,7 @@ const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegist
writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(true);
writer.WriteBit(true); // pic_scaling_matrix_present_flag
for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true);

View File

@@ -698,7 +698,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
PipelineStatistics* statistics, bool build_in_parallel) try {
// TODO: Remove this when Intel fixes their shader compiler.
// https://github.com/IGCIT/Intel-GPU-Community-Issue-Tracker-IGCIT/issues/159
if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS &&
!Settings::values.enable_compute_pipelines.GetValue()) {
LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash());
return nullptr;
}

View File

@@ -348,25 +348,12 @@ void RasterizerVulkan::Clear(u32 layer_count) {
const u32 color_attachment = regs.clear_surface.RT;
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
VkClearValue clear_value;
bool is_integer = false;
bool is_signed = false;
size_t int_size = 8;
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; ++i) {
const auto& this_rt = regs.rt[i];
if (this_rt.Address() == 0) {
continue;
}
if (this_rt.format == Tegra::RenderTargetFormat::NONE) {
continue;
}
const auto format =
VideoCore::Surface::PixelFormatFromRenderTargetFormat(this_rt.format);
is_integer = IsPixelFormatInteger(format);
is_signed = IsPixelFormatSignedInteger(format);
int_size = PixelComponentSizeBitsInteger(format);
break;
}
const auto format =
VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format);
bool is_integer = IsPixelFormatInteger(format);
bool is_signed = IsPixelFormatSignedInteger(format);
size_t int_size = PixelComponentSizeBitsInteger(format);
VkClearValue clear_value{};
if (!is_integer) {
std::memcpy(clear_value.color.float32, regs.clear_color.data(),
regs.clear_color.size() * sizeof(f32));

View File

@@ -34,8 +34,8 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
return found != formats.end() ? *found : formats[0];
}
static constexpr VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
bool has_fifo_relaxed) {
static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
bool has_fifo_relaxed) {
// Mailbox doesn't lock the application like FIFO (vsync)
// FIFO present mode locks the framerate to the monitor's refresh rate
Settings::VSyncMode setting = [has_imm, has_mailbox]() {

View File

@@ -47,31 +47,35 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
void(slot_image_views.insert(runtime, NullImageViewParams{}));
void(slot_samplers.insert(runtime, sampler_descriptor));
const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const u64 device_mem_per = device_memory / 100;
minimum_memory = device_mem_per * 25;
expected_memory = device_mem_per * 50;
critical_memory = device_mem_per * 80;
LOG_INFO(HW_GPU, "Texture cache device memory limits: min {} expected {} critical {}",
minimum_memory, expected_memory, critical_memory);
if constexpr (HAS_DEVICE_MEMORY_INFO) {
const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const s64 min_spacing_expected = device_memory - 1_GiB;
const s64 min_spacing_critical = device_memory - 512_MiB;
const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
expected_memory = static_cast<u64>(
std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
critical_memory = static_cast<u64>(
std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
minimum_memory = static_cast<u64>((device_memory - mem_threshold) / 2);
} else {
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
minimum_memory = 0;
}
}
template <class P>
void TextureCache<P>::RunGarbageCollector() {
if (total_used_memory < minimum_memory) {
return;
}
bool is_expected = total_used_memory >= expected_memory;
bool is_critical = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = is_critical ? 10ULL : is_expected ? 25ULL : 50ULL;
size_t num_iterations = is_critical ? 40 : (is_expected ? 20 : 10);
boost::container::small_vector<
std::tuple<ImageId, bool, boost::container::small_vector<BufferImageCopy, 16>>, 40>
to_delete;
u64 total_download_size{0};
u32 largest_download_size{0};
const auto clean_up = [&](ImageId image_id) {
bool high_priority_mode = total_used_memory >= expected_memory;
bool aggressive_mode = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
const auto clean_up = [this, &num_iterations, &high_priority_mode,
&aggressive_mode](ImageId image_id) {
if (num_iterations == 0) {
return true;
}
@@ -82,70 +86,53 @@ void TextureCache<P>::RunGarbageCollector() {
// used by the async decoder thread.
return false;
}
const bool do_download = image.IsSafeDownload() &&
False(image.flags & ImageFlagBits::BadOverlap) &&
(False(image.flags & ImageFlagBits::CostlyLoad) || is_critical);
if (do_download) {
total_download_size += image.unswizzled_size_bytes;
largest_download_size = std::max(largest_download_size, image.unswizzled_size_bytes);
if (!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) {
return false;
}
const bool must_download =
image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
if (!high_priority_mode && must_download) {
return false;
}
if (must_download) {
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
const auto copies = FullDownloadCopies(image.info);
image.DownloadMemory(map, copies);
runtime.Finish();
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
swizzle_data_buffer);
}
if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id);
}
UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5);
if (total_used_memory < critical_memory) {
if (aggressive_mode) {
// Sink the aggresiveness.
num_iterations >>= 2;
aggressive_mode = false;
return false;
}
if (high_priority_mode && total_used_memory < expected_memory) {
num_iterations >>= 1;
high_priority_mode = false;
}
}
to_delete.push_back({image_id, do_download, {}});
return false;
};
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
if (total_download_size > 0) {
auto map = runtime.DownloadStagingBuffer(total_download_size);
for (auto& [image_id, do_download, copies] : to_delete) {
if (!do_download) {
continue;
}
Image& image = slot_images[image_id];
copies = FullDownloadCopies(image.info);
image.DownloadMemory(map, copies);
map.offset += Common::AlignUp(image.unswizzled_size_bytes, 64);
}
runtime.Finish();
swizzle_data_buffer.resize_destructive(Common::AlignUp(largest_download_size, 1024));
u64 offset{0};
for (auto& [image_id, do_download, copies] : to_delete) {
Image& image = slot_images[image_id];
if (do_download) {
for (auto& copy : copies) {
copy.buffer_offset += offset;
}
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
swizzle_data_buffer);
offset += Common::AlignUp(image.unswizzled_size_bytes, 64);
}
if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id);
}
UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5);
}
} else {
for (auto& [image_id, do_download, copies] : to_delete) {
Image& image = slot_images[image_id];
if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id);
}
UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5);
}
}
}
template <class P>
void TextureCache<P>::TickFrame() {
// If we can obtain the memory info, use it instead of the estimate.
if (runtime.CanReportMemoryUsage() && frame_tick % 60 == 0) {
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
}
RunGarbageCollector();
if (total_used_memory > minimum_memory) {
RunGarbageCollector();
}
sentenced_images.Tick();
sentenced_framebuffers.Tick();
sentenced_image_view.Tick();

View File

@@ -3,8 +3,6 @@
#pragma once
#include <boost/container/small_vector.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/texture_cache/slot_vector.h"
@@ -16,7 +14,6 @@ constexpr size_t MAX_MIP_LEVELS = 14;
constexpr SlotId CORRUPT_ID{0xfffffffe};
using BufferId = SlotId;
using ImageId = SlotId;
using ImageMapId = SlotId;
using ImageViewId = SlotId;
@@ -149,12 +146,6 @@ struct BufferCopy {
size_t size;
};
struct BufferCopies {
u64 total_size;
u64 largest_copy;
boost::container::small_vector<BufferCopy, 16> copies;
};
struct SwizzleParameters {
Extent3D num_tiles;
Extent3D block;

View File

@@ -914,7 +914,7 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
}
}
boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const ImageInfo& info) {
std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) {
const Extent3D size = info.size;
const u32 bytes_per_block = BytesPerBlock(info.format);
if (info.type == ImageType::Linear) {
@@ -942,7 +942,7 @@ boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const Ima
u32 host_offset = 0;
boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
std::vector<BufferImageCopy> copies(num_levels);
for (s32 level = 0; level < num_levels; ++level) {
const Extent3D level_size = AdjustMipSize(size, level);
const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size);

View File

@@ -5,7 +5,6 @@
#include <optional>
#include <span>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
#include "common/scratch_buffer.h"
@@ -74,8 +73,7 @@ struct OverlapResult {
void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
std::span<BufferImageCopy> copies);
[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(
const ImageInfo& info);
[[nodiscard]] std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info);
[[nodiscard]] Extent3D MipSize(Extent3D size, u32 level);

View File

@@ -406,6 +406,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
dynamic_state3_blending = false;
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV versions older than 23.1.0 have broken depth clamp dynamic state");
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false;
dynamic_state3_enables = false;
}
}
if (extensions.vertex_input_dynamic_state && is_radv) {
// TODO(ameerj): Blacklist only offending driver versions
@@ -463,6 +471,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
must_emulate_bgr565 = true;
}
if (extensions.push_descriptor && is_intel_anv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version >= VK_MAKE_API_VERSION(0, 22, 3, 0)) {
// Disable VK_KHR_push_descriptor due to
// mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
LOG_WARNING(Render_Vulkan,
"ANV drivers 22.3.0 and later have broken VK_KHR_push_descriptor");
extensions.push_descriptor = false;
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
}
}
if (is_mvk) {
LOG_WARNING(Render_Vulkan,
"MVK driver breaks when using more than 16 vertex attributes/bindings");

View File

@@ -716,6 +716,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
ReadGlobalSetting(Settings::values.enable_compute_pipelines);
ReadGlobalSetting(Settings::values.bg_red);
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
@@ -1366,6 +1367,7 @@ void Config::SaveRendererValues() {
WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
WriteGlobalSetting(Settings::values.enable_compute_pipelines);
WriteGlobalSetting(Settings::values.bg_red);
WriteGlobalSetting(Settings::values.bg_green);
WriteGlobalSetting(Settings::values.bg_blue);

View File

@@ -36,8 +36,9 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
filesystem_tab{std::make_unique<ConfigureFilesystem>(this)},
general_tab{std::make_unique<ConfigureGeneral>(system_, this)},
graphics_tab{std::make_unique<ConfigureGraphics>(system_, this)},
graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)},
graphics_tab{std::make_unique<ConfigureGraphics>(
system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
input_tab{std::make_unique<ConfigureInput>(system_, this)},
network_tab{std::make_unique<ConfigureNetwork>(system_, this)},

View File

@@ -72,8 +72,8 @@ private:
std::unique_ptr<ConfigureDebugTab> debug_tab_tab;
std::unique_ptr<ConfigureFilesystem> filesystem_tab;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
std::unique_ptr<ConfigureInput> input_tab;
std::unique_ptr<ConfigureNetwork> network_tab;

View File

@@ -2,9 +2,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Include this early to include Vulkan headers how we want to
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include <algorithm>
#include <functional>
#include <iosfwd>
#include <iterator>
#include <string>
@@ -74,8 +76,11 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode)
}
}
ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
const std::function<void()>& expose_compute_option_,
QWidget* parent)
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()},
expose_compute_option{expose_compute_option_}, system{system_} {
vulkan_device = Settings::values.vulkan_device.GetValue();
RetrieveVulkanDevices();
@@ -513,8 +518,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
const Common::DynamicLibrary library = OpenLibrary();
const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1, wsi.type);
const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
vk::SurfaceKHR surface = //< needed to view present modes for a device
CreateSurface(instance, wsi);
vk::SurfaceKHR surface = CreateSurface(instance, wsi);
vulkan_devices.clear();
vulkan_devices.reserve(physical_devices.size());
@@ -527,6 +531,17 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
physical_device.GetSurfacePresentModesKHR(*surface);
vulkan_devices.push_back(QString::fromStdString(name));
device_present_modes.push_back(present_modes);
VkPhysicalDeviceDriverProperties driver_properties{};
driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
driver_properties.pNext = nullptr;
VkPhysicalDeviceProperties2 properties{};
properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
properties.pNext = &driver_properties;
dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);
if (driver_properties.driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
expose_compute_option();
}
}
} catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());

View File

@@ -3,6 +3,7 @@
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include <QColor>
@@ -37,7 +38,9 @@ class ConfigureGraphics : public QWidget {
Q_OBJECT
public:
explicit ConfigureGraphics(const Core::System& system_, QWidget* parent = nullptr);
explicit ConfigureGraphics(const Core::System& system_,
const std::function<void()>& expose_compute_option_,
QWidget* parent = nullptr);
~ConfigureGraphics() override;
void ApplyConfiguration();
@@ -81,6 +84,7 @@ private:
// selection in the combobox
u32 vulkan_device{};
Settings::ShaderBackend shader_backend{};
const std::function<void()>& expose_compute_option;
const Core::System& system;
};

View File

@@ -15,6 +15,8 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(const Core::System& system_
SetupPerGameUI();
SetConfiguration();
ui->enable_compute_pipelines_checkbox->setVisible(false);
}
ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
@@ -27,6 +29,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->async_astc->setEnabled(runtime_lock);
ui->use_asynchronous_shaders->setEnabled(runtime_lock);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock);
ui->async_present->setChecked(Settings::values.async_presentation.GetValue());
ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
@@ -36,6 +39,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
ui->use_vulkan_driver_pipeline_cache->setChecked(
Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
ui->enable_compute_pipelines_checkbox->setChecked(
Settings::values.enable_compute_pipelines.GetValue());
if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setCurrentIndex(
@@ -74,6 +79,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
ui->use_vulkan_driver_pipeline_cache,
use_vulkan_driver_pipeline_cache);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
ui->enable_compute_pipelines_checkbox,
enable_compute_pipelines);
}
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -104,6 +112,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
Settings::values.max_anisotropy.UsingGlobal());
ui->enable_compute_pipelines_checkbox->setEnabled(
Settings::values.enable_compute_pipelines.UsingGlobal());
return;
}
@@ -125,6 +135,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
Settings::values.use_vulkan_driver_pipeline_cache,
use_vulkan_driver_pipeline_cache);
ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox,
Settings::values.enable_compute_pipelines,
enable_compute_pipelines);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
@@ -132,3 +145,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->anisotropic_filtering_combobox, ui->af_label,
static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
}
void ConfigureGraphicsAdvanced::ExposeComputeOption() {
ui->enable_compute_pipelines_checkbox->setVisible(true);
}

View File

@@ -28,6 +28,8 @@ public:
void ApplyConfiguration();
void SetConfiguration();
void ExposeComputeOption();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
@@ -44,6 +46,7 @@ private:
ConfigurationShared::CheckState use_asynchronous_shaders;
ConfigurationShared::CheckState use_fast_gpu_time;
ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
ConfigurationShared::CheckState enable_compute_pipelines;
const Core::System& system;
};

View File

@@ -136,6 +136,17 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enable_compute_pipelines_checkbox">
<property name="toolTip">
<string>Enable compute pipelines, required by some games. This setting only exists for Intel proprietary drivers, and may crash if enabled.
Compute pipelines are always enabled on all other drivers.</string>
</property>
<property name="text">
<string>Enable Compute Pipelines (Intel Vulkan only)</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="af_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_1">

View File

@@ -48,7 +48,9 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
connect(poll_timer.get(), &QTimer::timeout, [this] {
const auto buttons = controller->GetNpadButtons();
if (buttons.raw != Core::HID::NpadButton::None) {
const auto home_pressed = controller->GetHomeButtons().home != 0;
const auto capture_pressed = controller->GetCaptureButtons().capture != 0;
if (home_pressed || capture_pressed) {
SetPollingResult(buttons.raw, false);
return;
}
@@ -154,8 +156,10 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
model->setData(index, previous_key);
return;
}
const QString button_string = tr("Home+%1").arg(GetButtonName(button));
const auto home_pressed = this->controller->GetHomeButtons().home != 0;
const auto capture_pressed = this->controller->GetCaptureButtons().capture != 0;
const QString button_string =
GetButtonCombinationName(button, home_pressed, capture_pressed);
const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string);
@@ -174,72 +178,83 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
poll_timer->start(200); // Check for new inputs every 200ms
// We need to disable configuration to be able to read npad buttons
controller->DisableConfiguration();
controller->DisableSystemButtons();
}
void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) {
timeout_timer->stop();
poll_timer->stop();
(*input_setter)(button, cancel);
// Re-Enable configuration
controller->EnableConfiguration();
controller->EnableSystemButtons();
(*input_setter)(button, cancel);
input_setter = std::nullopt;
}
QString ConfigureHotkeys::GetButtonName(Core::HID::NpadButton button) const {
QString ConfigureHotkeys::GetButtonCombinationName(Core::HID::NpadButton button,
const bool home = false,
const bool capture = false) const {
Core::HID::NpadButtonState state{button};
QString button_combination;
if (home) {
button_combination.append(QStringLiteral("Home+"));
}
if (capture) {
button_combination.append(QStringLiteral("Screenshot+"));
}
if (state.a) {
return QStringLiteral("A");
button_combination.append(QStringLiteral("A+"));
}
if (state.b) {
return QStringLiteral("B");
button_combination.append(QStringLiteral("B+"));
}
if (state.x) {
return QStringLiteral("X");
button_combination.append(QStringLiteral("X+"));
}
if (state.y) {
return QStringLiteral("Y");
button_combination.append(QStringLiteral("Y+"));
}
if (state.l || state.right_sl || state.left_sl) {
return QStringLiteral("L");
button_combination.append(QStringLiteral("L+"));
}
if (state.r || state.right_sr || state.left_sr) {
return QStringLiteral("R");
button_combination.append(QStringLiteral("R+"));
}
if (state.zl) {
return QStringLiteral("ZL");
button_combination.append(QStringLiteral("ZL+"));
}
if (state.zr) {
return QStringLiteral("ZR");
button_combination.append(QStringLiteral("ZR+"));
}
if (state.left) {
return QStringLiteral("Dpad_Left");
button_combination.append(QStringLiteral("Dpad_Left+"));
}
if (state.right) {
return QStringLiteral("Dpad_Right");
button_combination.append(QStringLiteral("Dpad_Right+"));
}
if (state.up) {
return QStringLiteral("Dpad_Up");
button_combination.append(QStringLiteral("Dpad_Up+"));
}
if (state.down) {
return QStringLiteral("Dpad_Down");
button_combination.append(QStringLiteral("Dpad_Down+"));
}
if (state.stick_l) {
return QStringLiteral("Left_Stick");
button_combination.append(QStringLiteral("Left_Stick+"));
}
if (state.stick_r) {
return QStringLiteral("Right_Stick");
button_combination.append(QStringLiteral("Right_Stick+"));
}
if (state.minus) {
return QStringLiteral("Minus");
button_combination.append(QStringLiteral("Minus+"));
}
if (state.plus) {
return QStringLiteral("Plus");
button_combination.append(QStringLiteral("Plus+"));
}
if (button_combination.isEmpty()) {
return tr("Invalid");
} else {
button_combination.chop(1);
return button_combination;
}
return tr("Invalid");
}
std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {

View File

@@ -59,7 +59,7 @@ private:
QStandardItemModel* model;
void SetPollingResult(Core::HID::NpadButton button, bool cancel);
QString GetButtonName(Core::HID::NpadButton button) const;
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;
Core::HID::EmulatedController* controller;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;

View File

@@ -48,8 +48,9 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
audio_tab = std::make_unique<ConfigureAudio>(system_, this);
cpu_tab = std::make_unique<ConfigureCpu>(system_, this);
general_tab = std::make_unique<ConfigureGeneral>(system_, this);
graphics_tab = std::make_unique<ConfigureGraphics>(system_, this);
graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this);
graphics_tab = std::make_unique<ConfigureGraphics>(
system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this);
input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
system_tab = std::make_unique<ConfigureSystem>(system_, this);

View File

@@ -75,8 +75,8 @@ private:
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCpu> cpu_tab;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureInputPerGame> input_tab;
std::unique_ptr<ConfigureSystem> system_tab;
};

View File

@@ -1164,7 +1164,8 @@ void GMainWindow::InitializeRecentFileMenuActions() {
UpdateRecentFiles();
}
void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name) {
void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
const bool tas_allowed) {
static const QString main_window = QStringLiteral("Main Window");
action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name));
action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name));
@@ -1176,7 +1177,14 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name
const auto* controller_hotkey =
hotkey_registry.GetControllerHotkey(main_window, action_name, controller);
connect(
controller_hotkey, &ControllerShortcut::Activated, this, [action] { action->trigger(); },
controller_hotkey, &ControllerShortcut::Activated, this,
[action, tas_allowed, this] {
auto [tas_status, current_tas_frame, total_tas_frames] =
input_subsystem->GetTas()->GetStatus();
if (tas_allowed || tas_status == InputCommon::TasInput::TasState::Stopped) {
action->trigger();
}
},
Qt::QueuedConnection);
}
@@ -1193,9 +1201,9 @@ void GMainWindow::InitializeHotkeys() {
LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"));
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"));
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"));
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
static const QString main_window = QStringLiteral("Main Window");
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {

View File

@@ -214,7 +214,8 @@ public slots:
private:
/// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry.
void LinkActionShortcut(QAction* action, const QString& action_name);
void LinkActionShortcut(QAction* action, const QString& action_name,
const bool tas_allowed = false);
void RegisterMetaTypes();