From bfb7cbc2922655e8a7a9e6f8bb80a186d4d75495 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Thu, 10 Jun 2021 16:25:25 -0400 Subject: [PATCH 001/245] program_metadata: Unpack FileAccessHeader and FileAccessControl Avoids a reference binding to a misaligned addresses. Unpacking one requires unpacking the other, otherwise there'll be a misaligned address on the leftover one. --- src/core/file_sys/program_metadata.cpp | 52 +++++++++++++++++++++++--- src/core/file_sys/program_metadata.h | 14 ++----- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 4e46c24cf4..484d4baea3 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -34,11 +34,55 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { return Loader::ResultStatus::ErrorBadACIHeader; } - if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) { + // Load acid_file_access per-component instead of the entire struct, since this struct does not + // reflect the layout of the real data. + std::size_t current_offset = acid_header.fac_offset; + if (sizeof(FileAccessControl::version) != file->ReadBytes(&acid_file_access.version, + sizeof(FileAccessControl::version), + current_offset)) { + return Loader::ResultStatus::ErrorBadFileAccessControl; + } + if (sizeof(FileAccessControl::permissions) != + file->ReadBytes(&acid_file_access.permissions, sizeof(FileAccessControl::permissions), + current_offset += sizeof(FileAccessControl::version) + 3)) { + return Loader::ResultStatus::ErrorBadFileAccessControl; + } + if (sizeof(FileAccessControl::unknown) != + file->ReadBytes(&acid_file_access.unknown, sizeof(FileAccessControl::unknown), + current_offset + sizeof(FileAccessControl::permissions))) { return Loader::ResultStatus::ErrorBadFileAccessControl; } - if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) { + // Load aci_file_access per-component instead of the entire struct, same as acid_file_access + current_offset = aci_header.fah_offset; + if (sizeof(FileAccessHeader::version) != file->ReadBytes(&aci_file_access.version, + sizeof(FileAccessHeader::version), + current_offset)) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::permissions) != + file->ReadBytes(&aci_file_access.permissions, sizeof(FileAccessHeader::permissions), + current_offset += sizeof(FileAccessHeader::version) + 3)) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_offset) != + file->ReadBytes(&aci_file_access.unk_offset, sizeof(FileAccessHeader::unk_offset), + current_offset += sizeof(FileAccessHeader::permissions))) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_size) != + file->ReadBytes(&aci_file_access.unk_size, sizeof(FileAccessHeader::unk_size), + current_offset += sizeof(FileAccessHeader::unk_offset))) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_offset_2) != + file->ReadBytes(&aci_file_access.unk_offset_2, sizeof(FileAccessHeader::unk_offset_2), + current_offset += sizeof(FileAccessHeader::unk_size))) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_size_2) != + file->ReadBytes(&aci_file_access.unk_size_2, sizeof(FileAccessHeader::unk_size_2), + current_offset + sizeof(FileAccessHeader::unk_offset_2))) { return Loader::ResultStatus::ErrorBadFileAccessHeader; } @@ -153,9 +197,7 @@ void ProgramMetadata::Print() const { LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); - u64_le permissions_l; // local copy to fix alignment error - std::memcpy(&permissions_l, &acid_file_access.permissions, sizeof(permissions_l)); - LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", permissions_l); + LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); // Begin ACI0 printing (actual perms, unsigned) LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 1eee916be9..c89a1c4455 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -143,20 +143,18 @@ private: static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong"); -#pragma pack(push, 1) - + // FileAccessControl and FileAccessHeader need loaded per-component: this layout does not + // reflect the real layout to avoid reference binding to misaligned addresses struct FileAccessControl { u8 version; - INSERT_PADDING_BYTES(3); + // 3 padding bytes u64_le permissions; std::array unknown; }; - static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong"); - struct FileAccessHeader { u8 version; - INSERT_PADDING_BYTES(3); + // 3 padding bytes u64_le permissions; u32_le unk_offset; u32_le unk_size; @@ -164,10 +162,6 @@ private: u32_le unk_size_2; }; - static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong"); - -#pragma pack(pop) - Header npdm_header; AciHeader aci_header; AcidHeader acid_header; From f5e635addaef59159bf6bc529b17954eda3684a1 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Sun, 31 Jul 2022 04:46:26 +0200 Subject: [PATCH 002/245] ldn: Initial implementation --- src/core/CMakeLists.txt | 2 + src/core/hle/service/ldn/lan_discovery.cpp | 644 +++++++++++++++++++++ src/core/hle/service/ldn/lan_discovery.h | 133 +++++ src/core/hle/service/ldn/ldn.cpp | 233 ++++---- src/core/hle/service/ldn/ldn_types.h | 50 +- src/core/internal_network/socket_proxy.cpp | 8 +- src/dedicated_room/yuzu_room.cpp | 3 +- src/network/room.cpp | 63 ++ src/network/room.h | 1 + src/network/room_member.cpp | 57 ++ src/network/room_member.h | 35 +- src/yuzu/main.cpp | 4 +- src/yuzu/main.ui | 14 + src/yuzu/multiplayer/chat_room.cpp | 12 +- src/yuzu/multiplayer/state.cpp | 1 + 15 files changed, 1134 insertions(+), 126 deletions(-) create mode 100644 src/core/hle/service/ldn/lan_discovery.cpp create mode 100644 src/core/hle/service/ldn/lan_discovery.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 806e7ff6c0..52017878c9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -500,6 +500,8 @@ add_library(core STATIC hle/service/jit/jit.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h + hle/service/ldn/lan_discovery.cpp + hle/service/ldn/lan_discovery.h hle/service/ldn/ldn_results.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp new file mode 100644 index 0000000000..b04c990771 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -0,0 +1,644 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ldn/lan_discovery.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" + +namespace Service::LDN { + +LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_) + : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_), + discovery(discovery_) {} + +LanStation::~LanStation() = default; + +NodeStatus LanStation::GetStatus() const { + return status; +} + +void LanStation::OnClose() { + LOG_INFO(Service_LDN, "OnClose {}", node_id); + Reset(); + discovery->UpdateNodes(); +} + +void LanStation::Reset() { + status = NodeStatus::Disconnected; +}; + +void LanStation::OverrideInfo() { + bool connected = GetStatus() == NodeStatus::Connected; + node_info->node_id = node_id; + node_info->is_connected = connected ? 1 : 0; +} + +LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_) + : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}), + room_network{room_network_} { + LOG_INFO(Service_LDN, "LANDiscovery"); +} + +LANDiscovery::~LANDiscovery() { + LOG_INFO(Service_LDN, "~LANDiscovery"); + if (inited) { + Result rc = Finalize(); + LOG_INFO(Service_LDN, "Finalize: {}", rc.raw); + } +} + +void LANDiscovery::InitNetworkInfo() { + network_info.common.bssid = GetFakeMac(); + network_info.common.channel = WifiChannel::Wifi24_6; + network_info.common.link_level = LinkLevel::Good; + network_info.common.network_type = PackedNetworkType::Ldn; + network_info.common.ssid = fake_ssid; + + auto& nodes = network_info.ldn.nodes; + for (std::size_t i = 0; i < NodeCountMax; i++) { + nodes[i].node_id = static_cast(i); + nodes[i].is_connected = 0; + } +} + +void LANDiscovery::InitNodeStateChange() { + for (auto& node_update : nodeChanges) { + node_update.state_change = NodeStateChange::None; + } + for (auto& node_state : node_last_states) { + node_state = 0; + } +} + +State LANDiscovery::GetState() const { + return state; +} + +void LANDiscovery::SetState(State new_state) { + state = new_state; +} + +Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const { + if (state == State::AccessPointCreated || state == State::StationConnected) { + std::memcpy(&out_network, &network_info, sizeof(network_info)); + return ResultSuccess; + } + + return ResultBadState; +} + +Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, + std::vector& out_updates, + std::size_t buffer_count) { + if (buffer_count > NodeCountMax) { + return ResultInvalidBufferCount; + } + + if (state == State::AccessPointCreated || state == State::StationConnected) { + std::memcpy(&out_network, &network_info, sizeof(network_info)); + for (std::size_t i = 0; i < buffer_count; i++) { + out_updates[i].state_change = nodeChanges[i].state_change; + nodeChanges[i].state_change = NodeStateChange::None; + } + return ResultSuccess; + } + + return ResultBadState; +} + +DisconnectReason LANDiscovery::GetDisconnectReason() const { + return disconnect_reason; +} + +Result LANDiscovery::Scan(std::vector& networks, u16& count, + const ScanFilter& filter) { + if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) || + filter.network_type <= NetworkType::All) { + if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) { + return ResultBadInput; + } + } + + { + std::scoped_lock lock{packet_mutex}; + scan_results.clear(); + + SendBroadcast(Network::LDNPacketType::Scan); + } + + LOG_INFO(Service_LDN, "Waiting for scan replies"); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + std::scoped_lock lock{packet_mutex}; + for (const auto& [key, info] : scan_results) { + if (count >= networks.size()) { + break; + } + + if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) { + if (filter.network_id.intent_id.local_communication_id != + info.network_id.intent_id.local_communication_id) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) { + if (filter.network_id.session_id != info.network_id.session_id) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) { + if (filter.network_type != static_cast(info.common.network_type)) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) { + if (filter.ssid != info.common.ssid) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) { + if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) { + continue; + } + } + + networks[count++] = info; + } + + return ResultSuccess; +} + +Result LANDiscovery::SetAdvertiseData(std::vector& data) { + std::scoped_lock lock{packet_mutex}; + std::size_t size = data.size(); + if (size > AdvertiseDataSizeMax) { + return ResultAdvertiseDataTooLarge; + } + + std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size); + network_info.ldn.advertise_data_size = static_cast(size); + + UpdateNodes(); + + return ResultSuccess; +} + +Result LANDiscovery::OpenAccessPoint() { + std::scoped_lock lock{packet_mutex}; + disconnect_reason = DisconnectReason::None; + if (state == State::None) { + return ResultBadState; + } + + ResetStations(); + SetState(State::AccessPointOpened); + + return ResultSuccess; +} + +Result LANDiscovery::CloseAccessPoint() { + std::scoped_lock lock{packet_mutex}; + if (state == State::None) { + return ResultBadState; + } + + if (state == State::AccessPointCreated) { + DestroyNetwork(); + } + + ResetStations(); + SetState(State::Initialized); + + return ResultSuccess; +} + +Result LANDiscovery::OpenStation() { + std::scoped_lock lock{packet_mutex}; + disconnect_reason = DisconnectReason::None; + if (state == State::None) { + return ResultBadState; + } + + ResetStations(); + SetState(State::StationOpened); + + return ResultSuccess; +} + +Result LANDiscovery::CloseStation() { + std::scoped_lock lock{packet_mutex}; + if (state == State::None) { + return ResultBadState; + } + + if (state == State::StationConnected) { + Disconnect(); + } + + ResetStations(); + SetState(State::Initialized); + + return ResultSuccess; +} + +Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config, + const UserConfig& user_config, + const NetworkConfig& network_config) { + std::scoped_lock lock{packet_mutex}; + + if (state != State::AccessPointOpened) { + return ResultBadState; + } + + InitNetworkInfo(); + network_info.ldn.node_count_max = network_config.node_count_max; + network_info.ldn.security_mode = security_config.security_mode; + + if (network_config.channel == WifiChannel::Default) { + network_info.common.channel = WifiChannel::Wifi24_6; + } else { + network_info.common.channel = network_config.channel; + } + + std::independent_bits_engine bits_engine; + network_info.network_id.session_id.high = bits_engine(); + network_info.network_id.session_id.low = bits_engine(); + network_info.network_id.intent_id = network_config.intent_id; + + NodeInfo& node0 = network_info.ldn.nodes[0]; + const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version); + if (rc2.IsError()) { + return ResultAccessPointConnectionFailed; + } + + SetState(State::AccessPointCreated); + + InitNodeStateChange(); + node0.is_connected = 1; + UpdateNodes(); + + return rc2; +} + +Result LANDiscovery::DestroyNetwork() { + for (auto local_ip : connected_clients) { + SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip); + } + + ResetStations(); + + SetState(State::AccessPointOpened); + LanEvent(); + + return ResultSuccess; +} + +Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config, + u16 local_communication_version) { + std::scoped_lock lock{packet_mutex}; + if (network_info_.ldn.node_count == 0) { + return ResultInvalidNodeCount; + } + + Result rc = GetNodeInfo(node_info, user_config, local_communication_version); + if (rc.IsError()) { + return ResultConnectionFailed; + } + + Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address; + std::reverse(std::begin(node_host), std::end(node_host)); // htonl + host_ip = node_host; + SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip); + + InitNodeStateChange(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + return ResultSuccess; +} + +Result LANDiscovery::Disconnect() { + if (host_ip) { + SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip); + } + + SetState(State::StationOpened); + LanEvent(); + + return ResultSuccess; +} + +Result LANDiscovery::Initialize(LanEventFunc lan_event, bool listening) { + std::scoped_lock lock{packet_mutex}; + if (inited) { + return ResultSuccess; + } + + for (auto& station : stations) { + station.discovery = this; + station.node_info = &network_info.ldn.nodes[station.node_id]; + station.Reset(); + } + + connected_clients.clear(); + LanEvent = lan_event; + + SetState(State::Initialized); + + inited = true; + return ResultSuccess; +} + +Result LANDiscovery::Finalize() { + std::scoped_lock lock{packet_mutex}; + Result rc = ResultSuccess; + + if (inited) { + if (state == State::AccessPointCreated) { + DestroyNetwork(); + } + if (state == State::StationConnected) { + Disconnect(); + } + + ResetStations(); + inited = false; + } + + SetState(State::None); + + return rc; +} + +void LANDiscovery::ResetStations() { + for (auto& station : stations) { + station.Reset(); + } + connected_clients.clear(); +} + +void LANDiscovery::UpdateNodes() { + u8 count = 0; + for (auto& station : stations) { + bool connected = station.GetStatus() == NodeStatus::Connected; + if (connected) { + count++; + } + station.OverrideInfo(); + } + network_info.ldn.node_count = count + 1; + + for (auto local_ip : connected_clients) { + SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip); + } + + OnNetworkInfoChanged(); +} + +void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) { + network_info = info; + if (state == State::StationOpened) { + SetState(State::StationConnected); + } + OnNetworkInfoChanged(); +} + +void LANDiscovery::OnDisconnectFromHost() { + LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast(state)); + host_ip = std::nullopt; + if (state == State::StationConnected) { + SetState(State::StationOpened); + LanEvent(); + } +} + +void LANDiscovery::OnNetworkInfoChanged() { + if (IsNodeStateChanged()) { + LanEvent(); + } + return; +} + +Network::IPv4Address LANDiscovery::GetLocalIp() const { + Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF}; + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + local_ip = room_member->GetFakeIpAddress(); + } + } + return local_ip; +} + +template +void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data, + Ipv4Address remote_ip) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = false; + packet.local_ip = GetLocalIp(); + packet.remote_ip = remote_ip; + + packet.data.clear(); + packet.data.resize(sizeof(data)); + std::memcpy(packet.data.data(), &data, sizeof(data)); + SendPacket(packet); +} + +void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = false; + packet.local_ip = GetLocalIp(); + packet.remote_ip = remote_ip; + + packet.data.clear(); + SendPacket(packet); +} + +template +void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = true; + packet.local_ip = GetLocalIp(); + + packet.data.clear(); + packet.data.resize(sizeof(data)); + std::memcpy(packet.data.data(), &data, sizeof(data)); + SendPacket(packet); +} + +void LANDiscovery::SendBroadcast(Network::LDNPacketType type) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = true; + packet.local_ip = GetLocalIp(); + + packet.data.clear(); + SendPacket(packet); +} + +void LANDiscovery::SendPacket(const Network::LDNPacket& packet) { + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + room_member->SendLdnPacket(packet); + } + } +} + +void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { + std::scoped_lock lock{packet_mutex}; + switch (packet.type) { + case Network::LDNPacketType::Scan: { + LOG_INFO(Frontend, "Scan packet received!"); + if (state == State::AccessPointCreated) { + // Reply to the sender + SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip); + } + break; + } + case Network::LDNPacketType::ScanResp: { + LOG_INFO(Frontend, "ScanResp packet received!"); + + NetworkInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + scan_results.insert({info.common.bssid, info}); + + break; + } + case Network::LDNPacketType::Connect: { + LOG_INFO(Frontend, "Connect packet received!"); + + NodeInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); + + connected_clients.push_back(packet.local_ip); + + for (LanStation& station : stations) { + if (station.status != NodeStatus::Connected) { + *station.node_info = info; + station.status = NodeStatus::Connected; + break; + } + } + + UpdateNodes(); + + break; + } + case Network::LDNPacketType::Disconnect: { + LOG_INFO(Frontend, "Disconnect packet received!"); + + connected_clients.erase( + std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip), + connected_clients.end()); + + NodeInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); + + for (LanStation& station : stations) { + if (station.status == NodeStatus::Connected && + station.node_info->mac_address == info.mac_address) { + station.OnClose(); + break; + } + } + + break; + } + case Network::LDNPacketType::DestroyNetwork: { + ResetStations(); + OnDisconnectFromHost(); + break; + } + case Network::LDNPacketType::SyncNetwork: { + if (state == State::StationOpened || state == State::StationConnected) { + LOG_INFO(Frontend, "SyncNetwork packet received!"); + NetworkInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + + OnSyncNetwork(info); + } else { + LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!"); + } + + break; + } + default: { + LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast(packet.type)); + break; + } + } +} + +bool LANDiscovery::IsNodeStateChanged() { + bool changed = false; + const auto& nodes = network_info.ldn.nodes; + for (int i = 0; i < NodeCountMax; i++) { + if (nodes[i].is_connected != node_last_states[i]) { + if (nodes[i].is_connected) { + nodeChanges[i].state_change |= NodeStateChange::Connect; + } else { + nodeChanges[i].state_change |= NodeStateChange::Disconnect; + } + node_last_states[i] = nodes[i].is_connected; + changed = true; + } + } + return changed; +} + +bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const { + const auto flag_value = static_cast(flag); + const auto search_flag_value = static_cast(search_flag); + return (flag_value & search_flag_value) == search_flag_value; +} + +int LANDiscovery::GetStationCount() { + int count = 0; + for (const auto& station : stations) { + if (station.GetStatus() != NodeStatus::Disconnected) { + count++; + } + } + + return count; +} + +MacAddress LANDiscovery::GetFakeMac() const { + MacAddress mac{}; + mac.raw[0] = 0x02; + mac.raw[1] = 0x00; + + const auto ip = GetLocalIp(); + memcpy(mac.raw.data() + 2, &ip, sizeof(ip)); + + return mac; +} + +Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig, + u16 localCommunicationVersion) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface available"); + return ResultNoIpAddress; + } + + node.mac_address = GetFakeMac(); + node.is_connected = 1; + std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1); + node.local_communication_version = localCommunicationVersion; + + Ipv4Address current_address = GetLocalIp(); + std::reverse(std::begin(current_address), std::end(current_address)); // ntohl + node.ipv4_address = current_address; + + return ResultSuccess; +} + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h new file mode 100644 index 0000000000..255342456e --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.h @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/logging/log.h" +#include "common/socket_types.h" +#include "core/hle/result.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "network/network.h" + +namespace Service::LDN { + +class LANDiscovery; + +class LanStation { +public: + LanStation(s8 node_id_, LANDiscovery* discovery_); + ~LanStation(); + + void OnClose(); + NodeStatus GetStatus() const; + void Reset(); + void OverrideInfo(); + +protected: + friend class LANDiscovery; + NodeInfo* node_info; + NodeStatus status; + s8 node_id; + LANDiscovery* discovery; +}; + +class LANDiscovery { +public: + typedef std::function LanEventFunc; + + LANDiscovery(Network::RoomNetwork& room_network_); + ~LANDiscovery(); + + State GetState() const; + void SetState(State new_state); + + Result GetNetworkInfo(NetworkInfo& out_network) const; + Result GetNetworkInfo(NetworkInfo& out_network, std::vector& out_updates, + std::size_t buffer_count); + + DisconnectReason GetDisconnectReason() const; + Result Scan(std::vector& networks, u16& count, const ScanFilter& filter); + Result SetAdvertiseData(std::vector& data); + + Result OpenAccessPoint(); + Result CloseAccessPoint(); + + Result OpenStation(); + Result CloseStation(); + + Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config, + const NetworkConfig& network_config); + Result DestroyNetwork(); + + Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config, + u16 local_communication_version); + Result Disconnect(); + + Result Initialize(LanEventFunc lan_event = empty_func, bool listening = true); + Result Finalize(); + + void ReceivePacket(const Network::LDNPacket& packet); + +protected: + friend class LanStation; + + void InitNetworkInfo(); + void InitNodeStateChange(); + + void ResetStations(); + void UpdateNodes(); + + void OnSyncNetwork(const NetworkInfo& info); + void OnDisconnectFromHost(); + void OnNetworkInfoChanged(); + + bool IsNodeStateChanged(); + bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const; + int GetStationCount(); + MacAddress GetFakeMac() const; + Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config, + u16 local_communication_version); + + Network::IPv4Address GetLocalIp() const; + template + void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip); + void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip); + template + void SendBroadcast(Network::LDNPacketType type, const Data& data); + void SendBroadcast(Network::LDNPacketType type); + void SendPacket(const Network::LDNPacket& packet); + + static const LanEventFunc empty_func; + const Ssid fake_ssid{"YuzuFakeSsidForLdn"}; + + bool inited{}; + std::mutex packet_mutex; + std::array stations; + std::array nodeChanges{}; + std::array node_last_states{}; + std::unordered_map scan_results{}; + NodeInfo node_info{}; + NetworkInfo network_info{}; + State state{State::None}; + DisconnectReason disconnect_reason{DisconnectReason::None}; + + // TODO (flTobi): Should this be an std::set? + std::vector connected_clients; + std::optional host_ip = std::nullopt; + + LanEventFunc LanEvent; + + Network::RoomNetwork& room_network; +}; +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index c11daff547..6537f49cf2 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -4,11 +4,13 @@ #include #include "core/core.h" +#include "core/hle/service/ldn/lan_discovery.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldn/ldn_results.h" #include "core/hle/service/ldn/ldn_types.h" #include "core/internal_network/network.h" #include "core/internal_network/network_interface.h" +#include "network/network.h" // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent #undef CreateEvent @@ -105,13 +107,13 @@ class IUserLocalCommunicationService final public: explicit IUserLocalCommunicationService(Core::System& system_) : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, - service_context{system, "IUserLocalCommunicationService"}, room_network{ - system_.GetRoomNetwork()} { + service_context{system, "IUserLocalCommunicationService"}, + room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { // clang-format off static const FunctionInfo functions[] = { {0, &IUserLocalCommunicationService::GetState, "GetState"}, {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, - {2, nullptr, "GetIpv4Address"}, + {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"}, {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, @@ -119,7 +121,7 @@ public: {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, {102, &IUserLocalCommunicationService::Scan, "Scan"}, {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, - {104, nullptr, "SetWirelessControllerRestriction"}, + {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"}, {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, @@ -148,16 +150,30 @@ public: } ~IUserLocalCommunicationService() { + if (is_initialized) { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } + } + service_context.CloseEvent(state_change_event); } + /// Callback to parse and handle a received LDN packet. + void OnLDNPacketReceived(const Network::LDNPacket& packet) { + lan_discovery.ReceivePacket(packet); + } + void OnEventFired() { state_change_event->GetWritableEvent().Signal(); } void GetState(Kernel::HLERequestContext& ctx) { State state = State::Error; - LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state); + + if (is_initialized) { + state = lan_discovery.GetState(); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -175,7 +191,7 @@ public: } NetworkInfo network_info{}; - const auto rc = ResultSuccess; + const auto rc = lan_discovery.GetNetworkInfo(network_info); if (rc.IsError()) { LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); IPC::ResponseBuilder rb{ctx, 2}; @@ -183,28 +199,52 @@ public: return; } - LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", - network_info.common.ssid.GetStringValue(), network_info.ldn.node_count); - ctx.WriteBuffer(network_info); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(rc); + rb.Push(ResultSuccess); + } + + void GetIpv4Address(Kernel::HLERequestContext& ctx) { + LOG_CRITICAL(Service_LDN, "called"); + + const auto network_interface = Network::GetSelectedNetworkInterface(); + + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface available"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNoIpAddress); + return; + } + + Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)}; + Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)}; + + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + current_address = room_member->GetFakeIpAddress(); + } + } + + std::reverse(std::begin(current_address), std::end(current_address)); // ntohl + std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(current_address); + rb.PushRaw(subnet_mask); } void GetDisconnectReason(Kernel::HLERequestContext& ctx) { - const auto disconnect_reason = DisconnectReason::None; - - LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(disconnect_reason); + rb.PushEnum(lan_discovery.GetDisconnectReason()); } void GetSecurityParameter(Kernel::HLERequestContext& ctx) { SecurityParameter security_parameter{}; NetworkInfo info{}; - const Result rc = ResultSuccess; + const Result rc = lan_discovery.GetNetworkInfo(info); if (rc.IsError()) { LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); @@ -217,8 +257,6 @@ public: std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), sizeof(SecurityParameter::data)); - LOG_WARNING(Service_LDN, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 10}; rb.Push(rc); rb.PushRaw(security_parameter); @@ -227,7 +265,7 @@ public: void GetNetworkConfig(Kernel::HLERequestContext& ctx) { NetworkConfig config{}; NetworkInfo info{}; - const Result rc = ResultSuccess; + const Result rc = lan_discovery.GetNetworkInfo(info); if (rc.IsError()) { LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); @@ -241,12 +279,6 @@ public: config.node_count_max = info.ldn.node_count_max; config.local_communication_version = info.ldn.nodes[0].local_communication_version; - LOG_WARNING(Service_LDN, - "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, " - "local_communication_version={}", - config.intent_id.local_communication_id, config.intent_id.scene_id, - config.channel, config.node_count_max, config.local_communication_version); - IPC::ResponseBuilder rb{ctx, 10}; rb.Push(rc); rb.PushRaw(config); @@ -265,17 +297,17 @@ public: const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { - LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size, + LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, node_buffer_count); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultBadInput); return; } - NetworkInfo info; + NetworkInfo info{}; std::vector latest_update(node_buffer_count); - const auto rc = ResultSuccess; + const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size()); if (rc.IsError()) { LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); IPC::ResponseBuilder rb{ctx, 2}; @@ -283,9 +315,6 @@ public: return; } - LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", - info.common.ssid.GetStringValue(), info.ldn.node_count); - ctx.WriteBuffer(info, 0); ctx.WriteBuffer(latest_update, 1); @@ -317,92 +346,78 @@ public: u16 count = 0; std::vector network_infos(network_info_size); + Result rc = lan_discovery.Scan(network_infos, count, scan_filter); - LOG_WARNING(Service_LDN, - "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}", - channel, scan_filter.flag, scan_filter.network_type); + LOG_INFO(Service_LDN, + "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}", + channel, scan_filter.flag, scan_filter.network_type, is_private); ctx.WriteBuffer(network_infos); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); + rb.Push(rc); rb.Push(count); } - void OpenAccessPoint(Kernel::HLERequestContext& ctx) { + void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } + void OpenAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.OpenAccessPoint()); + } + void CloseAccessPoint(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + LOG_INFO(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.CloseAccessPoint()); } void CreateNetwork(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - SecurityConfig security_config; - UserConfig user_config; - INSERT_PADDING_WORDS_NOINIT(1); - NetworkConfig network_config; - }; - static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size."); + LOG_INFO(Service_LDN, "called"); - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_LDN, - "(STUBBED) called, passphrase_size={}, security_mode={}, " - "local_communication_version={}", - parameters.security_config.passphrase_size, - parameters.security_config.security_mode, - parameters.network_config.local_communication_version); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + CreateNetworkImpl(ctx); } void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + CreateNetworkImpl(ctx, true); + } + + void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { IPC::RequestParser rp{ctx}; - struct Parameters { - SecurityConfig security_config; - SecurityParameter security_parameter; - UserConfig user_config; - NetworkConfig network_config; - }; - static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size."); - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_LDN, - "(STUBBED) called, passphrase_size={}, security_mode={}, " - "local_communication_version={}", - parameters.security_config.passphrase_size, - parameters.security_config.security_mode, - parameters.network_config.local_communication_version); + const auto security_config{rp.PopRaw()}; + [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw() + : SecurityParameter{}}; + const auto user_config{rp.PopRaw()}; + rp.Pop(); // Padding + const auto network_Config{rp.PopRaw()}; IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config)); } void DestroyNetwork(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + LOG_INFO(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.DestroyNetwork()); } void SetAdvertiseData(Kernel::HLERequestContext& ctx) { std::vector read_buffer = ctx.ReadBuffer(); - LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size()); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); } void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { @@ -420,17 +435,17 @@ public: } void OpenStation(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + LOG_INFO(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.OpenStation()); } void CloseStation(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + LOG_INFO(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.CloseStation()); } void Connect(Kernel::HLERequestContext& ctx) { @@ -445,16 +460,13 @@ public: const auto parameters{rp.PopRaw()}; - LOG_WARNING(Service_LDN, - "(STUBBED) called, passphrase_size={}, security_mode={}, " - "local_communication_version={}", - parameters.security_config.passphrase_size, - parameters.security_config.security_mode, - parameters.local_communication_version); + LOG_INFO(Service_LDN, + "called, passphrase_size={}, security_mode={}, " + "local_communication_version={}", + parameters.security_config.passphrase_size, + parameters.security_config.security_mode, parameters.local_communication_version); const std::vector read_buffer = ctx.ReadBuffer(); - NetworkInfo network_info{}; - if (read_buffer.size() != sizeof(NetworkInfo)) { LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); IPC::ResponseBuilder rb{ctx, 2}; @@ -462,40 +474,47 @@ public: return; } + NetworkInfo network_info{}; std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.Connect(network_info, parameters.user_config, + static_cast(parameters.local_communication_version))); } void Disconnect(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + LOG_INFO(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.Disconnect()); } - void Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + void Initialize(Kernel::HLERequestContext& ctx) { const auto rc = InitializeImpl(ctx); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); } void Finalize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } is_initialized = false; IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(lan_discovery.Finalize()); } void Initialize2(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); - const auto rc = InitializeImpl(ctx); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); @@ -508,14 +527,26 @@ public: return ResultAirplaneModeEnabled; } + if (auto room_member = room_network.GetRoomMember().lock()) { + ldn_packet_received = room_member->BindOnLdnPacketReceived( + [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); + } else { + LOG_ERROR(Service_LDN, "Couldn't bind callback!"); + return ResultAirplaneModeEnabled; + } + + lan_discovery.Initialize([&]() { OnEventFired(); }); is_initialized = true; - // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented - return ResultAirplaneModeEnabled; + return ResultSuccess; } KernelHelpers::ServiceContext service_context; Kernel::KEvent* state_change_event; Network::RoomNetwork& room_network; + LANDiscovery lan_discovery; + + // Callback identifier for the OnLDNPacketReceived event. + Network::RoomMember::CallbackHandle ldn_packet_received; bool is_initialized{}; }; diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h index 6231e936da..d6609fff55 100644 --- a/src/core/hle/service/ldn/ldn_types.h +++ b/src/core/hle/service/ldn/ldn_types.h @@ -31,6 +31,14 @@ enum class NodeStateChange : u8 { DisconnectAndConnect, }; +inline NodeStateChange operator|(NodeStateChange a, NodeStateChange b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +inline NodeStateChange operator|=(NodeStateChange& a, NodeStateChange b) { + return a = a | b; +} + enum class ScanFilterFlag : u32 { None = 0, LocalCommunicationId = 1 << 0, @@ -100,13 +108,13 @@ enum class AcceptPolicy : u8 { enum class WifiChannel : s16 { Default = 0, - wifi24_1 = 1, - wifi24_6 = 6, - wifi24_11 = 11, - wifi50_36 = 36, - wifi50_40 = 40, - wifi50_44 = 44, - wifi50_48 = 48, + Wifi24_1 = 1, + Wifi24_6 = 6, + Wifi24_11 = 11, + Wifi50_36 = 36, + Wifi50_40 = 40, + Wifi50_44 = 44, + Wifi50_48 = 48, }; enum class LinkLevel : s8 { @@ -116,6 +124,11 @@ enum class LinkLevel : s8 { Excellent, }; +enum class NodeStatus : u8 { + Disconnected, + Connected, +}; + struct NodeLatestUpdate { NodeStateChange state_change; INSERT_PADDING_BYTES(0x7); // Unknown @@ -159,19 +172,14 @@ struct Ssid { std::string GetStringValue() const { return std::string(raw.data()); } + + bool operator==(const Ssid& b) const { + return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0); + } }; static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); -struct Ipv4Address { - union { - u32 raw{}; - std::array bytes; - }; - - std::string GetStringValue() const { - return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); - } -}; +using Ipv4Address = std::array; static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); struct MacAddress { @@ -181,6 +189,14 @@ struct MacAddress { }; static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); +struct MACAddressHash { + size_t operator()(const MacAddress& address) const { + u64 value{}; + std::memcpy(&value, address.raw.data(), sizeof(address.raw)); + return value; + } +}; + struct ScanFilter { NetworkId network_id; NetworkType network_type; diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 0c746bd824..7d5d37bbcd 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/zstd_compression.h" #include "core/internal_network/network.h" #include "core/internal_network/network_interface.h" #include "core/internal_network/socket_proxy.h" @@ -32,8 +33,11 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { return; } + auto decompressed = packet; + decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); + std::lock_guard guard(packets_mutex); - received_packets.push(packet); + received_packets.push(decompressed); } template @@ -185,6 +189,8 @@ std::pair ProxySocket::Send(const std::vector& message, int flag void ProxySocket::SendPacket(ProxyPacket& packet) { if (auto room_member = room_network.GetRoomMember().lock()) { if (room_member->IsConnected()) { + packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), + packet.data.size()); room_member->SendProxyPacket(packet); } } diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index 7b6deba417..8d8ac1ed74 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp @@ -76,7 +76,8 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1"; static constexpr char token_delimiter{':'}; static void PadToken(std::string& token) { - while (token.size() % 4 != 0) { + const auto remainder = token.size() % 3; + for (size_t i = 0; i < (3 - remainder); i++) { token.push_back('='); } } diff --git a/src/network/room.cpp b/src/network/room.cpp index 8c63b255bc..dc5dbce7fa 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -211,6 +211,12 @@ public: */ void HandleProxyPacket(const ENetEvent* event); + /** + * Broadcasts this packet to all members except the sender. + * @param event The ENet event containing the data + */ + void HandleLdnPacket(const ENetEvent* event); + /** * Extracts a chat entry from a received ENet packet and adds it to the chat queue. * @param event The ENet event that was received. @@ -247,6 +253,9 @@ void Room::RoomImpl::ServerLoop() { case IdProxyPacket: HandleProxyPacket(&event); break; + case IdLdnPacket: + HandleLdnPacket(&event); + break; case IdChatMessage: HandleChatPacket(&event); break; @@ -861,6 +870,60 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) { enet_host_flush(server); } +void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) { + Packet in_packet; + in_packet.Append(event->packet->data, event->packet->dataLength); + + in_packet.IgnoreBytes(sizeof(u8)); // Message type + + in_packet.IgnoreBytes(sizeof(u8)); // LAN packet type + in_packet.IgnoreBytes(sizeof(IPv4Address)); // Local IP + + IPv4Address remote_ip; + in_packet.Read(remote_ip); // Remote IP + + bool broadcast; + in_packet.Read(broadcast); // Broadcast + + Packet out_packet; + out_packet.Append(event->packet->data, event->packet->dataLength); + ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), + ENET_PACKET_FLAG_RELIABLE); + + const auto& destination_address = remote_ip; + if (broadcast) { // Send the data to everyone except the sender + std::lock_guard lock(member_mutex); + bool sent_packet = false; + for (const auto& member : members) { + if (member.peer != event->peer) { + sent_packet = true; + enet_peer_send(member.peer, 0, enet_packet); + } + } + + if (!sent_packet) { + enet_packet_destroy(enet_packet); + } + } else { + std::lock_guard lock(member_mutex); + auto member = std::find_if(members.begin(), members.end(), + [destination_address](const Member& member_entry) -> bool { + return member_entry.fake_ip == destination_address; + }); + if (member != members.end()) { + enet_peer_send(member->peer, 0, enet_packet); + } else { + LOG_ERROR(Network, + "Attempting to send to unknown IP address: " + "{}.{}.{}.{}", + destination_address[0], destination_address[1], destination_address[2], + destination_address[3]); + enet_packet_destroy(enet_packet); + } + } + enet_host_flush(server); +} + void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { Packet in_packet; in_packet.Append(event->packet->data, event->packet->dataLength); diff --git a/src/network/room.h b/src/network/room.h index c2a4b1a702..edbd3ecfb2 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -40,6 +40,7 @@ enum RoomMessageTypes : u8 { IdRoomInformation, IdSetGameInfo, IdProxyPacket, + IdLdnPacket, IdChatMessage, IdNameCollision, IdIpCollision, diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index 06818af783..572e55a5b8 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -58,6 +58,7 @@ public: private: CallbackSet callback_set_proxy_packet; + CallbackSet callback_set_ldn_packet; CallbackSet callback_set_chat_messages; CallbackSet callback_set_status_messages; CallbackSet callback_set_room_information; @@ -107,6 +108,12 @@ public: */ void HandleProxyPackets(const ENetEvent* event); + /** + * Extracts an LdnPacket from a received ENet packet. + * @param event The ENet event that was received. + */ + void HandleLdnPackets(const ENetEvent* event); + /** * Extracts a chat entry from a received ENet packet and adds it to the chat queue. * @param event The ENet event that was received. @@ -166,6 +173,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() { case IdProxyPacket: HandleProxyPackets(&event); break; + case IdLdnPacket: + HandleLdnPackets(&event); + break; case IdChatMessage: HandleChatPacket(&event); break; @@ -372,6 +382,27 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) { Invoke(proxy_packet); } +void RoomMember::RoomMemberImpl::HandleLdnPackets(const ENetEvent* event) { + LDNPacket ldn_packet{}; + Packet packet; + packet.Append(event->packet->data, event->packet->dataLength); + + // Ignore the first byte, which is the message id. + packet.IgnoreBytes(sizeof(u8)); // Ignore the message type + + u8 packet_type; + packet.Read(packet_type); + ldn_packet.type = static_cast(packet_type); + + packet.Read(ldn_packet.local_ip); + packet.Read(ldn_packet.remote_ip); + packet.Read(ldn_packet.broadcast); + + packet.Read(ldn_packet.data); + + Invoke(ldn_packet); +} + void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { Packet packet; packet.Append(event->packet->data, event->packet->dataLength); @@ -449,6 +480,11 @@ RoomMember::RoomMemberImpl::CallbackSet& RoomMember::RoomMemberImpl return callback_set_proxy_packet; } +template <> +RoomMember::RoomMemberImpl::CallbackSet& RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_ldn_packet; +} + template <> RoomMember::RoomMemberImpl::CallbackSet& RoomMember::RoomMemberImpl::Callbacks::Get() { @@ -607,6 +643,21 @@ void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) { room_member_impl->Send(std::move(packet)); } +void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) { + Packet packet; + packet.Write(static_cast(IdLdnPacket)); + + packet.Write(static_cast(ldn_packet.type)); + + packet.Write(ldn_packet.local_ip); + packet.Write(ldn_packet.remote_ip); + packet.Write(ldn_packet.broadcast); + + packet.Write(ldn_packet.data); + + room_member_impl->Send(std::move(packet)); +} + void RoomMember::SendChatMessage(const std::string& message) { Packet packet; packet.Write(static_cast(IdChatMessage)); @@ -663,6 +714,11 @@ RoomMember::CallbackHandle RoomMember::BindOnProxyPacketReceived( return room_member_impl->Bind(callback); } +RoomMember::CallbackHandle RoomMember::BindOnLdnPacketReceived( + std::function callback) { + return room_member_impl->Bind(callback); +} + RoomMember::CallbackHandle RoomMember::BindOnRoomInformationChanged( std::function callback) { return room_member_impl->Bind(callback); @@ -699,6 +755,7 @@ void RoomMember::Leave() { } template void RoomMember::Unbind(CallbackHandle); +template void RoomMember::Unbind(CallbackHandle); template void RoomMember::Unbind(CallbackHandle); template void RoomMember::Unbind(CallbackHandle); template void RoomMember::Unbind(CallbackHandle); diff --git a/src/network/room_member.h b/src/network/room_member.h index f578f7f6a3..0d64172945 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -17,7 +17,24 @@ namespace Network { using AnnounceMultiplayerRoom::GameInfo; using AnnounceMultiplayerRoom::RoomInformation; -/// Information about the received WiFi packets. +enum class LDNPacketType : u8 { + Scan, + ScanResp, + Connect, + SyncNetwork, + Disconnect, + DestroyNetwork, +}; + +struct LDNPacket { + LDNPacketType type; + IPv4Address local_ip; + IPv4Address remote_ip; + bool broadcast; + std::vector data; +}; + +/// Information about the received proxy packets. struct ProxyPacket { SockAddrIn local_endpoint; SockAddrIn remote_endpoint; @@ -151,6 +168,12 @@ public: */ void SendProxyPacket(const ProxyPacket& packet); + /** + * Sends an LDN packet to the room. + * @param packet The WiFi packet to send. + */ + void SendLdnPacket(const LDNPacket& packet); + /** * Sends a chat message to the room. * @param message The contents of the message. @@ -204,6 +227,16 @@ public: CallbackHandle BindOnProxyPacketReceived( std::function callback); + /** + * Binds a function to an event that will be triggered every time an LDNPacket is received. + * The function wil be called everytime the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle BindOnLdnPacketReceived( + std::function callback); + /** * Binds a function to an event that will be triggered every time the RoomInformation changes. * The function wil be called every time the event is triggered. diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a85adc0724..9dfa8d639c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -896,8 +896,8 @@ void GMainWindow::InitializeWidgets() { } // TODO (flTobi): Add the widget when multiplayer is fully implemented - // statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); - // statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); tas_label = new QLabel(); tas_label->setObjectName(QStringLiteral("TASlabel")); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index cdf31b417d..60a8deab1c 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -120,6 +120,20 @@ + + + true + + + Multiplayer + + + + + + + + &Tools diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp index 9e672f82e9..dec9696c18 100644 --- a/src/yuzu/multiplayer/chat_room.cpp +++ b/src/yuzu/multiplayer/chat_room.cpp @@ -61,7 +61,10 @@ public: /// Format the message using the players color QString GetPlayerChatMessage(u16 player) const { - auto color = player_color[player % 16]; + const bool is_dark_theme = QIcon::themeName().contains(QStringLiteral("dark")) || + QIcon::themeName().contains(QStringLiteral("midnight")); + auto color = + is_dark_theme ? player_color_dark[player % 16] : player_color_default[player % 16]; QString name; if (username.isEmpty() || username == nickname) { name = nickname; @@ -84,9 +87,12 @@ public: } private: - static constexpr std::array player_color = { + static constexpr std::array player_color_default = { {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222", - "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "FFFF00"}}; + "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "#FFFF00"}}; + static constexpr std::array player_color_dark = { + {"#559AD1", "#4EC9A8", "#D69D85", "#C6C923", "#B975B5", "#D81F1F", "#7EAE39", "#4F8733", + "#F7CD8A", "#6FCACF", "#CE4897", "#8A2BE2", "#D2691E", "#9ACD32", "#FF7F50", "#152ccd"}}; static constexpr char ping_color[] = "#FFFF00"; QString timestamp; diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index 66e098296d..3ad8460281 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp @@ -249,6 +249,7 @@ void MultiplayerState::ShowNotification() { return; // Do not show notification if the chat window currently has focus show_notification = true; QApplication::alert(nullptr); + QApplication::beep(); status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); status_text->setText(tr("New Messages Received")); } From 92b1f8d5da2bcc857106d11a2af568bdabc93ed5 Mon Sep 17 00:00:00 2001 From: FengChen Date: Sat, 10 Sep 2022 20:01:33 +0800 Subject: [PATCH 003/245] Align index buffe size when vertex_buffer_unified_memory enable --- src/video_core/renderer_opengl/gl_buffer_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 32450ee1df..08f4d69aba 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -168,7 +168,7 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { if (has_unified_vertex_buffers) { buffer.MakeResident(GL_READ_ONLY); glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset, - static_cast(size)); + static_cast(Common::AlignUp(size, 4))); } else { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle()); index_buffer_offset = offset; From 8f207bd93ddb9778f0242fca0dab6ef155bd2a97 Mon Sep 17 00:00:00 2001 From: german77 Date: Fri, 9 Sep 2022 15:29:22 -0500 Subject: [PATCH 004/245] yuzu: Multiple room UI improvements --- src/core/hle/service/hid/hid.cpp | 3 +- .../internal_network/network_interface.cpp | 10 +++ src/core/internal_network/network_interface.h | 1 + src/yuzu/main.cpp | 8 ++ src/yuzu/main.h | 1 + src/yuzu/main.ui | 10 +-- src/yuzu/multiplayer/client_room.cpp | 3 +- src/yuzu/multiplayer/direct_connect.cpp | 2 + src/yuzu/multiplayer/direct_connect.h | 1 + src/yuzu/multiplayer/host_room.cpp | 1 + src/yuzu/multiplayer/host_room.h | 3 + src/yuzu/multiplayer/lobby.cpp | 67 +++++++++++----- src/yuzu/multiplayer/lobby.h | 8 ++ src/yuzu/multiplayer/lobby_p.h | 16 ++-- src/yuzu/multiplayer/message.cpp | 6 +- src/yuzu/multiplayer/state.cpp | 79 +++++++++++++------ src/yuzu/multiplayer/state.h | 14 ++++ src/yuzu/uisettings.h | 2 +- 18 files changed, 176 insertions(+), 59 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 3d34571600..7e923462ba 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -35,7 +35,8 @@ namespace Service::HID { // Updating period for each HID device. // Period time is obtained by measuring the number of samples in a second on HW using a homebrew -constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) +// Correct pad_update_ns is 4ms this is overclocked to lower input lag +constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 0f0a661606..858ae1cfbb 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -206,4 +206,14 @@ std::optional GetSelectedNetworkInterface() { return *res; } +void SelectFirstNetworkInterface() { + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + + if (network_interfaces.size() == 0) { + return; + } + + Settings::values.network_interface.SetValue(network_interfaces[0].name); +} + } // namespace Network diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h index 9b98b6b420..175e61b1f9 100644 --- a/src/core/internal_network/network_interface.h +++ b/src/core/internal_network/network_interface.h @@ -24,5 +24,6 @@ struct NetworkInterface { std::vector GetAvailableNetworkInterfaces(); std::optional GetSelectedNetworkInterface(); +void SelectFirstNetworkInterface(); } // namespace Network diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9dfa8d639c..deee1c370f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1296,6 +1296,7 @@ void GMainWindow::ConnectMenuEvents() { &MultiplayerState::OnDirectConnectToRoom); connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state, &MultiplayerState::OnOpenNetworkRoom); + connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig); // Tools connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, @@ -1336,6 +1337,8 @@ void GMainWindow::UpdateMenuState() { } else { ui->action_Pause->setText(tr("&Pause")); } + + multiplayer_state->UpdateNotificationStatus(); } void GMainWindow::OnDisplayTitleBars(bool show) { @@ -2766,6 +2769,11 @@ void GMainWindow::OnExit() { OnStopGame(); } +void GMainWindow::OnSaveConfig() { + system->ApplySettings(); + config->Save(); +} + void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { OverlayDialog dialog(render_window, *system, error_code, error_text, QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1ae2b93d9b..1a756b171e 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -169,6 +169,7 @@ public slots: void OnLoadComplete(); void OnExecuteProgram(std::size_t program_index); void OnExit(); + void OnSaveConfig(); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); void SoftwareKeyboardInitialize( diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 60a8deab1c..de1545216a 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -265,7 +265,7 @@ true - Browse Public Game Lobby + &Browse Public Game Lobby @@ -273,7 +273,7 @@ true - Create Room + &Create Room @@ -281,12 +281,12 @@ false - Leave Room + &Leave Room - Direct Connect to Room + &Direct Connect to Room @@ -294,7 +294,7 @@ false - Show Current Room + &Show Current Room diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp index b34a8d004a..caf34a414f 100644 --- a/src/yuzu/multiplayer/client_room.cpp +++ b/src/yuzu/multiplayer/client_room.cpp @@ -97,8 +97,9 @@ void ClientRoomWindow::UpdateView() { auto memberlist = member->GetMemberInformation(); ui->chat->SetPlayerList(memberlist); const auto information = member->GetRoomInformation(); - setWindowTitle(QString(tr("%1 (%2/%3 members) - connected")) + setWindowTitle(QString(tr("%1 - %2 (%3/%4 members) - connected")) .arg(QString::fromStdString(information.name)) + .arg(QString::fromStdString(information.preferred_game.name)) .arg(memberlist.size()) .arg(information.member_slots)); ui->description->setText(QString::fromStdString(information.description)); diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index 0170630744..10bf0a4fb3 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp @@ -106,6 +106,8 @@ void DirectConnectWindow::Connect() { UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault(); } + emit SaveConfig(); + // attempt to connect in a different thread QFuture f = QtConcurrent::run([&] { if (auto room_member = room_network.GetRoomMember().lock()) { diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h index e39dd1e0d6..b8f66cfb2b 100644 --- a/src/yuzu/multiplayer/direct_connect.h +++ b/src/yuzu/multiplayer/direct_connect.h @@ -31,6 +31,7 @@ signals: * connections that it might have. */ void Closed(); + void SaveConfig(); private slots: void OnConnection(); diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index 0c6adfd040..a8faa5b248 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp @@ -232,6 +232,7 @@ void HostRoomWindow::Host() { } UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); ui->host->setEnabled(true); + emit SaveConfig(); close(); } } diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h index 034cb2eefd..ae816e2e03 100644 --- a/src/yuzu/multiplayer/host_room.h +++ b/src/yuzu/multiplayer/host_room.h @@ -46,6 +46,9 @@ public: void UpdateGameList(QStandardItemModel* list); void RetranslateUi(); +signals: + void SaveConfig(); + private: void Host(); std::unique_ptr CreateVerifyBackend(bool use_validation) const; diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 107d405476..08c2756964 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/core.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/internal_network/network_interface.h" #include "network/network.h" #include "ui_lobby.h" @@ -26,9 +27,9 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, std::shared_ptr session, Core::System& system_) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()), - announce_multiplayer_session(session), system{system_}, room_network{ - system.GetRoomNetwork()} { + ui(std::make_unique()), announce_multiplayer_session(session), + profile_manager(std::make_unique()), system{system_}, + room_network{system.GetRoomNetwork()} { ui->setupUi(this); // setup the watcher for background connections @@ -60,9 +61,17 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, ui->nickname->setValidator(validation.GetNickname()); ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); - if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { - // Use yuzu Web Service user name as nickname by default - ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); + + // Try find the best nickname by default + if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) { + if (!Settings::values.yuzu_username.GetValue().empty()) { + ui->nickname->setText( + QString::fromStdString(Settings::values.yuzu_username.GetValue())); + } else if (!GetProfileUsername().empty()) { + ui->nickname->setText(QString::fromStdString(GetProfileUsername())); + } else { + ui->nickname->setText(QStringLiteral("yuzu")); + } } // UI Buttons @@ -76,12 +85,6 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, // Actions connect(&room_list_watcher, &QFutureWatcher::finished, this, &Lobby::OnRefreshLobby); - - // manually start a refresh when the window is opening - // TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as - // part of the constructor, but offload the refresh until after the window shown. perhaps emit a - // refreshroomlist signal from places that open the lobby - RefreshLobby(); } Lobby::~Lobby() = default; @@ -96,6 +99,7 @@ void Lobby::UpdateGameList(QStandardItemModel* list) { } if (proxy) proxy->UpdateGameList(game_list); + ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder); } void Lobby::RetranslateUi() { @@ -116,6 +120,11 @@ void Lobby::OnExpandRoom(const QModelIndex& index) { } void Lobby::OnJoinRoom(const QModelIndex& source) { + if (!Network::GetSelectedNetworkInterface()) { + LOG_INFO(WebService, "Automatically selected network interface for room network."); + Network::SelectFirstNetworkInterface(); + } + if (!Network::GetSelectedNetworkInterface()) { NetworkMessage::ErrorManager::ShowError( NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); @@ -197,16 +206,16 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); UISettings::values.multiplayer_port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); + emit SaveConfig(); } void Lobby::ResetModel() { model->clear(); model->insertColumns(0, Column::TOTAL); - model->setHeaderData(Column::EXPAND, Qt::Horizontal, QString(), Qt::DisplayRole); + model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole); model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole); model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole); model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole); - model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole); } void Lobby::RefreshLobby() { @@ -229,6 +238,7 @@ void Lobby::OnRefreshLobby() { for (int r = 0; r < game_list->rowCount(); ++r) { auto index = game_list->index(r, 0); auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong(); + if (game_id != 0 && room.information.preferred_game.id == game_id) { smdh_icon = game_list->data(index, Qt::DecorationRole).value(); } @@ -243,17 +253,16 @@ void Lobby::OnRefreshLobby() { members.append(var); } - auto first_item = new LobbyItem(); + auto first_item = new LobbyItemGame( + room.information.preferred_game.id, + QString::fromStdString(room.information.preferred_game.name), smdh_icon); auto row = QList({ first_item, new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)), - new LobbyItemGame(room.information.preferred_game.id, - QString::fromStdString(room.information.preferred_game.name), - smdh_icon), + new LobbyItemMemberList(members, room.information.member_slots), new LobbyItemHost(QString::fromStdString(room.information.host_username), QString::fromStdString(room.ip), room.information.port, QString::fromStdString(room.verify_uid)), - new LobbyItemMemberList(members, room.information.member_slots), }); model->appendRow(row); // To make the rows expandable, add the member data as a child of the first column of the @@ -283,6 +292,26 @@ void Lobby::OnRefreshLobby() { ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true); } } + + ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder); +} + +std::string Lobby::GetProfileUsername() { + const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue()); + Service::Account::ProfileBase profile{}; + + if (!current_user.has_value()) { + return ""; + } + + if (!profile_manager->GetProfileBase(*current_user, profile)) { + return ""; + } + + const auto text = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast(profile.username.data()), profile.username.size()); + + return text; } LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h index 2696aec21a..300dad13e1 100644 --- a/src/yuzu/multiplayer/lobby.h +++ b/src/yuzu/multiplayer/lobby.h @@ -24,6 +24,10 @@ namespace Core { class System; } +namespace Service::Account { +class ProfileManager; +} + /** * Listing of all public games pulled from services. The lobby should be simple enough for users to * find the game they want to play, and join it. @@ -75,8 +79,11 @@ private slots: signals: void StateChanged(const Network::RoomMember::State&); + void SaveConfig(); private: + std::string GetProfileUsername(); + /** * Removes all entries in the Lobby before refreshing. */ @@ -96,6 +103,7 @@ private: QFutureWatcher room_list_watcher; std::weak_ptr announce_multiplayer_session; + std::unique_ptr profile_manager; QFutureWatcher* watcher; Validation validation; Core::System& system; diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h index 8071cede4d..8b17075062 100644 --- a/src/yuzu/multiplayer/lobby_p.h +++ b/src/yuzu/multiplayer/lobby_p.h @@ -11,11 +11,10 @@ namespace Column { enum List { - EXPAND, - ROOM_NAME, GAME_NAME, - HOST, + ROOM_NAME, MEMBER, + HOST, TOTAL, }; } @@ -98,7 +97,12 @@ public: if (role == Qt::DecorationRole) { auto val = data(GameIconRole); if (val.isValid()) { - val = val.value().scaled(16, 16, Qt::KeepAspectRatio); + val = val.value().scaled(32, 32, Qt::KeepAspectRatio, + Qt::TransformationMode::SmoothTransformation); + } else { + auto blank_image = QPixmap(32, 32); + blank_image.fill(Qt::black); + val = blank_image; } return val; } else if (role != Qt::DisplayRole) { @@ -191,8 +195,8 @@ public: return LobbyItem::data(role); } auto members = data(MemberListRole).toList(); - return QStringLiteral("%1 / %2").arg(QString::number(members.size()), - data(MaxPlayerRole).toString()); + return QStringLiteral("%1 / %2 ") + .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); } bool operator<(const QStandardItem& other) const override { diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp index 758b5b731d..6d8f18274f 100644 --- a/src/yuzu/multiplayer/message.cpp +++ b/src/yuzu/multiplayer/message.cpp @@ -49,9 +49,9 @@ const ConnectionError ErrorManager::PERMISSION_DENIED( QT_TR_NOOP("You do not have enough permission to perform this action.")); const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); -const ConnectionError ErrorManager::NO_INTERFACE_SELECTED( - QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and " - "make a selection.")); +const ConnectionError ErrorManager::NO_INTERFACE_SELECTED(QT_TR_NOOP( + "No valid network interface is selected.\nPlease go to Configure -> System -> Network and " + "make a selection.")); static bool WarnMessage(const std::string& title, const std::string& text) { return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index 3ad8460281..ae2738ad4c 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp @@ -44,9 +44,6 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis status_text = new ClickableLabel(this); status_icon = new ClickableLabel(this); - status_text->setToolTip(tr("Current connection status")); - status_text->setText(tr("Not Connected. Click here to find a room!")); - status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); @@ -57,6 +54,8 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis HideNotification(); } }); + + retranslateUi(); } MultiplayerState::~MultiplayerState() = default; @@ -90,14 +89,7 @@ void MultiplayerState::Close() { void MultiplayerState::retranslateUi() { status_text->setToolTip(tr("Current connection status")); - if (current_state == Network::RoomMember::State::Uninitialized) { - status_text->setText(tr("Not Connected. Click here to find a room!")); - } else if (current_state == Network::RoomMember::State::Joined || - current_state == Network::RoomMember::State::Moderator) { - status_text->setText(tr("Connected")); - } else { - status_text->setText(tr("Not Connected")); - } + UpdateNotificationStatus(); if (lobby) { lobby->RetranslateUi(); @@ -113,21 +105,55 @@ void MultiplayerState::retranslateUi() { } } +void MultiplayerState::SetNotificationStatus(NotificationStatus status) { + notification_status = status; + UpdateNotificationStatus(); +} + +void MultiplayerState::UpdateNotificationStatus() { + switch (notification_status) { + case NotificationStatus::Unitialized: + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); + status_text->setText(tr("Not Connected. Click here to find a room!")); + leave_room->setEnabled(false); + show_room->setEnabled(false); + break; + case NotificationStatus::Disconnected: + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); + status_text->setText(tr("Not Connected")); + leave_room->setEnabled(false); + show_room->setEnabled(false); + break; + case NotificationStatus::Connected: + status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); + status_text->setText(tr("Connected")); + leave_room->setEnabled(true); + show_room->setEnabled(true); + break; + case NotificationStatus::Notification: + status_icon->setPixmap( + QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); + status_text->setText(tr("New Messages Received")); + leave_room->setEnabled(true); + show_room->setEnabled(true); + break; + } + + // Clean up status bar if game is running + if (system.IsPoweredOn()) { + status_text->clear(); + } +} + void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); if (state == Network::RoomMember::State::Joined || state == Network::RoomMember::State::Moderator) { OnOpenNetworkRoom(); - status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); - status_text->setText(tr("Connected")); - leave_room->setEnabled(true); - show_room->setEnabled(true); + SetNotificationStatus(NotificationStatus::Connected); } else { - status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); - status_text->setText(tr("Not Connected")); - leave_room->setEnabled(false); - show_room->setEnabled(false); + SetNotificationStatus(NotificationStatus::Disconnected); } current_state = state; @@ -185,6 +211,10 @@ void MultiplayerState::OnAnnounceFailed(const WebService::WebResult& result) { QMessageBox::Ok); } +void MultiplayerState::OnSaveConfig() { + emit SaveConfig(); +} + void MultiplayerState::UpdateThemedIcons() { if (show_notification) { status_icon->setPixmap( @@ -209,13 +239,16 @@ static void BringWidgetToFront(QWidget* widget) { void MultiplayerState::OnViewLobby() { if (lobby == nullptr) { lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system); + connect(lobby, &Lobby::SaveConfig, this, &MultiplayerState::OnSaveConfig); } + lobby->RefreshLobby(); BringWidgetToFront(lobby); } void MultiplayerState::OnCreateRoom() { if (host_room == nullptr) { host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); + connect(host_room, &HostRoomWindow::SaveConfig, this, &MultiplayerState::OnSaveConfig); } BringWidgetToFront(host_room); } @@ -250,14 +283,12 @@ void MultiplayerState::ShowNotification() { show_notification = true; QApplication::alert(nullptr); QApplication::beep(); - status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); - status_text->setText(tr("New Messages Received")); + SetNotificationStatus(NotificationStatus::Notification); } void MultiplayerState::HideNotification() { show_notification = false; - status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); - status_text->setText(tr("Connected")); + SetNotificationStatus(NotificationStatus::Connected); } void MultiplayerState::OnOpenNetworkRoom() { @@ -280,6 +311,8 @@ void MultiplayerState::OnOpenNetworkRoom() { void MultiplayerState::OnDirectConnectToRoom() { if (direct_connect == nullptr) { direct_connect = new DirectConnectWindow(system, this); + connect(direct_connect, &DirectConnectWindow::SaveConfig, this, + &MultiplayerState::OnSaveConfig); } BringWidgetToFront(direct_connect); } diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h index c92496413c..5d681c5c61 100644 --- a/src/yuzu/multiplayer/state.h +++ b/src/yuzu/multiplayer/state.h @@ -22,6 +22,13 @@ class MultiplayerState : public QWidget { Q_OBJECT; public: + enum class NotificationStatus { + Unitialized, + Disconnected, + Connected, + Notification, + }; + explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, QAction* show_room, Core::System& system_); ~MultiplayerState(); @@ -31,6 +38,10 @@ public: */ void Close(); + void SetNotificationStatus(NotificationStatus state); + + void UpdateNotificationStatus(); + ClickableLabel* GetStatusText() const { return status_text; } @@ -64,6 +75,7 @@ public slots: void OnOpenNetworkRoom(); void OnDirectConnectToRoom(); void OnAnnounceFailed(const WebService::WebResult&); + void OnSaveConfig(); void UpdateThemedIcons(); void ShowNotification(); void HideNotification(); @@ -72,6 +84,7 @@ signals: void NetworkStateChanged(const Network::RoomMember::State&); void NetworkError(const Network::RoomMember::Error&); void AnnounceFailed(const WebService::WebResult&); + void SaveConfig(); private: Lobby* lobby = nullptr; @@ -85,6 +98,7 @@ private: QAction* show_room; std::shared_ptr announce_multiplayer_session; Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized; + NotificationStatus notification_status = NotificationStatus::Unitialized; bool has_mod_perms = false; Network::RoomMember::CallbackHandle state_callback_handle; Network::RoomMember::CallbackHandle error_callback_handle; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index e12d414d96..753797efc4 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -102,7 +102,7 @@ struct Values { Settings::Setting callout_flags{0, "calloutFlags"}; // multiplayer settings - Settings::Setting multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; + Settings::Setting multiplayer_nickname{{}, "nickname"}; Settings::Setting multiplayer_ip{{}, "ip"}; Settings::SwitchableSetting multiplayer_port{24872, 0, UINT16_MAX, "port"}; Settings::Setting multiplayer_room_nickname{{}, "room_nickname"}; From 1694c55d62d44730d760371d1bf2559de1b191dd Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Sat, 10 Sep 2022 12:57:19 -0500 Subject: [PATCH 005/245] fix black icon --- src/yuzu/multiplayer/lobby_p.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h index 8b17075062..068c95acad 100644 --- a/src/yuzu/multiplayer/lobby_p.h +++ b/src/yuzu/multiplayer/lobby_p.h @@ -90,6 +90,8 @@ public: setData(game_name, GameNameRole); if (!smdh_icon.isNull()) { setData(smdh_icon, GameIconRole); + } else { + setData(QIcon::fromTheme(QStringLiteral("chip")).pixmap(32), GameIconRole); } } From aa11d73bba386076973010ba4c60d5b04ba828a3 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 10 Sep 2022 17:38:36 -0600 Subject: [PATCH 006/245] dedicated_room: fix token padding ... ... mebedtls' base64 routine has a strange behavioral issue where if the input is invalid, it will not report it as invalid, but rather returning a bunch of garbage data. This new round-tripping padding method should eliminate such issue. --- src/dedicated_room/yuzu_room.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index 8d8ac1ed74..359891883c 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp @@ -76,8 +76,18 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1"; static constexpr char token_delimiter{':'}; static void PadToken(std::string& token) { - const auto remainder = token.size() % 3; - for (size_t i = 0; i < (3 - remainder); i++) { + std::size_t outlen = 0; + + std::array output{}; + std::array roundtrip{}; + for (size_t i = 0; i < 3; i++) { + mbedtls_base64_decode(output.data(), output.size(), &outlen, + reinterpret_cast(token.c_str()), + token.length()); + mbedtls_base64_encode(roundtrip.data(), roundtrip.size(), &outlen, output.data(), outlen); + if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) { + break; + } token.push_back('='); } } From 88007077e201552fe0d8db0b464ca0e2cc8cd256 Mon Sep 17 00:00:00 2001 From: FengChen Date: Thu, 15 Sep 2022 17:04:44 +0800 Subject: [PATCH 007/245] video_core: Modify astc texture decode error fill value --- src/video_core/host_shaders/astc_decoder.comp | 2 +- src/video_core/textures/astc.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index 3441a5fe54..d608678a35 100644 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp @@ -1065,7 +1065,7 @@ TexelWeightParams DecodeBlockInfo() { void FillError(ivec3 coord) { for (uint j = 0; j < block_dims.y; j++) { for (uint i = 0; i < block_dims.x; i++) { - imageStore(dest_image, coord + ivec3(i, j, 0), vec4(1.0, 1.0, 0.0, 1.0)); + imageStore(dest_image, coord + ivec3(i, j, 0), vec4(0.0, 0.0, 0.0, 0.0)); } } } diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index e3f3d3c5d0..a4d4e0ed5c 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -1411,7 +1411,7 @@ static void FillVoidExtentLDR(InputBitStream& strm, std::span outBuf, u32 b static void FillError(std::span outBuf, u32 blockWidth, u32 blockHeight) { for (u32 j = 0; j < blockHeight; j++) { for (u32 i = 0; i < blockWidth; i++) { - outBuf[j * blockWidth + i] = 0xFFFF00FF; + outBuf[j * blockWidth + i] = 0x00000000; } } } From 9a95c7fa14bdfc14aacea92896c8ae8533918fe8 Mon Sep 17 00:00:00 2001 From: FengChen Date: Thu, 1 Sep 2022 22:05:11 +0800 Subject: [PATCH 008/245] video_core: Generate mipmap texture by drawing --- src/shader_recompiler/CMakeLists.txt | 1 + .../backend/glasm/emit_glasm.cpp | 3 + .../glasm/emit_glasm_context_get_set.cpp | 4 + .../backend/glasm/emit_glasm_instructions.h | 1 + .../glsl/emit_glsl_context_get_set.cpp | 4 + .../backend/glsl/emit_glsl_instructions.h | 1 + .../backend/glsl/glsl_emit_context.cpp | 3 + .../backend/spirv/emit_spirv.h | 4 + .../spirv/emit_spirv_context_get_set.cpp | 12 ++- .../backend/spirv/emit_spirv_instructions.h | 1 + .../backend/spirv/spirv_emit_context.cpp | 31 ++++++++ .../backend/spirv/spirv_emit_context.h | 4 + src/shader_recompiler/environment.h | 2 + .../frontend/ir/ir_emitter.cpp | 8 ++ .../frontend/ir/ir_emitter.h | 3 + src/shader_recompiler/frontend/ir/opcodes.inc | 1 + .../frontend/maxwell/translate_program.cpp | 2 + src/shader_recompiler/ir_opt/passes.h | 1 + .../ir_opt/position_pass.cpp | 77 +++++++++++++++++++ src/shader_recompiler/shader_info.h | 1 + .../renderer_opengl/gl_graphics_pipeline.cpp | 11 +++ .../renderer_opengl/gl_rasterizer.cpp | 10 +++ .../renderer_opengl/gl_shader_cache.cpp | 2 +- .../renderer_vulkan/pipeline_helper.h | 10 ++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 24 +++++- .../renderer_vulkan/vk_graphics_pipeline.h | 4 +- .../renderer_vulkan/vk_rasterizer.cpp | 16 ++++ src/video_core/shader_environment.cpp | 17 ++++ src/video_core/shader_environment.h | 9 +++ 29 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 src/shader_recompiler/ir_opt/position_pass.cpp diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index af8e51fe82..189e5cb17b 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -224,6 +224,7 @@ add_library(shader_recompiler STATIC ir_opt/lower_fp16_to_fp32.cpp ir_opt/lower_int64_to_int32.cpp ir_opt/passes.h + ir_opt/position_pass.cpp ir_opt/rescaling_pass.cpp ir_opt/ssa_rewrite_pass.cpp ir_opt/texture_pass.cpp diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index 97a6b383b4..1419207e8f 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp @@ -450,6 +450,9 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I if (program.info.uses_rescaling_uniform) { header += "PARAM scaling[1]={program.local[0..0]};"; } + if (program.info.uses_render_area) { + header += "PARAM render_area[1]={program.local[1..1]};"; + } header += "TEMP "; for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) { header += fmt::format("R{},", index); diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index b5c08d6117..ee0b0f53d6 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -379,6 +379,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { ctx.Add("MOV.F {}.x,scaling[0].z;", inst); } +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) { + ctx.Add("MOV.F {},render_area[0];", inst); +} + void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); } diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index 8b0ac30317..c08f48ed9a 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h @@ -73,6 +73,7 @@ void EmitSampleId(EmitContext& ctx, IR::Inst& inst); void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); void EmitYDirection(EmitContext& ctx, IR::Inst& inst); void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst); void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset); void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value); void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index fad8d1e304..d7c845469f 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -416,6 +416,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { ctx.AddF32("{}=scaling.z;", inst); } +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) { + ctx.AddF32x4("{}=render_area;", inst); +} + void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { ctx.AddU32("{}=lmem[{}];", inst, word_offset); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 639691ba67..3c8bcb7e99 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h @@ -87,6 +87,7 @@ void EmitSampleId(EmitContext& ctx, IR::Inst& inst); void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); void EmitYDirection(EmitContext& ctx, IR::Inst& inst); void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst); void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index c767a9dc3b..5d01ec0cdc 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -358,6 +358,9 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile if (info.uses_rescaling_uniform) { header += "layout(location=0) uniform vec4 scaling;"; } + if (info.uses_render_area) { + header += "layout(location=1) uniform vec4 render_area;"; + } DefineConstantBuffers(bindings); DefineConstantBufferIndirect(); DefineStorageBuffers(bindings); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 7567b6fc96..9378814840 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h @@ -23,8 +23,12 @@ struct RescalingLayout { alignas(16) std::array rescaling_images; u32 down_factor; }; +struct RenderAreaLayout { + std::array render_area; +}; constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); +constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area); [[nodiscard]] std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 2c68aba392..a4751b42d0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -353,7 +353,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { case IR::Attribute::TessellationEvaluationPointV: return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); - default: throw NotImplementedException("Read attribute {}", attr); } @@ -537,6 +536,17 @@ Id EmitResolutionDownFactor(EmitContext& ctx) { } } +Id EmitRenderArea(EmitContext& ctx) { + if (ctx.profile.unified_descriptor_binding) { + const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[4])}; + const Id index{ctx.Const(ctx.render_are_member_index)}; + const Id pointer{ctx.OpAccessChain(pointer_type, ctx.render_area_push_constant, index)}; + return ctx.OpLoad(ctx.F32[4], pointer); + } else { + throw NotImplementedException("SPIR-V Instruction"); + } +} + Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; return ctx.OpLoad(ctx.U32[1], pointer); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 984d072b4a..86dd3c4f34 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -76,6 +76,7 @@ Id EmitSampleId(EmitContext& ctx); Id EmitIsHelperInvocation(EmitContext& ctx); Id EmitYDirection(EmitContext& ctx); Id EmitResolutionDownFactor(EmitContext& ctx); +Id EmitRenderArea(EmitContext& ctx); Id EmitLoadLocal(EmitContext& ctx, Id word_offset); void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); Id EmitUndefU1(EmitContext& ctx); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index aecc4c612b..c26ad8f935 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -473,6 +473,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf DefineAttributeMemAccess(program.info); DefineGlobalMemoryFunctions(program.info); DefineRescalingInput(program.info); + DefineRenderArea(program.info); } EmitContext::~EmitContext() = default; @@ -982,6 +983,36 @@ void EmitContext::DefineRescalingInputUniformConstant() { } } +void EmitContext::DefineRenderArea(const Info& info) { + if (!info.uses_render_area) { + return; + } + + if (profile.unified_descriptor_binding) { + boost::container::static_vector members{}; + u32 member_index{0}; + + members.push_back(F32[4]); + render_are_member_index = member_index++; + + const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))}; + Decorate(push_constant_struct, spv::Decoration::Block); + Name(push_constant_struct, "RenderAreaInfo"); + + MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0); + MemberName(push_constant_struct, render_are_member_index, "render_area"); + + const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)}; + render_area_push_constant = + AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant); + Name(render_area_push_constant, "render_area_push_constants"); + + if (profile.supported_spirv >= 0x00010400) { + interfaces.push_back(render_area_push_constant); + } + } +} + void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { if (info.constant_buffer_descriptors.empty()) { return; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index bc25b8b844..c86e509113 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -243,6 +243,9 @@ public: u32 texture_rescaling_index{}; u32 image_rescaling_index{}; + Id render_area_push_constant{}; + u32 render_are_member_index{}; + Id local_memory{}; Id shared_memory_u8{}; @@ -318,6 +321,7 @@ private: void DefineRescalingInput(const Info& info); void DefineRescalingInputPushConstant(); void DefineRescalingInputUniformConstant(); + void DefineRenderArea(const Info& info); void DefineInputs(const IR::Program& program); void DefineOutputs(const IR::Program& program); diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 9729d48c65..980e0e54c1 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -22,6 +22,8 @@ public: [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; + [[nodiscard]] virtual u32 ReadViewportTransformState() = 0; + [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; [[nodiscard]] virtual u32 LocalMemorySize() const = 0; diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 11086ed8c2..de1ce90a31 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp @@ -378,6 +378,14 @@ F32 IREmitter::ResolutionDownFactor() { return Inst(Opcode::ResolutionDownFactor); } +F32 IREmitter::RenderAreaWidth() { + return F32(CompositeExtract(Inst(Opcode::RenderArea), 0)); +} + +F32 IREmitter::RenderAreaHeight() { + return F32(CompositeExtract(Inst(Opcode::RenderArea), 1)); +} + U32 IREmitter::LaneId() { return Inst(Opcode::LaneId); } diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 25839a371d..f163c18d94 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -103,6 +103,9 @@ public: [[nodiscard]] F32 ResolutionDownFactor(); + [[nodiscard]] F32 RenderAreaWidth(); + [[nodiscard]] F32 RenderAreaHeight(); + [[nodiscard]] U32 LaneId(); [[nodiscard]] U32 LoadGlobalU8(const U64& address); diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 86410ddfc2..f3038f2984 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc @@ -63,6 +63,7 @@ OPCODE(SampleId, U32, OPCODE(IsHelperInvocation, U1, ) OPCODE(YDirection, F32, ) OPCODE(ResolutionDownFactor, F32, ) +OPCODE(RenderArea, F32x4, ) // Undefined OPCODE(UndefU1, U1, ) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 77efb4f577..dafb9deeec 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -213,6 +213,8 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool + +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/ir_opt/passes.h" + +namespace Shader::Optimization { + +namespace { +struct PositionInst { + IR::Inst* inst; + IR::Block* block; + IR::Attribute attr; +}; +using PositionInstVector = boost::container::small_vector; +} // Anonymous namespace + +void PositionPass(Environment& env, IR::Program& program) { + if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) { + return; + } + + Info& info{program.info}; + info.uses_render_area = true; + + PositionInstVector to_replace; + for (IR::Block* const block : program.post_order_blocks) { + for (IR::Inst& inst : block->Instructions()) { + switch (inst.GetOpcode()) { + case IR::Opcode::SetAttribute: { + const IR::Attribute attr{inst.Arg(0).Attribute()}; + switch (attr) { + case IR::Attribute::PositionX: + case IR::Attribute::PositionY: { + to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr}); + break; + } + default: + break; + } + break; + } + default: + break; + } + } + } + + for (PositionInst& position_inst : to_replace) { + IR::IREmitter ir{*position_inst.block, + IR::Block::InstructionList::s_iterator_to(*position_inst.inst)}; + const IR::F32 value(position_inst.inst->Arg(1)); + const IR::F32F64 scale(ir.Imm32(2.f)); + const IR::F32 negative_one{ir.Imm32(-1.f)}; + switch (position_inst.attr) { + case IR::Attribute::PositionX: { + position_inst.inst->SetArg( + 1, + ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one)); + break; + } + case IR::Attribute::PositionY: { + position_inst.inst->SetArg( + 1, + ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one)); + break; + } + default: + break; + } + } +} +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f5690805c4..e4b5ba5679 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -175,6 +175,7 @@ struct Info { bool uses_shadow_lod{}; bool uses_rescaling_uniform{}; bool uses_cbuf_indirect{}; + bool uses_render_area{}; IR::Type used_constant_buffer_types{}; IR::Type used_storage_buffer_types{}; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 67eae369dd..c297f7121e 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -502,6 +502,17 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { float_image_scaling_mask, down_factor, 0.0f); } } + if (info.uses_render_area) { + const auto render_area_width(static_cast(regs.render_area.width)); + const auto render_area_height(static_cast(regs.render_area.height)); + if (use_assembly) { + glProgramLocalParameter4fARB(AssemblyStage(stage), 1, render_area_width, + render_area_height, 0.0f, 0.0f); + } else { + glProgramUniform4f(source_programs[stage].handle, 1, render_area_width, + render_area_height, 0.0f, 0.0f); + } + } }}; if constexpr (Spec::enabled_stages[0]) { prepare_stage(0); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a0d048b0be..f018333aff 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -622,6 +622,16 @@ void RasterizerOpenGL::SyncViewport() { } flags[Dirty::Viewport0 + index] = false; + if (!regs.viewport_transform_enabled) { + const auto x = static_cast(regs.render_area.x); + const auto y = static_cast(regs.render_area.y); + const auto width = static_cast(regs.render_area.width); + const auto height = static_cast(regs.render_area.height); + glViewportIndexedf(static_cast(index), x, y, width != 0.0f ? width : 1.0f, + height != 0.0f ? height : 1.0f); + continue; + } + const auto& src = regs.viewport_transform[index]; GLfloat x = conv(src.translate_x - src.scale_x); GLfloat y = conv(src.translate_y - src.scale_y); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 1ad56d9e7a..7e76f679f4 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -49,7 +49,7 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 5; +constexpr u32 CACHE_VERSION = 7; template auto MakeSpan(Container& container) { diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index b24f3424a6..b7843e9953 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -68,13 +68,15 @@ public: } vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const { + using Shader::Backend::SPIRV::RenderAreaLayout; using Shader::Backend::SPIRV::RescalingLayout; const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u; const VkPushConstantRange range{ .stageFlags = static_cast( is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS), .offset = 0, - .size = static_cast(sizeof(RescalingLayout)) - size_offset, + .size = static_cast(sizeof(RescalingLayout)) - size_offset + + static_cast(sizeof(RenderAreaLayout)), }; return device->GetLogical().CreatePipelineLayout({ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -167,6 +169,12 @@ private: u32 image_bit{1u}; }; +class RenderAreaPushConstant { +public: + bool uses_render_area{}; + std::array words{}; +}; + inline void PushImageDescriptors(TextureCache& texture_cache, UpdateDescriptorQueue& update_descriptor_queue, const Shader::Info& info, RescalingPushConstant& rescaling, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 5aca8f0385..7eb623a7cf 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -31,6 +31,7 @@ namespace { using boost::container::small_vector; using boost::container::static_vector; using Shader::ImageBufferDescriptor; +using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET; using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET; using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; @@ -433,12 +434,19 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { update_descriptor_queue.Acquire(); RescalingPushConstant rescaling; + RenderAreaPushConstant render_area; const VkSampler* samplers_it{samplers.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()}; const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { buffer_cache.BindHostStageBuffers(stage); PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling, samplers_it, views_it); + const auto& info{stage_infos[0]}; + if (info.uses_render_area) { + render_area.uses_render_area = true; + render_area.words = {static_cast(regs.render_area.width), + static_cast(regs.render_area.height)}; + } }}; if constexpr (Spec::enabled_stages[0]) { prepare_stage(0); @@ -455,10 +463,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if constexpr (Spec::enabled_stages[4]) { prepare_stage(4); } - ConfigureDraw(rescaling); + ConfigureDraw(rescaling, render_area); } -void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { +void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, + const RenderAreaPushConstant& render_are) { texture_cache.UpdateRenderTargets(false); scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); @@ -474,7 +483,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; const void* const descriptor_data{update_descriptor_queue.UpdateData()}; scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), - is_rescaling, update_rescaling](vk::CommandBuffer cmdbuf) { + is_rescaling, update_rescaling, + uses_render_area = render_are.uses_render_area, + render_area_data = render_are.words](vk::CommandBuffer cmdbuf) { if (bind_pipeline) { cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); } @@ -483,11 +494,16 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { rescaling_data.data()); if (update_rescaling) { const f32 config_down_factor{Settings::values.resolution_info.down_factor}; - const f32 scale_down_factor{is_rescaling ? config_down_factor : 1.0f}; + const f32 scale_down_factor{is_rescaling ? config_down_factor : 2.0f}; cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS, RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor), &scale_down_factor); } + if (uses_render_area) { + cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS, + RENDERAREA_LAYOUT_OFFSET, sizeof(render_area_data), + &render_area_data); + } if (!descriptor_set_layout) { return; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index e8949a9ab0..8842f62d74 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -62,6 +62,7 @@ class Device; class PipelineStatistics; class RenderPassCache; class RescalingPushConstant; +class RenderAreaPushConstant; class Scheduler; class UpdateDescriptorQueue; @@ -113,7 +114,8 @@ private: template void ConfigureImpl(bool is_indexed); - void ConfigureDraw(const RescalingPushConstant& rescaling); + void ConfigureDraw(const RescalingPushConstant& rescaling, + const RenderAreaPushConstant& render_are); void MakePipeline(VkRenderPass render_pass); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7e40c2df1a..bf9f825f4a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -682,6 +682,22 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg if (!state_tracker.TouchViewports()) { return; } + if (!regs.viewport_transform_enabled) { + const auto x = static_cast(regs.render_area.x); + const auto y = static_cast(regs.render_area.y); + const auto width = static_cast(regs.render_area.width); + const auto height = static_cast(regs.render_area.height); + VkViewport viewport{ + .x = x, + .y = y, + .width = width != 0.0f ? width : 1.0f, + .height = height != 0.0f ? height : 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); }); + return; + } const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; const std::array viewports{ diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 808d88eec8..3ead06dd65 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -191,6 +191,8 @@ void GenericEnvironment::Serialize(std::ofstream& file) const { .write(reinterpret_cast(&start_address), sizeof(start_address)) .write(reinterpret_cast(&cached_lowest), sizeof(cached_lowest)) .write(reinterpret_cast(&cached_highest), sizeof(cached_highest)) + .write(reinterpret_cast(&viewport_transform_state), + sizeof(viewport_transform_state)) .write(reinterpret_cast(&stage), sizeof(stage)) .write(reinterpret_cast(code.data()), code_size); for (const auto& [key, type] : texture_types) { @@ -311,6 +313,12 @@ Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, via_header_index, handle); } +u32 GraphicsEnvironment::ReadViewportTransformState() { + const auto& regs{maxwell3d->regs}; + viewport_transform_state = regs.viewport_transform_enabled; + return viewport_transform_state; +} + ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, u32 start_address_) @@ -342,6 +350,10 @@ Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); } +u32 ComputeEnvironment::ReadViewportTransformState() { + return viewport_transform_state; +} + void FileEnvironment::Deserialize(std::ifstream& file) { u64 code_size{}; u64 num_texture_types{}; @@ -354,6 +366,7 @@ void FileEnvironment::Deserialize(std::ifstream& file) { .read(reinterpret_cast(&start_address), sizeof(start_address)) .read(reinterpret_cast(&read_lowest), sizeof(read_lowest)) .read(reinterpret_cast(&read_highest), sizeof(read_highest)) + .read(reinterpret_cast(&viewport_transform_state), sizeof(viewport_transform_state)) .read(reinterpret_cast(&stage), sizeof(stage)); code = std::make_unique(Common::DivCeil(code_size, sizeof(u64))); file.read(reinterpret_cast(code.get()), code_size); @@ -411,6 +424,10 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) { return it->second; } +u32 FileEnvironment::ReadViewportTransformState() { + return viewport_transform_state; +} + u32 FileEnvironment::LocalMemorySize() const { return local_memory_size; } diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 5a145f33a6..a0659fd7c3 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -85,6 +85,8 @@ protected: u32 cached_highest = 0; u32 initial_offset = 0; + u32 viewport_transform_state = 1; + bool has_unbound_instructions = false; }; @@ -102,6 +104,8 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + u32 ReadViewportTransformState() override; + private: Tegra::Engines::Maxwell3D* maxwell3d{}; size_t stage_index{}; @@ -120,6 +124,8 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + u32 ReadViewportTransformState() override; + private: Tegra::Engines::KeplerCompute* kepler_compute{}; }; @@ -143,6 +149,8 @@ public: [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; + [[nodiscard]] u32 ReadViewportTransformState() override; + [[nodiscard]] u32 LocalMemorySize() const override; [[nodiscard]] u32 SharedMemorySize() const override; @@ -164,6 +172,7 @@ private: u32 read_lowest{}; u32 read_highest{}; u32 initial_offset{}; + u32 viewport_transform_state = 1; }; void SerializePipeline(std::span key, std::span envs, From d4cb0eac87c9dea121a0f6bd2355fa5e6c641274 Mon Sep 17 00:00:00 2001 From: FengChen Date: Sat, 10 Sep 2022 17:09:45 +0800 Subject: [PATCH 009/245] video_core: Fix legacy to generic location unpaired --- .../frontend/maxwell/translate_program.cpp | 39 ++++++++++++------- src/shader_recompiler/runtime_info.h | 2 + src/shader_recompiler/shader_info.h | 3 ++ .../renderer_opengl/gl_shader_cache.cpp | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 1 + 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 77efb4f577..3aee33a961 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -137,28 +137,35 @@ bool IsLegacyAttribute(IR::Attribute attribute) { } std::map GenerateLegacyToGenericMappings( - const VaryingState& state, std::queue ununsed_generics) { + const VaryingState& state, std::queue unused_generics, + const std::map& previous_stage_mapping) { std::map mapping; + auto update_mapping = [&mapping, &unused_generics, previous_stage_mapping](IR::Attribute attr, + size_t count) { + if (previous_stage_mapping.find(attr) != previous_stage_mapping.end()) { + for (size_t i = 0; i < count; ++i) { + mapping.insert({attr + i, previous_stage_mapping.at(attr + i)}); + } + } else { + for (size_t i = 0; i < count; ++i) { + mapping.insert({attr + i, unused_generics.front() + i}); + } + unused_generics.pop(); + } + }; for (size_t index = 0; index < 4; ++index) { auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; if (state.AnyComponent(attr)) { - for (size_t i = 0; i < 4; ++i) { - mapping.insert({attr + i, ununsed_generics.front() + i}); - } - ununsed_generics.pop(); + update_mapping(attr, 4); } } if (state[IR::Attribute::FogCoordinate]) { - mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()}); - ununsed_generics.pop(); + update_mapping(IR::Attribute::FogCoordinate, 1); } for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { auto attr = IR::Attribute::FixedFncTexture0S + index * 4; if (state.AnyComponent(attr)) { - for (size_t i = 0; i < 4; ++i) { - mapping.insert({attr + i, ununsed_generics.front() + i}); - } - ununsed_generics.pop(); + update_mapping(attr, 4); } } return mapping; @@ -271,15 +278,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); } } - auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics); + program.info.legacy_stores_mapping = + GenerateLegacyToGenericMappings(stores, ununsed_output_generics, {}); for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { switch (inst.GetOpcode()) { case IR::Opcode::SetAttribute: { const auto attr = inst.Arg(0).Attribute(); if (IsLegacyAttribute(attr)) { - stores.Set(mappings[attr], true); - inst.SetArg(0, Shader::IR::Value(mappings[attr])); + stores.Set(program.info.legacy_stores_mapping[attr], true); + inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr])); } break; } @@ -300,7 +308,8 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); } } - auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics); + auto mappings = GenerateLegacyToGenericMappings( + loads, ununsed_input_generics, runtime_info.previous_stage_legacy_stores_mapping); for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { switch (inst.GetOpcode()) { diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index dcb5ab158d..549b81ef75 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -60,6 +61,7 @@ struct TransformFeedbackVarying { struct RuntimeInfo { std::array generic_input_types{}; VaryingState previous_stage_stores; + std::map previous_stage_legacy_stores_mapping; bool convert_depth_mode{}; bool force_early_z{}; diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f5690805c4..c3c586adae 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -5,6 +5,7 @@ #include #include +#include #include "common/common_types.h" #include "shader_recompiler/frontend/ir/type.h" @@ -123,6 +124,8 @@ struct Info { VaryingState stores; VaryingState passthrough; + std::map legacy_stores_mapping; + bool loads_indexed_attributes{}; std::array stores_frag_color{}; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index ddb70934c1..fa05b47ff6 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -63,6 +63,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, Shader::RuntimeInfo info; if (previous_program) { info.previous_stage_stores = previous_program->info.stores; + info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping; } else { // Mark all stores as available for vertex shaders info.previous_stage_stores.mask.set(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9708dc45e4..53dda50484 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -131,6 +131,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program Shader::RuntimeInfo info; if (previous_program) { info.previous_stage_stores = previous_program->info.stores; + info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping; if (previous_program->is_geometry_passthrough) { info.previous_stage_stores.mask |= previous_program->info.passthrough.mask; } From 4213f1c126afda9c5235c868ded4e7d95438bffc Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Mon, 12 Sep 2022 22:39:18 +0200 Subject: [PATCH 010/245] Address some review comments --- src/core/hle/service/ldn/lan_discovery.cpp | 49 +++++++------------ src/core/hle/service/ldn/lan_discovery.h | 17 ++++--- src/core/hle/service/ldn/ldn.cpp | 2 - src/core/hle/service/ldn/ldn_types.h | 14 +++--- .../internal_network/network_interface.cpp | 4 +- src/network/room_member.cpp | 2 +- src/yuzu/main.ui | 2 +- 7 files changed, 38 insertions(+), 52 deletions(-) diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp index b04c990771..8f3c045501 100644 --- a/src/core/hle/service/ldn/lan_discovery.cpp +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -35,12 +35,9 @@ void LanStation::OverrideInfo() { LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_) : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}), - room_network{room_network_} { - LOG_INFO(Service_LDN, "LANDiscovery"); -} + room_network{room_network_} {} LANDiscovery::~LANDiscovery() { - LOG_INFO(Service_LDN, "~LANDiscovery"); if (inited) { Result rc = Finalize(); LOG_INFO(Service_LDN, "Finalize: {}", rc.raw); @@ -62,7 +59,7 @@ void LANDiscovery::InitNetworkInfo() { } void LANDiscovery::InitNodeStateChange() { - for (auto& node_update : nodeChanges) { + for (auto& node_update : node_changes) { node_update.state_change = NodeStateChange::None; } for (auto& node_state : node_last_states) { @@ -97,8 +94,8 @@ Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, if (state == State::AccessPointCreated || state == State::StationConnected) { std::memcpy(&out_network, &network_info, sizeof(network_info)); for (std::size_t i = 0; i < buffer_count; i++) { - out_updates[i].state_change = nodeChanges[i].state_change; - nodeChanges[i].state_change = NodeStateChange::None; + out_updates[i].state_change = node_changes[i].state_change; + node_changes[i].state_change = NodeStateChange::None; } return ResultSuccess; } @@ -168,9 +165,9 @@ Result LANDiscovery::Scan(std::vector& networks, u16& count, return ResultSuccess; } -Result LANDiscovery::SetAdvertiseData(std::vector& data) { +Result LANDiscovery::SetAdvertiseData(std::span data) { std::scoped_lock lock{packet_mutex}; - std::size_t size = data.size(); + const std::size_t size = data.size(); if (size > AdvertiseDataSizeMax) { return ResultAdvertiseDataTooLarge; } @@ -288,7 +285,7 @@ Result LANDiscovery::DestroyNetwork() { ResetStations(); SetState(State::AccessPointOpened); - LanEvent(); + lan_event(); return ResultSuccess; } @@ -323,12 +320,12 @@ Result LANDiscovery::Disconnect() { } SetState(State::StationOpened); - LanEvent(); + lan_event(); return ResultSuccess; } -Result LANDiscovery::Initialize(LanEventFunc lan_event, bool listening) { +Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) { std::scoped_lock lock{packet_mutex}; if (inited) { return ResultSuccess; @@ -341,7 +338,7 @@ Result LANDiscovery::Initialize(LanEventFunc lan_event, bool listening) { } connected_clients.clear(); - LanEvent = lan_event; + lan_event = lan_event_; SetState(State::Initialized); @@ -408,13 +405,13 @@ void LANDiscovery::OnDisconnectFromHost() { host_ip = std::nullopt; if (state == State::StationConnected) { SetState(State::StationOpened); - LanEvent(); + lan_event(); } } void LANDiscovery::OnNetworkInfoChanged() { if (IsNodeStateChanged()) { - LanEvent(); + lan_event(); } return; } @@ -439,7 +436,6 @@ void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data, packet.local_ip = GetLocalIp(); packet.remote_ip = remote_ip; - packet.data.clear(); packet.data.resize(sizeof(data)); std::memcpy(packet.data.data(), &data, sizeof(data)); SendPacket(packet); @@ -453,7 +449,6 @@ void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip packet.local_ip = GetLocalIp(); packet.remote_ip = remote_ip; - packet.data.clear(); SendPacket(packet); } @@ -465,7 +460,6 @@ void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) packet.broadcast = true; packet.local_ip = GetLocalIp(); - packet.data.clear(); packet.data.resize(sizeof(data)); std::memcpy(packet.data.data(), &data, sizeof(data)); SendPacket(packet); @@ -478,7 +472,6 @@ void LANDiscovery::SendBroadcast(Network::LDNPacketType type) { packet.broadcast = true; packet.local_ip = GetLocalIp(); - packet.data.clear(); SendPacket(packet); } @@ -581,9 +574,9 @@ bool LANDiscovery::IsNodeStateChanged() { for (int i = 0; i < NodeCountMax; i++) { if (nodes[i].is_connected != node_last_states[i]) { if (nodes[i].is_connected) { - nodeChanges[i].state_change |= NodeStateChange::Connect; + node_changes[i].state_change |= NodeStateChange::Connect; } else { - nodeChanges[i].state_change |= NodeStateChange::Disconnect; + node_changes[i].state_change |= NodeStateChange::Disconnect; } node_last_states[i] = nodes[i].is_connected; changed = true; @@ -598,15 +591,11 @@ bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) co return (flag_value & search_flag_value) == search_flag_value; } -int LANDiscovery::GetStationCount() { - int count = 0; - for (const auto& station : stations) { - if (station.GetStatus() != NodeStatus::Disconnected) { - count++; - } - } - - return count; +int LANDiscovery::GetStationCount() const { + return static_cast( + std::count_if(stations.begin(), stations.end(), [](const auto& station) { + return station.GetStatus() != NodeStatus::Disconnected; + })); } MacAddress LANDiscovery::GetFakeMac() const { diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h index 255342456e..3833cd764a 100644 --- a/src/core/hle/service/ldn/lan_discovery.h +++ b/src/core/hle/service/ldn/lan_discovery.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ protected: class LANDiscovery { public: - typedef std::function LanEventFunc; + using LanEventFunc = std::function; LANDiscovery(Network::RoomNetwork& room_network_); ~LANDiscovery(); @@ -58,7 +59,7 @@ public: DisconnectReason GetDisconnectReason() const; Result Scan(std::vector& networks, u16& count, const ScanFilter& filter); - Result SetAdvertiseData(std::vector& data); + Result SetAdvertiseData(std::span data); Result OpenAccessPoint(); Result CloseAccessPoint(); @@ -74,7 +75,7 @@ public: u16 local_communication_version); Result Disconnect(); - Result Initialize(LanEventFunc lan_event = empty_func, bool listening = true); + Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true); Result Finalize(); void ReceivePacket(const Network::LDNPacket& packet); @@ -94,7 +95,7 @@ protected: bool IsNodeStateChanged(); bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const; - int GetStationCount(); + int GetStationCount() const; MacAddress GetFakeMac() const; Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config, u16 local_communication_version); @@ -109,12 +110,12 @@ protected: void SendPacket(const Network::LDNPacket& packet); static const LanEventFunc empty_func; - const Ssid fake_ssid{"YuzuFakeSsidForLdn"}; + static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"}; bool inited{}; std::mutex packet_mutex; std::array stations; - std::array nodeChanges{}; + std::array node_changes{}; std::array node_last_states{}; std::unordered_map scan_results{}; NodeInfo node_info{}; @@ -124,9 +125,9 @@ protected: // TODO (flTobi): Should this be an std::set? std::vector connected_clients; - std::optional host_ip = std::nullopt; + std::optional host_ip; - LanEventFunc LanEvent; + LanEventFunc lan_event; Network::RoomNetwork& room_network; }; diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 6537f49cf2..ea3e7e55af 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -205,8 +205,6 @@ public: } void GetIpv4Address(Kernel::HLERequestContext& ctx) { - LOG_CRITICAL(Service_LDN, "called"); - const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface) { diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h index d6609fff55..44c2c773b0 100644 --- a/src/core/hle/service/ldn/ldn_types.h +++ b/src/core/hle/service/ldn/ldn_types.h @@ -31,13 +31,7 @@ enum class NodeStateChange : u8 { DisconnectAndConnect, }; -inline NodeStateChange operator|(NodeStateChange a, NodeStateChange b) { - return static_cast(static_cast(a) | static_cast(b)); -} - -inline NodeStateChange operator|=(NodeStateChange& a, NodeStateChange b) { - return a = a | b; -} +DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange) enum class ScanFilterFlag : u32 { None = 0, @@ -163,7 +157,7 @@ struct Ssid { Ssid() = default; - explicit Ssid(std::string_view data) { + constexpr explicit Ssid(std::string_view data) { length = static_cast(std::min(data.size(), SsidLengthMax)); data.copy(raw.data(), length); raw[length] = 0; @@ -176,6 +170,10 @@ struct Ssid { bool operator==(const Ssid& b) const { return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0); } + + bool operator!=(const Ssid& b) const { + return !operator==(b); + } }; static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 858ae1cfbb..057fd36617 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -188,7 +188,7 @@ std::vector GetAvailableNetworkInterfaces() { std::optional GetSelectedNetworkInterface() { const auto& selected_network_interface = Settings::values.network_interface.GetValue(); const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { + if (network_interfaces.empty()) { LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); return std::nullopt; } @@ -209,7 +209,7 @@ std::optional GetSelectedNetworkInterface() { void SelectFirstNetworkInterface() { const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { + if (network_interfaces.empty()) { return; } diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index 572e55a5b8..b94cb24ad7 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -716,7 +716,7 @@ RoomMember::CallbackHandle RoomMember::BindOnProxyPacketReceived( RoomMember::CallbackHandle RoomMember::BindOnLdnPacketReceived( std::function callback) { - return room_member_impl->Bind(callback); + return room_member_impl->Bind(std::move(callback)); } RoomMember::CallbackHandle RoomMember::BindOnRoomInformationChanged( diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index de1545216a..74d49dbd41 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -125,7 +125,7 @@ true - Multiplayer + &Multiplayer From 09a87966e06fd8ba1cf40332a6c4ef81f8a1eac8 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Tue, 20 Sep 2022 22:21:52 +0200 Subject: [PATCH 011/245] cmake: Fix FindPkgConfig --- CMakeLists.txt | 6 +++--- externals/ffmpeg/CMakeLists.txt | 2 +- externals/libusb/CMakeLists.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20dd1383f5..957df54f54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,7 +252,7 @@ if(ENABLE_QT) endif() # Check for headers - Include(FindPkgConfig REQUIRED) + find_package(PkgConfig REQUIRED) pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0) if (NOT QT_DEP_GLU_FOUND) message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \ @@ -386,7 +386,7 @@ endif() # Ensure libusb is properly configured (based on dolphin libusb include) if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB) - include(FindPkgConfig) + find_package(PkgConfig) if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD") pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24) else() @@ -410,7 +410,7 @@ set(FFmpeg_COMPONENTS swscale) if (UNIX AND NOT APPLE) - Include(FindPkgConfig REQUIRED) + find_package(PkgConfig REQUIRED) pkg_check_modules(LIBVA libva) endif() if (NOT YUZU_USE_BUNDLED_FFMPEG) diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 20ad716ea6..0baac8e170 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -43,7 +43,7 @@ if (NOT WIN32) CACHE PATH "Paths to FFmpeg libraries" FORCE) endforeach() - Include(FindPkgConfig REQUIRED) + find_package(PkgConfig REQUIRED) pkg_check_modules(LIBVA libva) pkg_check_modules(CUDA cuda) pkg_check_modules(FFNVCODEC ffnvcodec) diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt index 055b892952..3cb1b36878 100644 --- a/externals/libusb/CMakeLists.txt +++ b/externals/libusb/CMakeLists.txt @@ -108,7 +108,7 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE) target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - Include(FindPkgConfig) + find_package(PkgConfig) pkg_check_modules(LIBUDEV REQUIRED libudev) if (LIBUDEV_FOUND) From 5a74ced59a159c374071028d431fbf877c819b22 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Wed, 21 Sep 2022 11:51:31 -0500 Subject: [PATCH 012/245] yuzu: Silence some clang warnings --- src/core/hle/service/hid/irs.cpp | 3 ++- src/yuzu/applets/qt_controller.cpp | 2 +- src/yuzu/configuration/configure_input.cpp | 7 +++---- src/yuzu/main.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index c4b44cbf93..6a3453457f 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -542,7 +542,8 @@ Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_h Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( const Core::IrSensor::IrCameraHandle& camera_handle) { - ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id"); + const auto npad_id_max_index = static_cast(sizeof(StatusManager::device)); + ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id"); return shared_memory->device[camera_handle.npad_id]; } diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 1d8072243b..12efdc2162 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -291,7 +291,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() { // Here, we check and validate the current configuration against all applicable parameters. const auto num_connected_players = static_cast( std::count_if(player_groupboxes.begin(), player_groupboxes.end(), - [this](const QGroupBox* player) { return player->isChecked(); })); + [](const QGroupBox* player) { return player->isChecked(); })); const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index cb55472c9e..1db374d4a2 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -163,10 +163,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, [this, input_subsystem, &hid_core] { CallConfigureDialog(*this, input_subsystem, hid_core); }); - connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, - [this, input_subsystem, &hid_core] { - CallConfigureDialog(*this, input_subsystem); - }); + connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, [this, input_subsystem] { + CallConfigureDialog(*this, input_subsystem); + }); connect(ui->vibrationButton, &QPushButton::clicked, [this, &hid_core] { CallConfigureDialog(*this, hid_core); }); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3c1bd19db6..8c624427f7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2000,7 +2000,7 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src } void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { - const QString entry_type = [this, type] { + const QString entry_type = [type] { switch (type) { case InstalledEntryType::Game: return tr("Contents"); @@ -2097,7 +2097,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, const std::string& game_path) { - const QString question = [this, target] { + const QString question = [target] { switch (target) { case GameListRemoveTarget::GlShaderCache: return tr("Delete OpenGL Transferable Shader Cache?"); From db88eaa346f32f0c42bb38981e61af180ded3bcb Mon Sep 17 00:00:00 2001 From: Andrea Pappacoda Date: Thu, 22 Sep 2022 21:51:56 +0200 Subject: [PATCH 013/245] build(room): simplify yuzu-room installation CMake is able to automatically install binaries in the correct location. Also see my older patch, https://github.com/yuzu-emu/yuzu/commit/af94bf4a594b6a3599fae1d78e5d283b9f602032 Cc: @FearlessTobi --- src/dedicated_room/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt index 1efdbc1f76..2d9731f193 100644 --- a/src/dedicated_room/CMakeLists.txt +++ b/src/dedicated_room/CMakeLists.txt @@ -23,5 +23,5 @@ endif() target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if(UNIX AND NOT APPLE) - install(TARGETS yuzu-room RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") + install(TARGETS yuzu-room) endif() From 23589ad9b8a3d2e1eecd0243d592ce5c6e4138e6 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Tue, 18 Jan 2022 20:08:56 -0600 Subject: [PATCH 014/245] service: hid: Partially implement palma controller --- src/core/CMakeLists.txt | 2 + src/core/hle/service/hid/controllers/npad.cpp | 6 +- .../hle/service/hid/controllers/palma.cpp | 229 +++++++++ src/core/hle/service/hid/controllers/palma.h | 163 +++++++ src/core/hle/service/hid/errors.h | 2 + src/core/hle/service/hid/hid.cpp | 446 ++++++++++++++++-- src/core/hle/service/hid/hid.h | 29 ++ 7 files changed, 843 insertions(+), 34 deletions(-) create mode 100644 src/core/hle/service/hid/controllers/palma.cpp create mode 100644 src/core/hle/service/hid/controllers/palma.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 33cf470d5c..c176623234 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -460,6 +460,8 @@ add_library(core STATIC hle/service/hid/controllers/mouse.h hle/service/hid/controllers/npad.cpp hle/service/hid/controllers/npad.h + hle/service/hid/controllers/palma.cpp + hle/service/hid/controllers/palma.h hle/service/hid/controllers/stubbed.cpp hle/service/hid/controllers/stubbed.h hle/service/hid/controllers/touchscreen.cpp diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index cb29004e87..f8972ec7a5 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -660,7 +660,6 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: set_motion_state(sixaxis_fullkey_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::Handheld: @@ -676,6 +675,11 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing case Core::HID::NpadStyleIndex::JoyconRight: set_motion_state(sixaxis_right_lifo_state, motion_state[1]); break; + case Core::HID::NpadStyleIndex::Pokeball: + using namespace std::literals::chrono_literals; + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); + break; default: break; } diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp new file mode 100644 index 0000000000..575d4e626b --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -0,0 +1,229 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_) + : ControllerBase{hid_core_}, service_context{service_context_} { + controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); + operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); +} + +Controller_Palma::~Controller_Palma() = default; + +void Controller_Palma::OnInit() {} + +void Controller_Palma::OnRelease() {} + +void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + if (!IsControllerActivated()) { + return; + } +} + +Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, + PalmaConnectionHandle& handle) { + active_handle.npad_id = npad_id; + handle = active_handle; + return ResultSuccess; +} + +Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + ActivateController(); + return ResultSuccess; +} + +Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); + } + return operation_complete_event->GetReadableEvent(); +} + +Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation_type = operation.operation; + data = operation.data; + return ResultSuccess; +} + +Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, + u64 palma_activity) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::PlayActivity; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, + PalmaFrModeType fr_mode_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + fr_mode = fr_mode_; + return ResultSuccess; +} + +Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadStep; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +void Controller_Palma::ReadPalmaApplicationSection() {} + +void Controller_Palma::WritePalmaApplicationSection() {} + +Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadUniqueCode; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::SetUniqueCodeInvalid; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +void Controller_Palma::WritePalmaActivityEntry() {} + +Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, + u64 unknown) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, + u8* t_mem, u64 size) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteWaveEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + database_id_version = database_id_version_; + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data[0] = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( + const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation.data[0] = static_cast(database_id_version); + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +void Controller_Palma::SuspendPalmaFeature() {} + +Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return operation.result; +} +void Controller_Palma::ReadPalmaPlayLog() {} + +void Controller_Palma::ResetPalmaPlayLog() {} + +void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { + // If true controllers are able to be paired + is_connectable = is_all_connectable; +} + +void Controller_Palma::SetIsPalmaPairedConnectable() {} + +Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + // TODO: Do something + return ResultSuccess; +} + +void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} + +void Controller_Palma::CancelWritePalmaWaveEntry() {} + +void Controller_Palma::EnablePalmaBoostMode() {} + +void Controller_Palma::GetPalmaBluetoothAddress() {} + +void Controller_Palma::SetDisallowedPalmaConnection() {} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h new file mode 100644 index 0000000000..1d7fc94e12 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/errors.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { +class Controller_Palma final : public ControllerBase { +public: + using PalmaOperationData = std::array; + + // This is nn::hid::PalmaOperationType + enum class PalmaOperationType { + PlayActivity, + SetFrModeType, + ReadStep, + EnableStep, + ResetStep, + ReadApplicationSection, + WriteApplicationSection, + ReadUniqueCode, + SetUniqueCodeInvalid, + WriteActivityEntry, + WriteRgbLedPatternEntry, + WriteWaveEntry, + ReadDataBaseIdentificationVersion, + WriteDataBaseIdentificationVersion, + SuspendFeature, + ReadPlayLog, + ResetPlayLog, + }; + + // This is nn::hid::PalmaWaveSet + enum class PalmaWaveSet : u64 { + Small, + Medium, + Large, + }; + + // This is nn::hid::PalmaFrModeType + enum class PalmaFrModeType : u64 { + Off, + B01, + B02, + B03, + Downloaded, + }; + + // This is nn::hid::PalmaFeature + enum class PalmaFeature : u64 { + FrMode, + RumbleFeedback, + Step, + MuteSwitch, + }; + + // This is nn::hid::PalmaOperationInfo + struct PalmaOperationInfo { + PalmaOperationType operation{}; + Result result{PalmaResultSuccess}; + PalmaOperationData data{}; + }; + static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); + + // This is nn::hid::PalmaActivityEntry + struct PalmaActivityEntry { + u32 rgb_led_pattern_index; + INSERT_PADDING_BYTES(2); + PalmaWaveSet wave_set; + u32 wave_index; + INSERT_PADDING_BYTES(12); + }; + static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); + + struct PalmaConnectionHandle { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_BYTES(4); // Unknown + }; + static_assert(sizeof(PalmaConnectionHandle) == 0x8, + "PalmaConnectionHandle has incorrect size."); + + explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_); + ~Controller_Palma() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + + Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); + Result InitializePalma(const PalmaConnectionHandle& handle); + Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const; + Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const; + Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); + Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); + Result ReadPalmaStep(const PalmaConnectionHandle& handle); + Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); + Result ResetPalmaStep(const PalmaConnectionHandle& handle); + Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); + Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); + Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); + Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem, + u64 size); + Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_); + Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); + Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; + void SetIsPalmaAllConnectable(bool is_all_connectable); + Result PairPalma(const PalmaConnectionHandle& handle); + void SetPalmaBoostMode(bool boost_mode); + +private: + void ReadPalmaApplicationSection(); + void WritePalmaApplicationSection(); + void WritePalmaActivityEntry(); + void SuspendPalmaFeature(); + void ReadPalmaPlayLog(); + void ResetPalmaPlayLog(); + void SetIsPalmaPairedConnectable(); + void CancelWritePalmaWaveEntry(); + void EnablePalmaBoostMode(); + void GetPalmaBluetoothAddress(); + void SetDisallowedPalmaConnection(); + + bool is_connectable{}; + s32 database_id_version{}; + PalmaOperationInfo operation{}; + PalmaFrModeType fr_mode{}; + PalmaConnectionHandle active_handle{}; + + Core::HID::EmulatedController* controller; + + Kernel::KEvent* operation_complete_event; + KernelHelpers::ServiceContext& service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 4613a4e607..76208e9a4b 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -7,6 +7,7 @@ namespace Service::HID { +constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; @@ -17,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; constexpr Result NpadIsSameType{ErrorModule::HID, 602}; constexpr Result InvalidNpadId{ErrorModule::HID, 709}; constexpr Result NpadNotConnected{ErrorModule::HID, 710}; +constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 3d34571600..de3fae2cb5 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -27,6 +27,7 @@ #include "core/hle/service/hid/controllers/keyboard.h" #include "core/hle/service/hid/controllers/mouse.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" #include "core/hle/service/hid/controllers/stubbed.h" #include "core/hle/service/hid/controllers/touchscreen.h" #include "core/hle/service/hid/controllers/xpad.h" @@ -60,6 +61,7 @@ IAppletResource::IAppletResource(Core::System& system_, MakeControllerWithServiceContext(HidController::NPad, shared_memory); MakeController(HidController::Gesture, shared_memory); MakeController(HidController::ConsoleSixAxisSensor, shared_memory); + MakeControllerWithServiceContext(HidController::Palma, shared_memory); // Homebrew doesn't try to activate some controllers, so we activate them by default GetController(HidController::NPad).ActivateController(); @@ -310,36 +312,36 @@ Hid::Hid(Core::System& system_) {406, nullptr, "GetNpadLeftRightInterfaceType"}, {407, nullptr, "GetNpadOfHighestBatteryLevel"}, {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, - {500, nullptr, "GetPalmaConnectionHandle"}, - {501, nullptr, "InitializePalma"}, - {502, nullptr, "AcquirePalmaOperationCompleteEvent"}, - {503, nullptr, "GetPalmaOperationInfo"}, - {504, nullptr, "PlayPalmaActivity"}, - {505, nullptr, "SetPalmaFrModeType"}, - {506, nullptr, "ReadPalmaStep"}, - {507, nullptr, "EnablePalmaStep"}, - {508, nullptr, "ResetPalmaStep"}, - {509, nullptr, "ReadPalmaApplicationSection"}, - {510, nullptr, "WritePalmaApplicationSection"}, - {511, nullptr, "ReadPalmaUniqueCode"}, - {512, nullptr, "SetPalmaUniqueCodeInvalid"}, - {513, nullptr, "WritePalmaActivityEntry"}, - {514, nullptr, "WritePalmaRgbLedPatternEntry"}, - {515, nullptr, "WritePalmaWaveEntry"}, - {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, - {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, - {518, nullptr, "SuspendPalmaFeature"}, - {519, nullptr, "GetPalmaOperationResult"}, - {520, nullptr, "ReadPalmaPlayLog"}, - {521, nullptr, "ResetPalmaPlayLog"}, + {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, + {501, &Hid::InitializePalma, "InitializePalma"}, + {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, + {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, + {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, + {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, + {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, + {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, + {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, + {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, + {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, + {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, + {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, + {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, + {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, + {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, + {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, + {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, + {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, + {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, + {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, + {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, - {523, nullptr, "SetIsPalmaPairedConnectable"}, - {524, nullptr, "PairPalma"}, + {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, + {524, &Hid::PairPalma, "PairPalma"}, {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, - {526, nullptr, "CancelWritePalmaWaveEntry"}, - {527, nullptr, "EnablePalmaBoostMode"}, - {528, nullptr, "GetPalmaBluetoothAddress"}, - {529, nullptr, "SetDisallowedPalmaConnection"}, + {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, + {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, + {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, + {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, @@ -1878,14 +1880,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) { rb.Push(false); } -void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { +void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - const auto is_palma_all_connectable{rp.Pop()}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + Controller_Palma::PalmaConnectionHandle handle; + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(handle); +} + +void Hid::InitializePalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.InitializePalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); +} + +void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + Controller_Palma::PalmaOperationType operation_type; + Controller_Palma::PalmaOperationData data; + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); + + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(static_cast(operation_type)); +} + +void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto palma_activity{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", + connection_handle.npad_id, palma_activity); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto fr_mode{rp.PopEnum()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", + connection_handle.npad_id, fr_mode); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.ReadPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", + parameters.connection_handle.npad_id, parameters.is_enabled); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = + controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.ResetPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .ReadPalmaUniqueCode(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .SetPalmaUniqueCodeInvalid(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) { + LOG_CRITICAL(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto unknown{rp.Pop()}; + + const auto buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", + connection_handle.npad_id, unknown); + + applet_resource->GetController(HidController::Palma) + .WritePalmaRgbLedPatternEntry(connection_handle, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto wave_set{rp.PopEnum()}; + const auto unknown{rp.Pop()}; + const auto t_mem_size{rp.Pop()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto size{rp.Pop()}; + + ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); LOG_WARNING(Service_HID, - "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", - applet_resource_user_id, is_palma_all_connectable); + "(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, " + "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", + connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); + + applet_resource->GetController(HidController::Palma) + .WritePalmaWaveEntry(connection_handle, wave_set, + system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 database_id_version; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", + parameters.connection_handle.npad_id, parameters.database_id_version); + + applet_resource->GetController(HidController::Palma) + .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, + parameters.database_id_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .GetPalmaDataBaseIdentificationVersion(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + const auto result = applet_resource->GetController(HidController::Palma) + .GetPalmaOperationResult(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_palma_all_connectable; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", + parameters.is_palma_all_connectable, parameters.applet_resource_user_id); + + applet_resource->GetController(HidController::Palma) + .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::PairPalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .PairPalma(connection_handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1897,6 +2246,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); + applet_resource->GetController(HidController::Palma) + .SetPalmaBoostMode(palma_boost_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index ac43330223..340d26fdc9 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -33,6 +33,7 @@ enum class HidController : std::size_t { NPad, Gesture, ConsoleSixAxisSensor, + Palma, MaxControllers, }; @@ -166,8 +167,36 @@ private: void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); + void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx); + void InitializePalma(Kernel::HLERequestContext& ctx); + void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx); + void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx); + void PlayPalmaActivity(Kernel::HLERequestContext& ctx); + void SetPalmaFrModeType(Kernel::HLERequestContext& ctx); + void ReadPalmaStep(Kernel::HLERequestContext& ctx); + void EnablePalmaStep(Kernel::HLERequestContext& ctx); + void ResetPalmaStep(Kernel::HLERequestContext& ctx); + void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx); + void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx); + void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx); + void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx); + void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx); + void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx); + void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void SuspendPalmaFeature(Kernel::HLERequestContext& ctx); + void GetPalmaOperationResult(Kernel::HLERequestContext& ctx); + void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx); + void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); + void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx); + void PairPalma(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx); + void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx); + void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx); void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); From 087c6c2ef1668b264c14c0c43ea7abe681d985cc Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 25 Sep 2022 02:28:03 -0400 Subject: [PATCH 015/245] vulkan: automatically use larger staging buffer sizes when possible --- .../vk_staging_buffer_pool.cpp | 84 +++++++++++++------ .../renderer_vulkan/vk_staging_buffer_pool.h | 3 + 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 06f68d09af..7fb2569537 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -26,20 +26,39 @@ using namespace Common::Literals; constexpr VkDeviceSize MAX_ALIGNMENT = 256; // Maximum size to put elements in the stream buffer constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB; -// Stream buffer size in bytes -constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB; -constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; constexpr VkMemoryPropertyFlags HOST_FLAGS = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS; -bool IsStreamHeap(VkMemoryHeap heap) noexcept { - return STREAM_BUFFER_SIZE < (heap.size * 2) / 3; +static bool IsStreamHeap(VkMemoryHeap heap, size_t staging_buffer_size) noexcept { + return staging_buffer_size < (heap.size * 2) / 3; +} + +static bool HasLargeDeviceLocalHostVisibleMemory(const VkPhysicalDeviceMemoryProperties& props) { + const auto flags{VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT}; + + for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { + const auto& memory_type{props.memoryTypes[type_index]}; + + if ((memory_type.propertyFlags & flags) != flags) { + // Memory must be device local and host visible + continue; + } + + const auto& heap{props.memoryHeaps[memory_type.heapIndex]}; + if (heap.size >= 7168_MiB) { + // This is the right type of memory + return true; + } + } + + return false; } std::optional FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, - VkMemoryPropertyFlags flags) noexcept { + VkMemoryPropertyFlags flags, + size_t staging_buffer_size) noexcept { for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { if (((type_mask >> type_index) & 1) == 0) { // Memory type is incompatible @@ -50,7 +69,7 @@ std::optional FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p // Memory type doesn't have the flags we want continue; } - if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex])) { + if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex], staging_buffer_size)) { // Memory heap is not suitable for streaming continue; } @@ -61,17 +80,17 @@ std::optional FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p } u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, - bool try_device_local) { + bool try_device_local, size_t staging_buffer_size) { std::optional type; if (try_device_local) { // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this - type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS); + type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS, staging_buffer_size); if (type) { return *type; } } // Otherwise try without the DEVICE_LOCAL_BIT - type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS); + type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS, staging_buffer_size); if (type) { return *type; } @@ -79,20 +98,32 @@ u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_ throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); } -size_t Region(size_t iterator) noexcept { - return iterator / REGION_SIZE; +size_t Region(size_t iterator, size_t region_size) noexcept { + return iterator / region_size; } } // Anonymous namespace StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_) : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { + + const auto memory_properties{device.GetPhysical().GetMemoryProperties().memoryProperties}; + if (HasLargeDeviceLocalHostVisibleMemory(memory_properties)) { + // Possible on many integrated and newer discrete cards + staging_buffer_size = 1_GiB; + } else { + // Well-supported default size used by most Vulkan PC games + staging_buffer_size = 256_MiB; + } + + region_size = staging_buffer_size / StagingBufferPool::NUM_SYNCS; + const vk::Device& dev = device.GetLogical(); stream_buffer = dev.CreateBuffer(VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .size = STREAM_BUFFER_SIZE, + .size = staging_buffer_size, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, @@ -117,19 +148,18 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem .image = nullptr, .buffer = *stream_buffer, }; - const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties; VkMemoryAllocateInfo stream_memory_info{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = make_dedicated ? &dedicated_info : nullptr, .allocationSize = requirements.size, - .memoryTypeIndex = - FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true), + .memoryTypeIndex = FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true, + staging_buffer_size), }; stream_memory = dev.TryAllocateMemory(stream_memory_info); if (!stream_memory) { LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory"); - stream_memory_info.memoryTypeIndex = - FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false); + stream_memory_info.memoryTypeIndex = FindMemoryTypeIndex( + memory_properties, requirements.memoryTypeBits, false, staging_buffer_size); stream_memory = dev.AllocateMemory(stream_memory_info); } @@ -137,7 +167,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem stream_memory.SetObjectNameEXT("Stream Buffer Memory"); } stream_buffer.BindMemory(*stream_memory, 0); - stream_pointer = stream_memory.Map(0, STREAM_BUFFER_SIZE); + stream_pointer = stream_memory.Map(0, staging_buffer_size); } StagingBufferPool::~StagingBufferPool() = default; @@ -158,25 +188,25 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - if (AreRegionsActive(Region(free_iterator) + 1, - std::min(Region(iterator + size) + 1, NUM_SYNCS))) { + if (AreRegionsActive(Region(free_iterator, region_size) + 1, + std::min(Region(iterator + size, region_size) + 1, NUM_SYNCS))) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } const u64 current_tick = scheduler.CurrentTick(); - std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), - current_tick); + std::fill(sync_ticks.begin() + Region(used_iterator, region_size), + sync_ticks.begin() + Region(iterator, region_size), current_tick); used_iterator = iterator; free_iterator = std::max(free_iterator, iterator + size); - if (iterator + size >= STREAM_BUFFER_SIZE) { - std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, - current_tick); + if (iterator + size >= staging_buffer_size) { + std::fill(sync_ticks.begin() + Region(used_iterator, region_size), + sync_ticks.begin() + NUM_SYNCS, current_tick); used_iterator = 0; iterator = 0; free_iterator = size; - if (AreRegionsActive(0, Region(size) + 1)) { + if (AreRegionsActive(0, Region(size, region_size) + 1)) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 91dc84da80..90c67177f6 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -93,6 +93,9 @@ private: size_t free_iterator = 0; std::array sync_ticks{}; + size_t staging_buffer_size = 0; + size_t region_size = 0; + StagingBuffersCache device_local_cache; StagingBuffersCache upload_cache; StagingBuffersCache download_cache; From acc887cc3488ed90013ff3314650b5076c0426ae Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 25 Sep 2022 20:33:25 -0400 Subject: [PATCH 016/245] service: vi: Move VI results into its own file --- src/core/hle/service/vi/vi.cpp | 28 ++++++++++++---------------- src/core/hle/service/vi/vi_results.h | 13 +++++++++++++ 2 files changed, 25 insertions(+), 16 deletions(-) create mode 100644 src/core/hle/service/vi/vi_results.h diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 5468796482..0a347a0e9c 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -29,16 +29,12 @@ #include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" +#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_s.h" #include "core/hle/service/vi/vi_u.h" namespace Service::VI { -constexpr Result ERR_OPERATION_FAILED{ErrorModule::VI, 1}; -constexpr Result ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; -constexpr Result ERR_UNSUPPORTED{ErrorModule::VI, 6}; -constexpr Result ERR_NOT_FOUND{ErrorModule::VI, 7}; - struct DisplayInfo { /// The name of this particular display. char display_name[0x40]{"Default"}; @@ -348,7 +344,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -498,7 +494,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Display not found! display_name={}", name); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -554,14 +550,14 @@ private: if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Invalid scaling mode provided."); - rb.Push(ERR_OPERATION_FAILED); + rb.Push(ResultOperationFailed); return; } if (scaling_mode != NintendoScaleMode::ScaleToWindow && scaling_mode != NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); - rb.Push(ERR_UNSUPPORTED); + rb.Push(ResultNotSupported); return; } @@ -594,7 +590,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -602,7 +598,7 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -640,7 +636,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -648,7 +644,7 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -681,7 +677,7 @@ private: if (!vsync_event) { LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -764,7 +760,7 @@ private: return ConvertedScaleMode::PreserveAspectRatio; default: LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); - return ERR_OPERATION_FAILED; + return ResultOperationFailed; } } @@ -794,7 +790,7 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& if (!IsValidServiceAccess(permission, policy)) { LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_PERMISSION_DENIED); + rb.Push(ResultPermissionDenied); return; } diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h new file mode 100644 index 0000000000..a46c247d27 --- /dev/null +++ b/src/core/hle/service/vi/vi_results.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/result.h" + +namespace Service::VI { + +constexpr Result ResultOperationFailed{ErrorModule::VI, 1}; +constexpr Result ResultPermissionDenied{ErrorModule::VI, 5}; +constexpr Result ResultNotSupported{ErrorModule::VI, 6}; +constexpr Result ResultNotFound{ErrorModule::VI, 7}; + +} // namespace Service::VI From 41e855bd427e07ade6b9292e12bbe5a7c4e76a69 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 25 Sep 2022 21:20:36 -0400 Subject: [PATCH 017/245] service: vi: Retrieve vsync event once per display The display vsync event can only be retrieved once per display. Returns VI::ResultPermissionDenied if we attempt to retrieve the vsync event for the same display. Prevents games such as .hack//G.U. Last Recode from consuming all the handles in the handle table by spamming vsync event retrievals and allows it to go in game. --- src/core/hle/service/nvflinger/nvflinger.cpp | 7 ++++--- src/core/hle/service/nvflinger/nvflinger.h | 6 ++++-- src/core/hle/service/vi/display/vi_display.cpp | 15 +++++++++++++-- src/core/hle/service/vi/display/vi_display.h | 14 ++++++++++++-- src/core/hle/service/vi/vi.cpp | 14 +++++++++----- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 9b382bf566..93057e800a 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -22,6 +22,7 @@ #include "core/hle/service/nvflinger/ui/graphic_buffer.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" #include "video_core/gpu.h" namespace Service::NVFlinger { @@ -163,15 +164,15 @@ std::optional NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { return layer->GetBinderId(); } -Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { +ResultVal NVFlinger::FindVsyncEvent(u64 display_id) { const auto lock_guard = Lock(); auto* const display = FindDisplay(display_id); if (display == nullptr) { - return nullptr; + return VI::ResultNotFound; } - return &display->GetVSyncEvent(); + return display->GetVSyncEvent(); } VI::Display* NVFlinger::FindDisplay(u64 display_id) { diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 044ac6ac8d..3bbe5d92b9 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -11,6 +11,7 @@ #include #include "common/common_types.h" +#include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" namespace Common { @@ -71,8 +72,9 @@ public: /// Gets the vsync event for the specified display. /// - /// If an invalid display ID is provided, then nullptr is returned. - [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); + /// If an invalid display ID is provided, then VI::ResultNotFound is returned. + /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. + [[nodiscard]] ResultVal FindVsyncEvent(u64 display_id); /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b34febb503..aa49aa7752 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -19,6 +19,7 @@ #include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" namespace Service::VI { @@ -55,8 +56,18 @@ const Layer& Display::GetLayer(std::size_t index) const { return *layers.at(index); } -Kernel::KReadableEvent& Display::GetVSyncEvent() { - return vsync_event->GetReadableEvent(); +ResultVal Display::GetVSyncEvent() { + if (got_vsync_event) { + return ResultPermissionDenied; + } + + got_vsync_event = true; + + return GetVSyncEventUnchecked(); +} + +Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { + return &vsync_event->GetReadableEvent(); } void Display::SignalVSyncEvent() { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 3838bb5990..8dbb0ef80d 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -9,6 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/result.h" namespace Kernel { class KEvent; @@ -73,8 +74,16 @@ public: return layers.size(); } - /// Gets the readable vsync event. - Kernel::KReadableEvent& GetVSyncEvent(); + /** + * Gets the internal vsync event. + * + * @returns The internal Vsync event if it has not yet been retrieved, + * VI::ResultPermissionDenied otherwise. + */ + [[nodiscard]] ResultVal GetVSyncEvent(); + + /// Gets the internal vsync event. + Kernel::KReadableEvent* GetVSyncEventUnchecked(); /// Signals the internal vsync event. void SignalVSyncEvent(); @@ -118,6 +127,7 @@ private: std::vector> layers; Kernel::KEvent* vsync_event{}; + bool got_vsync_event{false}; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 0a347a0e9c..f083811ec9 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -671,19 +671,23 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop(); - LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + LOG_DEBUG(Service_VI, "called. display_id={}", display_id); const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); - if (!vsync_event) { - LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + if (vsync_event.Failed()) { + const auto result = vsync_event.Code(); + if (result == ResultNotFound) { + LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); + rb.Push(result); return; } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(vsync_event); + rb.PushCopyObjects(*vsync_event); } void ConvertScalingMode(Kernel::HLERequestContext& ctx) { From ad9f97cd8dbdaa95ef11418b699f74e57f37cbbc Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Mon, 26 Sep 2022 00:29:38 -0400 Subject: [PATCH 018/245] ci/linux: Drop linuxdeploy usage Recent versions of Docker appear to cause the Qt linuxdeploy plugin to throw a boost file copy error. This switches from linuxdeploy to a script of mine I've been working on for a while. --- .ci/scripts/linux/docker.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index e85dba0291..67726ee0fb 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh @@ -33,16 +33,14 @@ DESTDIR="$PWD/AppDir" ninja install rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester # Download tools needed to build an AppImage -wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-x86_64.AppImage -wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-plugin-qt-x86_64.AppImage +wget -nc https://raw.githubusercontent.com/lat9nq/deploy/main/linux/deploy-linux.sh wget -nc https://raw.githubusercontent.com/yuzu-emu/AppImageKit-checkrt/old/AppRun.sh wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/exec-x86_64.so # Set executable bit chmod 755 \ + deploy-linux.sh \ AppRun.sh \ exec-x86_64.so \ - linuxdeploy-x86_64.AppImage \ - linuxdeploy-plugin-qt-x86_64.AppImage # Workaround for https://github.com/AppImage/AppImageKit/issues/828 export APPIMAGE_EXTRACT_AND_RUN=1 @@ -52,7 +50,7 @@ mkdir -p AppDir/usr/optional/libstdc++ mkdir -p AppDir/usr/optional/libgcc_s # Deploy yuzu's needed dependencies -./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt +DEPLOY_QT=1 ./deploy-linux.sh AppDir/usr/bin/yuzu AppDir # Workaround for libQt5MultimediaGstTools indirectly requiring libwayland-client and breaking Vulkan usage on end-user systems find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print From 4b40799e0320ad79b5273f49f5857cfff38337fe Mon Sep 17 00:00:00 2001 From: The yuzu Community Date: Sat, 1 Oct 2022 04:10:52 +0000 Subject: [PATCH 019/245] Update translations (2022-10-01) --- dist/languages/ca.ts | 978 ++++++++++++++++-------------- dist/languages/cs.ts | 978 ++++++++++++++++-------------- dist/languages/da.ts | 982 ++++++++++++++++-------------- dist/languages/de.ts | 1110 ++++++++++++++++++---------------- dist/languages/el.ts | 1027 +++++++++++++++++--------------- dist/languages/es.ts | 1027 +++++++++++++++++--------------- dist/languages/fr.ts | 1240 ++++++++++++++++++++------------------ dist/languages/id.ts | 978 ++++++++++++++++-------------- dist/languages/it.ts | 1044 +++++++++++++++++--------------- dist/languages/ja_JP.ts | 1028 +++++++++++++++++--------------- dist/languages/ko_KR.ts | 1249 +++++++++++++++++++++------------------ dist/languages/nb.ts | 978 ++++++++++++++++-------------- dist/languages/nl.ts | 978 ++++++++++++++++-------------- dist/languages/pl.ts | 978 ++++++++++++++++-------------- dist/languages/pt_BR.ts | 1076 +++++++++++++++++---------------- dist/languages/pt_PT.ts | 1076 +++++++++++++++++---------------- dist/languages/ru_RU.ts | 984 ++++++++++++++++-------------- dist/languages/sv.ts | 978 ++++++++++++++++-------------- dist/languages/tr_TR.ts | 1234 ++++++++++++++++++++------------------ dist/languages/vi.ts | 978 ++++++++++++++++-------------- dist/languages/vi_VN.ts | 978 ++++++++++++++++-------------- dist/languages/zh_CN.ts | 994 ++++++++++++++++--------------- dist/languages/zh_TW.ts | 980 ++++++++++++++++-------------- 23 files changed, 12710 insertions(+), 11143 deletions(-) diff --git a/dist/languages/ca.ts b/dist/languages/ca.ts index 59f864f10d..1d33bd89f1 100644 --- a/dist/languages/ca.ts +++ b/dist/languages/ca.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -763,200 +763,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Activar GDB Stub - + Port: Port: - + Logging Registre - + Global Log Filter Filtre de registre global - + Show Log in Console Mostra el registre a la consola - + Open Log Location Obrir ubicació de l'arxiu del registre - + When checked, the max size of the log increases from 100 MB to 1 GB Quan està marcat, la mida màxima del registre augmenta de 100 MB a 1 GB - + Enable Extended Logging** Habilitar registre ampliat** - + Homebrew Homebrew - + Arguments String Cadena d'arguments - + Graphics Gràfics - + When checked, the graphics API enters a slower debugging mode Quan està marcat, l'API de gràfics entrarà en un mode de depuració més lent - + Enable Graphics Debugging Activar depuració de gràfics - + When checked, it enables Nsight Aftermath crash dumps Quan està marcat, habilitarà els volcats dels errors d'Nsight Aftermath - + Enable Nsight Aftermath Activar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Quan està marcat, bolcarà tots els shaders d'assemblador originals del cache de shaders del disc o del joc mentre els troba - + Dump Game Shaders Bolcar shaders del joc - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Quan està marcat, desactiva el compilador de macro Just In Time. Activar això fa que els jocs funcionin més lentament - + Disable Macro JIT Desactivar macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Quan està marcat, yuzu registrarà estadístiques sobre la cache de canonada compilada - + Enable Shader Feedback Activar informació de shaders - + When checked, it executes shaders without loop logic changes Quan està marcat, s'executaran els shaders sense canvis de lògica de bucle - + Disable Loop safety checks Desactivar comprovacions de seguretat de bucles - + Debugging Depuració - - Enable FS Access Log - Activar registre d'accés al FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Activa els serveis d'informes detallats** - + + Enable FS Access Log + Activar registre d'accés al FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avançat - + Kiosk (Quest) Mode Mode quiosc (Quest) - + Enable CPU Debugging Activar depuració de la CPU - + Enable Debug Asserts Activar alertes de depuració - + Enable Auto-Stub** Activar Auto-Stub** - + Enable All Controller Types Activar tots els tipus de controladors - + Disable Web Applet Desactivar el Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Això es restablirà automàticament quan es tanqui yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1575,37 +1610,47 @@ This would ban both their forum username and their IP address. Utilitzar temps ràpid a la GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtrat anisotròpic: - + Automatic Automàtic - + Default Valor predeterminat - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2097,7 +2142,7 @@ This would ban both their forum username and their IP address. - + Left Stick Palanca esquerra @@ -2191,14 +2236,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2217,7 +2262,7 @@ This would ban both their forum username and their IP address. - + Plus Més @@ -2230,15 +2275,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2295,231 +2340,236 @@ This would ban both their forum username and their IP address. - + Right Stick Palanca dreta - - - - + + + + Clear Esborrar - - - - - + + + + + [not set] [no establert] - - - Toggle button - Botó commutador - - - - + + Invert button Botó d'inversió - - + + + Toggle button + Botó commutador + + + + Invert axis Invertir eixos - - - + + + Set threshold Configurar llindar - - + + Choose a value between 0% and 100% Esculli un valor entre 0% i 100% - + + Toggle axis + + + + Set gyro threshold Configurar llindar giroscopi - + Map Analog Stick Configuració de palanca analògica - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Després de prémer D'acord, primer moveu el joystick horitzontalment i després verticalment. Per invertir els eixos, primer moveu el joystick verticalment i després horitzontalment. - + Center axis Centrar eixos - - + + Deadzone: %1% Zona morta: %1% - - + + Modifier Range: %1% Rang del modificador: %1% - - + + Pro Controller Controlador Pro - + Dual Joycons Joycons duals - + Left Joycon Joycon esquerra - + Right Joycon Joycon dret - + Handheld Portàtil - + GameCube Controller Controlador de GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Controlador NES - + SNES Controller Controlador SNES - + N64 Controller Controlador N64 - + Sega Genesis Sega Genesis - + Start / Pause Inici / Pausa - + Z Z - + Control Stick Palanca de control - + C-Stick C-Stick - + Shake! Sacseja! - + [waiting] [esperant] - + New Profile Nou perfil - + Enter a profile name: Introdueixi un nom de perfil: - - + + Create Input Profile Crear perfil d'entrada - + The given profile name is not valid! El nom de perfil introduït no és vàlid! - + Failed to create the input profile "%1" Error al crear el perfil d'entrada "%1" - + Delete Input Profile Eliminar perfil d'entrada - + Failed to delete the input profile "%1" Error al eliminar el perfil d'entrada "%1" - + Load Input Profile Carregar perfil d'entrada - + Failed to load the input profile "%1" Error al carregar el perfil d'entrada "%1" - + Save Input Profile Guardar perfil d'entrada - + Failed to save the input profile "%1" Error al guardar el perfil d'entrada "%1" @@ -2774,42 +2824,42 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo Desenvolupador - + Add-Ons Complements - + General General - + System Sistema - + CPU CPU - + Graphics Gràfics - + Adv. Graphics Gràfics avanç. - + Audio Àudio - + Properties Propietats @@ -3521,47 +3571,47 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo <html><head/><body><p>Llegeix l'entrada dels controladors des dels scripts en el mateix format que els scripts TAS-nx.<br/>Per a una explicació més detallada, si us plau, consulti la <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">pàgina d'ajuda</span></a> a la pàgina web de yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Per a comprovar quines tecles d'accés ràpid controlen la reproducció/gravació, si us plau, revisi la configuració de les tecles d'accés ràpid (Configuració -> General -> Tecles d'accés ràpid) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. AVÍS: Aquesta és una característica experimental.<br/>No es reproduiran els scripts perfectament amb l'actual i imperfecte mètode de sincronització de cuadres. - + Settings Paràmetres - + Enable TAS features Activar funcionalitats TAS - + Loop script Repetir script en bucle - + Pause execution during loads Pausar execució durant les càrregues - + Script Directory Directori d'scripts - + Path Ruta - + ... ... @@ -3971,7 +4021,7 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les - + Verify Verificar @@ -4058,7 +4108,7 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les - + Unspecified Sense especificar @@ -4073,17 +4123,36 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les El token no ha sigut verificat. El canvi al seu token no s'ha guardat. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Comprovant... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verificació fallida + + + Verification failed Verificació fallida - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verificació fallida. Comprovi que hagi ingressat el seu token correctament, i que la seva connexió a internet estigui funcionant. @@ -4152,12 +4221,12 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les DirectConnectWindow - + Connecting - + Connect @@ -4165,488 +4234,488 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Es recullen dades anònimes</a> per ajudar a millorar yuzu. <br/><br/>Desitja compartir les seves dades d'ús amb nosaltres? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Carregant Web applet... - + Disable Web Applet Desactivar el Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Desactivar l'Applet Web pot provocar comportaments indefinits i només hauria d'utilitzar-se amb Super Mario 3D All-Stars. Estàs segur de que vols desactivar l'Applet Web? (Això pot ser reactivat als paràmetres Debug.) - + The amount of shaders currently being built La quantitat de shaders que s'estan compilant actualment - + The current selected resolution scaling multiplier. El multiplicador d'escala de resolució seleccionat actualment. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocitat d'emulació actual. Valors superiors o inferiors a 100% indiquen que l'emulació s'està executant més ràpidament o més lentament que a la Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quants fotogrames per segon està mostrant el joc actualment. Això variarà d'un joc a un altre i d'una escena a una altra. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Temps que costa emular un fotograma de la Switch, sense tenir en compte la limitació de fotogrames o la sincronització vertical. Per a una emulació òptima, aquest valor hauria de ser com a màxim de 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Esborrar arxius recents - + &Continue &Continuar - + &Pause &Pausar - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu està executant un joc - + Warning Outdated Game Format Advertència format del joc desfasat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Està utilitzant el format de directori de ROM deconstruït per a aquest joc, que és un format desactualitzat que ha sigut reemplaçat per altres, com NCA, NAX, XCI o NSP. Els directoris de ROM deconstruïts careixen d'icones, metadades i suport d'actualitzacions.<br><br>Per a obtenir una explicació dels diversos formats de Switch que suporta yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>faci una ullada a la nostra wiki</a>. Aquest missatge no es tornarà a mostrar. - - + + Error while loading ROM! Error carregant la ROM! - + The ROM format is not supported. El format de la ROM no està suportat. - + An error occurred initializing the video core. S'ha produït un error inicialitzant el nucli de vídeo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu ha trobat un error mentre executava el nucli de vídeo. Això sol ser causat per controladors de la GPU obsolets, inclosos els integrats. Si us plau, consulti el registre per a més detalls. Per obtenir més informació sobre com accedir al registre, consulti la següent pàgina: <a href='https://yuzu-emu.org/help/reference/log-files/'>Com carregar el fitxer de registre</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Error al carregar la ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Si us plau, segueixi <a href='https://yuzu-emu.org/help/quickstart/'>la guia d'inici de yuzu</a> per a bolcar de nou els seus fitxers.<br>Pot consultar la wiki de yuzu wiki</a> o el Discord de yuzu</a> per obtenir ajuda. - + An unknown error occurred. Please see the log for more details. S'ha produït un error desconegut. Si us plau, consulti el registre per a més detalls. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Dades de partides guardades - + Mod Data Dades de mods - + Error Opening %1 Folder Error obrint la carpeta %1 - - + + Folder does not exist! La carpeta no existeix! - + Error Opening Transferable Shader Cache Error obrint la cache transferible de shaders - + Failed to create the shader cache directory for this title. No s'ha pogut crear el directori de la cache dels shaders per aquest títol. - + Contents Continguts - + Update Actualització - + DLC DLC - + Remove Entry Eliminar entrada - + Remove Installed Game %1? Eliminar el joc instal·lat %1? - - - - - - + + + + + + Successfully Removed S'ha eliminat correctament - + Successfully removed the installed base game. S'ha eliminat correctament el joc base instal·lat. - - - + + + Error Removing %1 Error eliminant %1 - + The base game is not installed in the NAND and cannot be removed. El joc base no està instal·lat a la NAND i no pot ser eliminat. - + Successfully removed the installed update. S'ha eliminat correctament l'actualització instal·lada. - + There is no update installed for this title. No hi ha cap actualització instal·lada per aquest títol. - + There are no DLC installed for this title. No hi ha cap DLC instal·lat per aquest títol. - + Successfully removed %1 installed DLC. S'ha eliminat correctament %1 DLC instal·lat/s. - + Delete OpenGL Transferable Shader Cache? Desitja eliminar la cache transferible de shaders d'OpenGL? - + Delete Vulkan Transferable Shader Cache? Desitja eliminar la cache transferible de shaders de Vulkan? - + Delete All Transferable Shader Caches? Desitja eliminar totes les caches transferibles de shaders? - + Remove Custom Game Configuration? Desitja eliminar la configuració personalitzada del joc? - + Remove File Eliminar arxiu - - + + Error Removing Transferable Shader Cache Error eliminant la cache transferible de shaders - - + + A shader cache for this title does not exist. No existeix una cache de shaders per aquest títol. - + Successfully removed the transferable shader cache. S'ha eliminat correctament la cache transferible de shaders. - + Failed to remove the transferable shader cache. No s'ha pogut eliminar la cache transferible de shaders. - - + + Error Removing Transferable Shader Caches Error al eliminar les caches de shaders transferibles - + Successfully removed the transferable shader caches. Caches de shaders transferibles eliminades correctament. - + Failed to remove the transferable shader cache directory. No s'ha pogut eliminar el directori de caches de shaders transferibles. - - + + Error Removing Custom Configuration Error eliminant la configuració personalitzada - + A custom configuration for this title does not exist. No existeix una configuració personalitzada per aquest joc. - + Successfully removed the custom game configuration. S'ha eliminat correctament la configuració personalitzada del joc. - + Failed to remove the custom game configuration. No s'ha pogut eliminar la configuració personalitzada del joc. - - + + RomFS Extraction Failed! La extracció de RomFS ha fallat! - + There was an error copying the RomFS files or the user cancelled the operation. S'ha produït un error copiant els arxius RomFS o l'usuari ha cancel·lat la operació. - + Full Completa - + Skeleton Esquelet - + Select RomFS Dump Mode Seleccioni el mode de bolcat de RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Si us plau, seleccioni la forma en que desitja bolcar la RomFS.<br>Completa copiarà tots els arxius al nou directori mentre que<br>esquelet només crearà l'estructura de directoris. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root No hi ha suficient espai lliure a %1 per extreure el RomFS. Si us plau, alliberi espai o esculli un altre directori de bolcat a Emulació > Configuració > Sistema > Sistema d'arxius > Carpeta arrel de bolcat - + Extracting RomFS... Extraient RomFS... - - + + Cancel Cancel·la - + RomFS Extraction Succeeded! Extracció de RomFS completada correctament! - + The operation completed successfully. L'operació s'ha completat correctament. - + Error Opening %1 Error obrint %1 - + Select Directory Seleccionar directori - + Properties Propietats - + The game properties could not be loaded. Les propietats del joc no s'han pogut carregar. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Executable de Switch (%1);;Tots els Arxius (*.*) - + Load File Carregar arxiu - + Open Extracted ROM Directory Obrir el directori de la ROM extreta - + Invalid Directory Selected Directori seleccionat invàlid - + The directory you have selected does not contain a 'main' file. El directori que ha seleccionat no conté un arxiu 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Arxiu de Switch Instal·lable (*.nca *.nsp *.xci);;Arxiu de Continguts Nintendo (*.nca);;Paquet d'enviament Nintendo (*.nsp);;Imatge de Cartutx NX (*.xci) - + Install Files Instal·lar arxius - + %n file(s) remaining %n arxiu(s) restants%n arxiu(s) restants - + Installing file "%1"... Instal·lant arxiu "%1"... - - + + Install Results Resultats instal·lació - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Per evitar possibles conflictes, no recomanem als usuaris que instal·lin jocs base a la NAND. Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i DLCs. - + %n file(s) were newly installed %n nou(s) arxiu(s) s'ha(n) instal·lat @@ -4654,7 +4723,7 @@ Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i - + %n file(s) were overwritten %n arxiu(s) s'han sobreescrit @@ -4662,7 +4731,7 @@ Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i - + %n file(s) failed to install %n arxiu(s) no s'han instal·lat @@ -4670,411 +4739,391 @@ Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i - + System Application Aplicació del sistema - + System Archive Arxiu del sistema - + System Application Update Actualització de l'aplicació del sistema - + Firmware Package (Type A) Paquet de firmware (Tipus A) - + Firmware Package (Type B) Paquet de firmware (Tipus B) - + Game Joc - + Game Update Actualització de joc - + Game DLC DLC del joc - + Delta Title Títol delta - + Select NCA Install Type... Seleccioni el tipus d'instal·lació NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleccioni el tipus de títol que desitja instal·lar aquest NCA com a: (En la majoria dels casos, el valor predeterminat 'Joc' està bé.) - + Failed to Install Ha fallat la instal·lació - + The title type you selected for the NCA is invalid. El tipus de títol seleccionat per el NCA és invàlid. - + File not found Arxiu no trobat - + File "%1" not found Arxiu "%1" no trobat - + OK D'acord - + Missing yuzu Account Falta el compte de yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Per tal d'enviar un cas de prova de compatibilitat de joc, ha de vincular el seu compte de yuzu.<br><br/>Per a vincular el seu compte de yuzu, vagi a Emulació & gt; Configuració & gt; Web. - + Error opening URL Error obrint URL - + Unable to open the URL "%1". No es pot obrir la URL "%1". - + TAS Recording Gravació TAS - + Overwrite file of player 1? Sobreescriure l'arxiu del jugador 1? - + Invalid config detected Configuració invàlida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. El controlador del mode portàtil no es pot fer servir en el mode acoblat. Es seleccionarà el controlador Pro en el seu lloc. - - + + Error Error - - + + The current game is not looking for amiibos El joc actual no està buscant amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'amiibo actual ha sigut eliminat - + Amiibo File (%1);; All Files (*.*) Arxiu Amiibo (%1);; Tots els Arxius (*.*) - + Load Amiibo Carregar Amiibo - - Error opening Amiibo data file - Error obrint l'arxiu de dades d'Amiibo - - - - Unable to open Amiibo file "%1" for reading. - No s'ha pogut obrir l'arxiu de dades d'Amiibo "%1" per a lectura. - - - - Error reading Amiibo data file - Error llegint l'arxiu de dades d'Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - No s'han pogut llegir completament les dades d'Amiibo. S'esperava llegir %1 bytes, però només s'han pogut llegir %2 bytes. - - - + Error loading Amiibo data Error al carregar les dades d'Amiibo - + Unable to load Amiibo data. No s'han pogut carregar les dades d'Amiibo. - + Capture Screenshot Captura de pantalla - + PNG Image (*.png) Imatge PNG (*.png) - + TAS state: Running %1/%2 Estat TAS: executant %1/%2 - + TAS state: Recording %1 Estat TAS: gravant %1 - + TAS state: Idle %1/%2 Estat TAS: inactiu %1/%2 - + TAS State: Invalid Estat TAS: invàlid - + &Stop Running &Parar l'execució - + &Start &Iniciar - + Stop R&ecording Parar g&ravació - + R&ecord G&ravar - + Building: %n shader(s) Construint: %n shader(s)Construint: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocitat: %1% / %2% - + Speed: %1% Velocitat: %1% - + Game: %1 FPS (Unlocked) Joc: %1 FPS (desbloquejat) - + Game: %1 FPS Joc: %1 FPS - + Frame: %1 ms Fotograma: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR ERROR GPU - + DOCKED - + HANDHELD - + NEAREST MÉS PROPER - - + + BILINEAR BILINEAL - + BICUBIC BICÚBIC - + GAUSSIAN GAUSSIÀ - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA SENSE AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. El joc que està intentant carregar requereix d'arxius addicionals de la seva Switch abans de poder jugar. <br/><br/>Per a obtenir més informació sobre com bolcar aquests arxius, vagi a la següent pàgina de la wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Bolcar arxius del sistema i les fonts compartides des d'una Consola Switch</a>. <br/><br/>Desitja tornar a la llista de jocs? Continuar amb l'emulació pot provocar el tancament inesperat, dades de partides guardades corruptes o altres errors. - + yuzu was unable to locate a Switch system archive. %1 yuzu no ha pogut localitzar l'arxiu de sistema de la Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu no ha pogut localitzar un arxiu de sistema de la Switch: %1. %2 - + System Archive Not Found Arxiu del sistema no trobat - + System Archive Missing Falta arxiu del sistema - + yuzu was unable to locate the Switch shared fonts. %1 yuzu no ha pogut trobar les fonts compartides de la Switch. %1 - + Shared Fonts Not Found Fonts compartides no trobades - + Shared Font Missing Falten les fonts compartides - + Fatal Error Error fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu ha trobat un error fatal, consulti el registre per a obtenir més detalls. Per a més informació sobre com accedir al registre, consulti la següent pàgina: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Com carregar l'arxiu de registre?</a>.<br/><br/> Desitja tornar al llistat de jocs? Continuar amb l'emulació pot provocar el tancament inesperat, dades de partides guardades corruptes o altres errors. - + Fatal Error encountered Trobat error fatal - + Confirm Key Rederivation Confirmi la clau de rederivació - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5091,37 +5140,37 @@ i opcionalment faci còpies de seguretat. Això eliminarà els arxius de les claus generats automàticament i tornarà a executar el mòdul de derivació de claus. - + Missing fuses Falten fusibles - + - Missing BOOT0 - Falta BOOT0 - + - Missing BCPKG2-1-Normal-Main - Falta BCPKG2-1-Normal-Main - + - Missing PRODINFO - Falta PRODINFO - + Derivation Components Missing Falten components de derivació - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Falten les claus d'encriptació. <br>Si us plau, segueixi <a href='https://yuzu-emu.org/help/quickstart/'>la guia ràpida de yuzu</a> per a obtenir totes les seves claus, firmware i jocs.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5130,39 +5179,39 @@ Això pot prendre fins a un minut depenent del rendiment del seu sistema. - + Deriving Keys Derivant claus - + Select RomFS Dump Target Seleccioni el destinatari per a bolcar el RomFS - + Please select which RomFS you would like to dump. Si us plau, seleccioni quin RomFS desitja bolcar. - + Are you sure you want to close yuzu? Està segur de que vol tancar yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Està segur de que vol aturar l'emulació? Qualsevol progrés no guardat es perdrà. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5544,12 +5593,12 @@ d'inici. HostRoomWindow - + Error Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5558,11 +5607,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5584,112 +5634,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Captura de pantalla - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Pantalla Completa - + Load File Carregar arxiu - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5804,42 +5853,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Jugadors - + Refreshing - + Refresh List @@ -6166,7 +6215,7 @@ Debug Message: - + Connected Connectat @@ -6188,7 +6237,7 @@ Debug Message: - + New Messages Received @@ -6292,22 +6341,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6315,7 +6381,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Error @@ -6374,7 +6440,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6429,7 +6495,7 @@ p, li { white-space: pre-wrap; } - + [not set] [no establert] @@ -6444,10 +6510,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eix %1%2 @@ -6461,9 +6527,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [desconegut] @@ -6628,15 +6694,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [invàlid] - - + + %1%2Hat %3 %1%2Rotació %3 @@ -6644,35 +6710,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eix %3 - + %1%2Axis %3,%4,%5 %1%2Eixos %3,%4,%5 - + %1%2Motion %3 %1%2Moviment %3 - - + + %1%2Button %3 %1%2Botó %3 - + [unused] [sense ús] @@ -6713,7 +6779,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/cs.ts b/dist/languages/cs.ts index cc83592a86..118bb0299d 100644 --- a/dist/languages/cs.ts +++ b/dist/languages/cs.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -755,200 +755,235 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj ConfigureDebug - + Debugger Debugger - + Enable GDB Stub Povolit GDB Stub - + Port: Port: - + Logging Logování - + Global Log Filter Centrální log filtr - + Show Log in Console Zobrazit log v konzoli - + Open Log Location Otevřít lokaci s logama - + When checked, the max size of the log increases from 100 MB to 1 GB Po povolení se zvýší maximální velikost logu z 100 MB na 1 GB. - + Enable Extended Logging** Povolit rozšířené logování - + Homebrew Homebrew - + Arguments String Argumenty - + Graphics Grafika - + When checked, the graphics API enters a slower debugging mode Po povolení se grafické API přepne do pomalejšího režimu ladění. - + Enable Graphics Debugging Zapnout ladění grafiky - + When checked, it enables Nsight Aftermath crash dumps Zaškrtnutí povolí crash dumpy Nsight Aftermath - + Enable Nsight Aftermath Povolit Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Když je zaškrtnuto, dumpne všechny originální shadery assembleru z diskové zálohy shaderů či hry jak jsou nalezeny. - + Dump Game Shaders Dumpnout shadery hry - + When checked, it will dump all the macro programs of the GPU Když je zaškrtnuto, dumpne všechny makro programy GPU - + Dump Maxwell Macros Dumpnout Maxwell Makra - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Po povolení se zakáže makro Just-in-Time překladač. Poté hry poběží pomaleji. - + Disable Macro JIT Zakázat Makro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Když je zaškrtnuto, yuzu bude logovat statistiky o kompilované mezipaměti pipelinu - + Enable Shader Feedback Povolit Shader Feedback - + When checked, it executes shaders without loop logic changes Když je zaškrtnuto, shadery budou exekutovány bez změn logických smyček. - + Disable Loop safety checks - + Debugging Ladění - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Pokročilé - + Kiosk (Quest) Mode Předváděcí (Quest/Kiosk) režim - + Enable CPU Debugging - + Enable Debug Asserts Povolit Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet Zakázat Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1567,37 +1602,47 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anizotropní filtrování: - + Automatic - + Default Výchozí - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2089,7 +2134,7 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + Left Stick Levá Páčka @@ -2183,14 +2228,14 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + L L - + ZL ZL @@ -2209,7 +2254,7 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + Plus Plus @@ -2222,15 +2267,15 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + R R - + ZR ZR @@ -2287,231 +2332,236 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + Right Stick Pravá páčka - - - - + + + + Clear Vyčistit - - - - - + + + + + [not set] [nenastaveno] - - - Toggle button - Přepnout tlačítko - - - - + + Invert button - - + + + Toggle button + Přepnout tlačítko + + + + Invert axis Převrátit osy - - - + + + Set threshold - - + + Choose a value between 0% and 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Namapovat analogovou páčku - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Po stisknutí OK nejprve posuňte joystick horizontálně, poté vertikálně. Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontálně. - + Center axis - - + + Deadzone: %1% Deadzone: %1% - - + + Modifier Range: %1% Rozsah modifikátoru: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Dual Joycons - + Left Joycon Levý Joycon - + Right Joycon Pravý Joycon - + Handheld V rukou - + GameCube Controller Ovladač GameCube - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Control Stick - + C-Stick C-Stick - + Shake! Shake! - + [waiting] [čekání] - + New Profile Nový profil - + Enter a profile name: Zadejte název profilu: - - + + Create Input Profile Vytvořit profil vstupu - + The given profile name is not valid! Zadaný název profilu není platný! - + Failed to create the input profile "%1" Nepodařilo se vytvořit profil vstupu "%1" - + Delete Input Profile Odstranit profil vstupu - + Failed to delete the input profile "%1" Nepodařilo se odstranit profil vstupu "%1" - + Load Input Profile Načíst profil vstupu - + Failed to load the input profile "%1" Nepodařilo se načíst profil vstupu "%1" - + Save Input Profile Uložit profil vstupu - + Failed to save the input profile "%1" Nepodařilo se uložit profil vstupu "%1" @@ -2766,42 +2816,42 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln Vývojář - + Add-Ons Doplňky - + General Obecné - + System Systém - + CPU CPU - + Graphics Grafika - + Adv. Graphics Pokroč. grafika - + Audio Zvuk - + Properties Vlastnosti @@ -3513,47 +3563,47 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings Nastavení - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Cesta - + ... ... @@ -3963,7 +4013,7 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z - + Verify Ověřit @@ -4050,7 +4100,7 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z - + Unspecified Nespecifikovaný @@ -4065,17 +4115,36 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z Token nebyl ověřen. Změna k vašemu tokenu nebyla uložena. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Ověřuji... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Ověřování selhalo + + + Verification failed Ověřování selhalo - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Ověřování selhalo. Zkontrolujte, zda jste zadali token správně a že vaše připojení k internetu funguje v pořádku. @@ -4144,12 +4213,12 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z DirectConnectWindow - + Connecting - + Connect @@ -4157,909 +4226,889 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymní data jsou sbírána</a> pro vylepšení yuzu. <br/><br/>Chcete s námi sdílet anonymní data? - + Telemetry Telemetry - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Načítání Web Appletu... - + Disable Web Applet Zakázat Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Počet aktuálně sestavovaných shaderů - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktuální emulační rychlost. Hodnoty vyšší než 100% indikují, že emulace běží rychleji nebo pomaleji než na Switchi. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Kolik snímků za sekundu aktuálně hra zobrazuje. Tohle závisí na hře od hry a scény od scény. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Čas potřebný na emulaci framu scény, nepočítá se limit nebo v-sync. Pro plnou rychlost by se tohle mělo pohybovat okolo 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Vymazat poslední soubory - + &Continue &Pokračovat - + &Pause &Pauza - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Varování Zastaralý Formát Hry - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Používáte rozbalený formát hry, který je zastaralý a byl nahrazen jinými jako NCA, NAX, XCI, nebo NSP. Rozbalená ROM nemá ikony, metadata, a podporu updatů.<br><br>Pro vysvětlení všech možných podporovaných typů, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>zkoukni naší wiki</a>. Tato zpráva se nebude znova zobrazovat. - - + + Error while loading ROM! Chyba při načítání ROM! - + The ROM format is not supported. Tento formát ROM není podporován. - + An error occurred initializing the video core. Nastala chyba při inicializaci jádra videa. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Chyba při načítání ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Pro extrakci souborů postupujte podle <a href='https://yuzu-emu.org/help/quickstart/'>rychlého průvodce yuzu</a>. Nápovědu naleznete na <br>wiki</a> nebo na Discordu</a>. - + An unknown error occurred. Please see the log for more details. Nastala chyba. Koukni do logu. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Uložit data - + Mod Data Módovat Data - + Error Opening %1 Folder Chyba otevírání složky %1 - - + + Folder does not exist! Složka neexistuje! - + Error Opening Transferable Shader Cache Chyba při otevírání přenositelné mezipaměti shaderů - + Failed to create the shader cache directory for this title. - + Contents Obsah - + Update Aktualizace - + DLC DLC - + Remove Entry Odebrat položku - + Remove Installed Game %1? Odebrat Nainstalovanou Hru %1? - - - - - - + + + + + + Successfully Removed Úspěšně odebráno - + Successfully removed the installed base game. Úspěšně odebrán nainstalovaný základ hry. - - - + + + Error Removing %1 Chyba při odstraňování %1 - + The base game is not installed in the NAND and cannot be removed. Základ hry není nainstalovaný na NAND a nemůže být odstraněn. - + Successfully removed the installed update. Úspěšně odebrána nainstalovaná aktualizace. - + There is no update installed for this title. Není nainstalovaná žádná aktualizace pro tento titul. - + There are no DLC installed for this title. Není nainstalované žádné DLC pro tento titul. - + Successfully removed %1 installed DLC. Úspěšně odstraněno %1 nainstalovaných DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? Odstranit vlastní konfiguraci hry? - + Remove File Odstranit soubor - - + + Error Removing Transferable Shader Cache Chyba při odstraňování přenositelné mezipaměti shaderů - - + + A shader cache for this title does not exist. Mezipaměť shaderů pro tento titul neexistuje. - + Successfully removed the transferable shader cache. Přenositelná mezipaměť shaderů úspěšně odstraněna - + Failed to remove the transferable shader cache. Nepodařilo se odstranit přenositelnou mezipaměť shaderů - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Chyba při odstraňování vlastní konfigurace hry - + A custom configuration for this title does not exist. Vlastní konfigurace hry pro tento titul neexistuje. - + Successfully removed the custom game configuration. Úspěšně odstraněna vlastní konfigurace hry. - + Failed to remove the custom game configuration. Nepodařilo se odstranit vlastní konfiguraci hry. - - + + RomFS Extraction Failed! Extrakce RomFS se nepovedla! - + There was an error copying the RomFS files or the user cancelled the operation. Nastala chyba při kopírování RomFS souborů, nebo uživatel operaci zrušil. - + Full Plný - + Skeleton Kostra - + Select RomFS Dump Mode Vyber RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Vyber jak by si chtěl RomFS vypsat.<br>Plné zkopíruje úplně všechno, ale<br>kostra zkopíruje jen strukturu složky. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Extrahuji RomFS... - - + + Cancel Zrušit - + RomFS Extraction Succeeded! Extrakce RomFS se povedla! - + The operation completed successfully. Operace byla dokončena úspěšně. - + Error Opening %1 Chyba při otevírání %1 - + Select Directory Vybraná Složka - + Properties Vlastnosti - + The game properties could not be loaded. Herní vlastnosti nemohly být načteny. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Executable (%1);;Všechny soubory (*.*) - + Load File Načíst soubor - + Open Extracted ROM Directory Otevřít složku s extrahovanou ROM - + Invalid Directory Selected Vybraná složka je neplatná - + The directory you have selected does not contain a 'main' file. Složka kterou jste vybrali neobsahuje soubor "main" - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Instalovatelný soubor pro Switch (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Instalovat Soubory - + %n file(s) remaining - + Installing file "%1"... Instalování souboru "%1"... - - + + Install Results Výsledek instalace - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Abychom předešli možným konfliktům, nedoporučujeme uživatelům instalovat základní hry na paměť NAND. Tuto funkci prosím používejte pouze k instalaci aktualizací a DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systémová Aplikace - + System Archive Systémový archív - + System Application Update Systémový Update Aplikace - + Firmware Package (Type A) Firmware-ový baliček (Typu A) - + Firmware Package (Type B) Firmware-ový baliček (Typu B) - + Game Hra - + Game Update Update Hry - + Game DLC Herní DLC - + Delta Title Delta Title - + Select NCA Install Type... Vyberte typ instalace NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vyberte typ title-u, který chcete nainstalovat tenhle NCA jako: (Většinou základní "game" stačí.) - + Failed to Install Chyba v instalaci - + The title type you selected for the NCA is invalid. Tento typ pro tento NCA není platný. - + File not found Soubor nenalezen - + File "%1" not found Soubor "%1" nenalezen - + OK OK - + Missing yuzu Account Chybí účet yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Pro přidání recenze kompatibility je třeba mít účet yuzu<br><br/>Pro nalinkování yuzu účtu jdi do Emulace &gt; Konfigurace &gt; Web. - + Error opening URL Chyba při otevírání URL - + Unable to open the URL "%1". Nelze otevřít URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected Zjištěno neplatné nastavení - + Handheld controller can't be used on docked mode. Pro controller will be selected. Ruční ovladač nelze používat v dokovacím režimu. Bude vybrán ovladač Pro Controller. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Soubor Amiibo (%1);; Všechny Soubory (*.*) - + Load Amiibo Načíst Amiibo - - Error opening Amiibo data file - Chyba při načítání souboru Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Amiibo "%1" nešlo otevřít v řežimu pro čtení. - - - - Error reading Amiibo data file - Chyba načítání Amiiba - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Načtení celého Amiiba nebylo možné. Očekáváno bylo %1 bytů, ale pouze %2 bytů se načetlo. - - - + Error loading Amiibo data Chyba načítání Amiiba - + Unable to load Amiibo data. Načtení Amiiba nebylo možné - + Capture Screenshot Pořídit Snímek Obrazovky - + PNG Image (*.png) PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Rychlost: %1% / %2% - + Speed: %1% Rychlost: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Hra: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMÁLNÍ - + GPU HIGH GPU VYSOKÝ - + GPU EXTREME GPU EXTRÉMNÍ - + GPU ERROR GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Hra, kterou se snažíte načíst potřebuje další data z vašeho Switche, než bude moci být načtena.<br/><br/>Pro více informací o získání těchto souboru se koukněte na wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Získávání Systémových Archivů a Sdílených Fontu z konzole Switch</a>.<br/><br/>Přejete si odejít do listu her? Pokračování v emulaci by mohlo mít negativní účinky jako crashe, rozbité savy , nebo další bugy. - + yuzu was unable to locate a Switch system archive. %1 Aplikace yuzu nenašla systémový archiv Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Aplikace yuzu nenašla systémový archiv Switch: %1. %2 - + System Archive Not Found Systémový Archív Nenalezen - + System Archive Missing Chybí systémový archiv - + yuzu was unable to locate the Switch shared fonts. %1 Aplikace yuzu nenašla sdílená písma Switch. %1 - + Shared Fonts Not Found Sdílené Fonty Nenalezeny - + Shared Font Missing Chybí sdílené písmo - + Fatal Error Fatální Chyba - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu narazilo na fatální chybu, prosím kouknšte do logu pro více informací. Pro více informací jak se dostat do logu se koukněte na následující stránku: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'> Jak Uploadnout Log</a>.<br/><br/>Přejete si odejít do listu her? Pokračování v emulaci může mít za následek crashe, rozbité savy, nebo další bugy. - + Fatal Error encountered Vyskytla se kritická chyba - + Confirm Key Rederivation Potvďte Rederivaci Klíčů - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5076,37 +5125,37 @@ a udělejte si zálohu. Toto vymaže věechny vaše automaticky generované klíče a znova spustí modul derivace klíčů. - + Missing fuses Chybí Fuses - + - Missing BOOT0 - Chybí BOOT0 - + - Missing BCPKG2-1-Normal-Main - Chybí BCPKG2-1-Normal-Main - + - Missing PRODINFO - Chybí PRODINFO - + Derivation Components Missing Chybé odvozené komponenty - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5115,39 +5164,39 @@ Tohle může zabrat až minutu podle výkonu systému. - + Deriving Keys Derivuji Klíče - + Select RomFS Dump Target Vyberte Cíl vypsaní RomFS - + Please select which RomFS you would like to dump. Vyberte, kterou RomFS chcete vypsat. - + Are you sure you want to close yuzu? Jste si jist, že chcete zavřít yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Jste si jist, že chcete ukončit emulaci? Jakýkolic neuložený postup bude ztracen. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5525,12 +5574,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5539,11 +5588,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5565,112 +5615,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Pořídit Snímek Obrazovky - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Celá Obrazovka - + Load File Načíst soubor - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5784,42 +5833,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Hráči - + Refreshing - + Refresh List @@ -6146,7 +6195,7 @@ Debug Message: - + Connected Připojeno @@ -6168,7 +6217,7 @@ Debug Message: - + New Messages Received @@ -6272,22 +6321,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6295,7 +6361,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6354,7 +6420,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6409,7 +6475,7 @@ p, li { white-space: pre-wrap; } - + [not set] [Nenastaveno] @@ -6424,10 +6490,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Osa %1%2 @@ -6441,9 +6507,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [Neznámá] @@ -6608,15 +6674,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6624,35 +6690,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [nepoužito] @@ -6693,7 +6759,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/da.ts b/dist/languages/da.ts index b6dea2f0dc..c7d8c76038 100644 --- a/dist/languages/da.ts +++ b/dist/languages/da.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -754,200 +754,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Aktiver GDB Stub - + Port: Port: - + Logging Logføring - + Global Log Filter Globalt Logfilter - + Show Log in Console Vis Log i Konsol - + Open Log Location Åbn Logplacering - + When checked, the max size of the log increases from 100 MB to 1 GB Når valgt, øges loggens maksimale størrelse fra 100 MB til 1 GB - + Enable Extended Logging** Aktivér Udvidet Logning** - + Homebrew Hjemmebrændt - + Arguments String Argumentsstreng - + Graphics Grafik - + When checked, the graphics API enters a slower debugging mode Når valgt, påbegynder grafik-APIen en langsommere fejlfindingstilstand - + Enable Graphics Debugging Aktivér Grafik-Fejlfinding - + When checked, it enables Nsight Aftermath crash dumps Når valgt, aktiverer det Nsight Aftermath nedbruds-nedfældelser - + Enable Nsight Aftermath Aktivér Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Når valgt, deaktiverer det makro-Just-In-Time-kompilatoren. Aktivering heraf får spil til, at køre langsommere - + Disable Macro JIT Deaktivér Makro-JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Når valgt, vil yuzu logføre statistikker om det kompilerede rørlinje-mellemlager - + Enable Shader Feedback Aktivér Shader-Tilbagemelding - + When checked, it executes shaders without loop logic changes Når valgt, eksekverer den shadere, uden loop-logik-forandringer - + Disable Loop safety checks Deaktivér Loop-sikkerhedskontrol - + Debugging Fejlfinding - - Enable FS Access Log - Aktivér FS-Tilgangslog - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Aktivér Vitterlig Rapporteringstjeneste - + + Enable FS Access Log + Aktivér FS-Tilgangslog + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avanceret - + Kiosk (Quest) Mode Kiosk (Rejse)-Tilstand - + Enable CPU Debugging Aktivér CPU-Fejlfinding - + Enable Debug Asserts Aktivér Fejlfindingshævdelser - + Enable Auto-Stub** Aktivér Automatisk Stub** - + Enable All Controller Types - + Disable Web Applet Deaktivér Net-Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Dette vil automatisk blive nulstillet, når yuzu lukkes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1566,37 +1601,47 @@ This would ban both their forum username and their IP address. Brug Hurtig GPU-Tid (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropisk Filtrering: - + Automatic - + Default Standard - + 2x - + 4x - + 8x - + 16x @@ -2088,7 +2133,7 @@ This would ban both their forum username and their IP address. - + Left Stick Venstre Styrepind @@ -2182,14 +2227,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2208,7 +2253,7 @@ This would ban both their forum username and their IP address. - + Plus Plus @@ -2221,15 +2266,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2286,231 +2331,236 @@ This would ban both their forum username and their IP address. - + Right Stick Højre Styrepind - - - - + + + + Clear Ryd - - - - - + + + + + [not set] [ikke indstillet] - - - Toggle button - Funktionsskifteknap - - - - + + Invert button - - + + + Toggle button + Funktionsskifteknap + + + + Invert axis Omvend akser - - - + + + Set threshold Angiv tærskel - - + + Choose a value between 0% and 100% Vælg en værdi imellem 0% og 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Tilsted Analog Pind - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Bevæg, efter tryk på OK, først din styrepind vandret og så lodret. Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. - + Center axis - - + + Deadzone: %1% - Dødzone: 1% + Dødzone: %1% - - + + Modifier Range: %1% Forandringsrækkevidde: %1% - - + + Pro Controller Pro-Styringsenhed - + Dual Joycons Dobbelt-Joycon - + Left Joycon Venstre Joycon - + Right Joycon Højre Joycon - + Handheld Håndholdt - + GameCube Controller GameCube-Styringsenhed - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Styrepind - + C-Stick C-Pind - + Shake! Ryst! - + [waiting] [venter] - + New Profile Ny Profil - + Enter a profile name: Indtast et profilnavn: - - + + Create Input Profile Opret Input-Profil - + The given profile name is not valid! Det angivne profilnavn er ikke gyldigt! - + Failed to create the input profile "%1" Oprettelse af input-profil "%1" mislykkedes - + Delete Input Profile Slet Input-Profil - + Failed to delete the input profile "%1" Sletning af input-profil "%1" mislykkedes - + Load Input Profile Indlæs Input-Profil - + Failed to load the input profile "%1" Indlæsning af input-profil "%1" mislykkedes - + Save Input Profile Gem Input-Profil - + Failed to save the input profile "%1" Lagring af input-profil "%1" mislykkedes @@ -2765,42 +2815,42 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret.Udvikler - + Add-Ons Tilføjelser - + General Generelt - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics - + Audio Lyd - + Properties Egenskaber @@ -3042,7 +3092,7 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. Deadzone: %1% - Dødzone: 1% + Dødzone: %1% @@ -3512,47 +3562,47 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret.<html><head/><body><p>Læser styringsenheds input fra skrift, i samme format som TAS-nx skrifter.<br/>Konsultér venligst <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">hjælp-siden</span></a>, for en mere detaljeret forklaring, på yuzu-hjemmesiden.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Referér venligst til Genvajstast-indstillingerne (Konfigurér -> Generelt -> Genvejstaster), for at kontrollere hvilke genvejstaster, der kontrollerer afspilning/optagelse. - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ADVARSEL: Dette er en eksperimentiel funktion.<br/>Det vil ikke afspille skrifter perfekt til billedet, med den aktuelle, uperfekte synkroniseringsmetode. - + Settings Indstillinger - + Enable TAS features Aktivér TAS-funktioner - + Loop script Loop skrift - + Pause execution during loads Sæt eksekvering på pause under indlæsninger - + Script Directory Skriftmappe - + Path Sti - + ... ... @@ -3962,7 +4012,7 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r - + Verify Bekræft @@ -4049,7 +4099,7 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r - + Unspecified Uspecificeret @@ -4064,17 +4114,36 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r Token blev ikke bekræftet. Ændringen af dit token er ikke blevet gemt. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Bekræfter... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Bekræftelse mislykkedes + + + Verification failed Bekræftelse mislykkedes - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Bekræftelse mislykkedes. Kontrollér at du har indtastet dit token korrekt, og at din internetforbindelse virker. @@ -4143,12 +4212,12 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r DirectConnectWindow - + Connecting - + Connect @@ -4156,907 +4225,887 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonym data indsamles</a>, for at hjælp med, at forbedre yuzu. <br/><br/>Kunne du tænke dig, at dele dine brugsdata med os? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Indlæser Net-Applet... - + Disable Web Applet Deaktivér Net-Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktuel emuleringshastighed. Værdier højere eller lavere end 100% indikerer, at emulering kører hurtigere eller langsommere end en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + VULKAN - + OPENGL - + &Clear Recent Files - + &Continue - + &Pause - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Advarsel, Forældet Spilformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. - - + + Error while loading ROM! Fejl under indlæsning af ROM! - + The ROM format is not supported. ROM-formatet understøttes ikke. - + An error occurred initializing the video core. Der skete en fejl under initialisering af video-kerne. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data - + Mod Data - + Error Opening %1 Folder Fejl ved Åbning af %1 Mappe - - + + Folder does not exist! Mappe eksisterer ikke! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - + Contents - + Update - + DLC - + Remove Entry - + Remove Installed Game %1? - - - - - - + + + + + + Successfully Removed - + Successfully removed the installed base game. - - - + + + Error Removing %1 - + The base game is not installed in the NAND and cannot be removed. - + Successfully removed the installed update. - + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! RomFS-Udpakning Mislykkedes! - + There was an error copying the RomFS files or the user cancelled the operation. Der skete en fejl ved kopiering af RomFS-filerne, eller brugeren afbrød opgaven. - + Full Fuld - + Skeleton Skelet - + Select RomFS Dump Mode Vælg RomFS-Nedfældelsestilstand - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Udpakker RomFS... - - + + Cancel Afbryd - + RomFS Extraction Succeeded! RomFS-Udpakning Lykkedes! - + The operation completed successfully. Fuldførelse af opgaven lykkedes. - + Error Opening %1 Fejl ved Åbning af %1 - + Select Directory Vælg Mappe - + Properties Egenskaber - + The game properties could not be loaded. Spil-egenskaberne kunne ikke indlæses. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch-Eksekverbar (%1);;Alle filer (*.*) - + Load File Indlæs Fil - + Open Extracted ROM Directory Åbn Udpakket ROM-Mappe - + Invalid Directory Selected Ugyldig Mappe Valgt - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Installér fil "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemapplikation - + System Archive Systemarkiv - + System Application Update Systemapplikationsopdatering - + Firmware Package (Type A) Firmwarepakke (Type A) - + Firmware Package (Type B) Firmwarepakke (Type B) - + Game Spil - + Game Update Spilopdatering - + Game DLC Spiludvidelse - + Delta Title Delta-Titel - + Select NCA Install Type... Vælg NCA-Installationstype... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install Installation mislykkedes - + The title type you selected for the NCA is invalid. - + File not found Fil ikke fundet - + File "%1" not found Fil "%1" ikke fundet - + OK OK - + Missing yuzu Account Manglende yuzu-Konto - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Amiibo-Fil (%1);; Alle Filer (*.*) - + Load Amiibo Indlæs Amiibo - - Error opening Amiibo data file - Fejl ved åbning af Amiibo-datafil - - - - Unable to open Amiibo file "%1" for reading. - Ude af stand til, at åbne Amiibo-fil "%1" til indlæsning. - - - - Error reading Amiibo data file - Fejl ved indlæsning af Amiibo-datafil - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - - - - + Error loading Amiibo data Fejl ved indlæsning af Amiibo-data - + Unable to load Amiibo data. Ude af stand til, at indlæse Amiibo-data. - + Capture Screenshot Optag Skærmbillede - + PNG Image (*.png) PNG-Billede (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Hastighed: %1% / %2% - + Speed: %1% Hastighed: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Spil: %1 FPS - + Frame: %1 ms Billede: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + yuzu was unable to locate a Switch system archive. %1 yuzu var ude af stand til, at lokalisere et Switch-systemarkiv. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu var ude af stand til, at lokalisere et Switch-systemarkiv. %1. %2 - + System Archive Not Found Systemarkiv Ikke Fundet - + System Archive Missing Systemarkiv Mangler - + yuzu was unable to locate the Switch shared fonts. %1 yuzu var ude af stand til, at finde delte Switch-skrifttyper. %1 - + Shared Fonts Not Found Delte Skrifttyper Ikke Fundet - + Shared Font Missing Delte Skrifttyper Mangler - + Fatal Error Fatal Fejl - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + Fatal Error encountered Stødte på Fatal Fejl - + Confirm Key Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5067,76 +5116,76 @@ This will delete your autogenerated key files and re-run the key derivation modu - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. - + Deriving Keys - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close yuzu? Er du sikker på, at du vil lukke yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Er du sikker på, at du vil stoppe emulereingen? Enhver ulagret data, vil gå tabt. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5511,12 +5560,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5525,11 +5574,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5551,112 +5601,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Optag Skærmbillede - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Fuldskærm - + Load File Indlæs Fil - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5770,42 +5819,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Spillere - + Refreshing - + Refresh List @@ -6132,7 +6181,7 @@ Debug Message: - + Connected Tilsluttet @@ -6154,7 +6203,7 @@ Debug Message: - + New Messages Received @@ -6258,22 +6307,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6281,7 +6347,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6336,7 +6402,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6391,7 +6457,7 @@ p, li { white-space: pre-wrap; } - + [not set] [ikke indstillet] @@ -6406,10 +6472,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Akse %1%2 @@ -6423,9 +6489,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [ukendt] @@ -6590,15 +6656,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6606,35 +6672,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [ubrugt] @@ -6675,7 +6741,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/de.ts b/dist/languages/de.ts index bb35ff2315..b7299e34dc 100644 --- a/dist/languages/de.ts +++ b/dist/languages/de.ts @@ -87,86 +87,86 @@ p, li { white-space: pre-wrap; } Send Chat Message - + Chatnachricht senden Send Message - + Nachricht senden - + Members - + Mitglieder - + %1 has joined %1 ist beigetreten - + %1 has left %1 ist gegangen - + %1 has been kicked %1 wurde gekickt - + %1 has been banned %1 wurde gebannt - + %1 has been unbanned %1 wurde entbannt - + View Profile Profil ansehen - - + + Block Player Spieler blockieren - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick Kicken - + Ban Bannen - + Kick Player Spieler kicken - + Are you sure you would like to <b>kick</b> %1? - + Ban Player Spieler bannen - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -206,7 +206,7 @@ This would ban both their forum username and their IP address. Disconnected - + Verbindung getrennt @@ -378,7 +378,7 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Infrarotkamera konfigurieren @@ -393,7 +393,7 @@ This would ban both their forum username and their IP address. Input device: - + Eingabegerät: @@ -461,7 +461,7 @@ This would ban both their forum username and their IP address. Paranoid (disables most optimizations) - + Paranoid (deaktiviert die meisten Optimierungen) @@ -751,200 +751,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Debugger - + Enable GDB Stub GDB-Stub aktivieren - + Port: Port: - + Logging Logging - + Global Log Filter Globaler Log-Filter - + Show Log in Console Log in der Konsole zeigen - + Open Log Location Log-Verzeichnis öffnen - + When checked, the max size of the log increases from 100 MB to 1 GB Wenn diese Option aktiviert ist, erhöht sich die maximale Größe des Logs von 100 MB auf 1 GB - + Enable Extended Logging** - + Erweitertes Logging aktivieren** - + Homebrew Homebrew - + Arguments String String-Argumente - + Graphics Grafik - + When checked, the graphics API enters a slower debugging mode Wenn diese Option aktiviert ist, wechselt die Grafik-API in einen langsameren Debug-Modus - + Enable Graphics Debugging Grafik-Debugging aktivieren - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath Nsight Aftermath aktivieren - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Diese Option deaktiviert den Macro-JIT-Compiler. Dies wird die Geschwindigkeit verringern. - + Disable Macro JIT Macro-JIT deaktivieren - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + Shader-Feedback aktivieren - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Debugging - - Enable FS Access Log - FS-Zugriffslog aktivieren - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + FS-Zugriffslog aktivieren + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Erweitert - + Kiosk (Quest) Mode Kiosk(Quest)-Modus - + Enable CPU Debugging CPU Debugging aktivieren - + Enable Debug Asserts aktiviere Debug-Meldungen - + Enable Auto-Stub** Auto-Stub** aktivieren - + Enable All Controller Types - + Disable Web Applet Deaktiviere die Web Applikation - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. + + + Restart Required + Neustart erforderlich + + + + yuzu is required to restart in order to apply this setting. + yuzu muss neugestartet werden, damit diese Einstellungen übernommen werden können. + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1258,7 +1293,7 @@ This would ban both their forum username and their IP address. Mute audio when in background - + Audio im Hintergrund stummschalten @@ -1301,7 +1336,7 @@ This would ban both their forum username and their IP address. Shader Backend: - + Shader Backend: @@ -1321,7 +1356,7 @@ This would ban both their forum username and their IP address. Use disk pipeline cache - + Disk-Pipeline-Cache verwenden @@ -1540,7 +1575,7 @@ This would ban both their forum username and their IP address. Use VSync - + VSync verwenden @@ -1563,37 +1598,47 @@ This would ban both their forum username and their IP address. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotrope Filterung: - + Automatic Automatisch - + Default Standard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1638,7 +1683,7 @@ This would ban both their forum username and their IP address. Controller Hotkey - + Controller-Hotkey @@ -1988,12 +2033,12 @@ This would ban both their forum username and their IP address. Ring Controller - + Ring-Controller Infrared Camera - + Infrarotkamera @@ -2023,7 +2068,7 @@ This would ban both their forum username and their IP address. Controller navigation - + Controller-Navigation @@ -2085,7 +2130,7 @@ This would ban both their forum username and their IP address. - + Left Stick Linker Analogstick @@ -2179,14 +2224,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2205,7 +2250,7 @@ This would ban both their forum username and their IP address. - + Plus Plus @@ -2218,15 +2263,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2283,231 +2328,236 @@ This would ban both their forum username and their IP address. - + Right Stick Rechter Analogstick - - - - + + + + Clear Löschen - - - - - + + + + + [not set] [nicht belegt] - - - Toggle button - Taste umschalten - - - - + + Invert button Knopf invertieren - - + + + Toggle button + Taste umschalten + + + + Invert axis Achsen umkehren - - - + + + Set threshold - - + + Choose a value between 0% and 100% Wert zwischen 0% und 100% wählen - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Analog-Stick festlegen - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Nach dem Drücken von OK den Joystick zuerst horizontal, dann vertikal bewegen. Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizontal. - + Center axis - + Achse zentrieren - - + + Deadzone: %1% Deadzone: %1% - - + + Modifier Range: %1% Modifikator-Radius: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Zwei Joycons - + Left Joycon Linker Joycon - + Right Joycon Rechter Joycon - + Handheld Handheld - + GameCube Controller GameCube-Controller - + Poke Ball Plus Poke-Ball Plus - + NES Controller NES Controller - + SNES Controller SNES Controller - + N64 Controller N64 Controller - + Sega Genesis Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Analog Stick - + C-Stick C-Stick - + Shake! Schütteln! - + [waiting] [wartet] - + New Profile Neues Profil - + Enter a profile name: Profilnamen eingeben: - - + + Create Input Profile Eingabeprofil erstellen - + The given profile name is not valid! Angegebener Profilname ist nicht gültig! - + Failed to create the input profile "%1" Erstellen des Eingabeprofils "%1" ist fehlgeschlagen - + Delete Input Profile Eingabeprofil löschen - + Failed to delete the input profile "%1" Löschen des Eingabeprofils "%1" ist fehlgeschlagen - + Load Input Profile Eingabeprofil laden - + Failed to load the input profile "%1" Laden des Eingabeprofils "%1" ist fehlgeschlagen - + Save Input Profile Eingabeprofil speichern - + Failed to save the input profile "%1" Speichern des Eingabeprofils "%1" ist fehlgeschlagen @@ -2762,42 +2812,42 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Entwickler - + Add-Ons Add-Ons - + General Allgemeines - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics Erw. Grafik - + Audio Audio - + Properties Einstellungen @@ -2986,7 +3036,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Configure Ring Controller - + Ring-Controller konfigurieren @@ -3002,13 +3052,13 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Pull - + Ziehen Push - + Drücken @@ -3509,47 +3559,47 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings Einstellungen - + Enable TAS features - + TAS-Funktionen aktivieren - + Loop script - + Script loopen - + Pause execution during loads - + Script Directory Skript-Verzeichnis - + Path Pfad - + ... ... @@ -3724,7 +3774,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Full Size (256x256) - + Volle Größe (256x256) @@ -3807,12 +3857,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Game Icon Size: - + Spiel-Icon Größe: Folder Icon Size: - + Ordner-Icon Größe: @@ -3865,7 +3915,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Press any controller button to vibrate the controller. - + Drücke einen Knopf um den Controller vibrieren zu lassen. @@ -3959,7 +4009,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf - + Verify Überprüfen @@ -4046,7 +4096,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf - + Unspecified Nicht spezifiziert @@ -4061,17 +4111,36 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Token wurde nicht verfiziert. Die Änderungen an deinem Token wurden nicht gespeichert. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifizieren... - + + Verified + Tooltip + Verifiziert + + + + Verification failed + Tooltip + Verifizierung fehlgeschlagen + + + Verification failed Verifizierung fehlgeschlagen - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verifizierung fehlgeschlagen. Prüfe ob dein Nutzername und Token richtig eingegeben wurden und ob deine Internetverbindung korrekt funktioniert. @@ -4140,12 +4209,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf DirectConnectWindow - + Connecting - + Verbinde - + Connect Verbinden @@ -4153,487 +4222,487 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonyme Daten werden gesammelt,</a> um yuzu zu verbessern.<br/><br/>Möchstest du deine Nutzungsdaten mit uns teilen? - + Telemetry Telemetrie - + Broken Vulkan Installation Detected - + Defekte Vulkan-Installation erkannt - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Lade Web-Applet... - + Disable Web Applet Deaktiviere die Web Applikation - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Wie viele Shader im Moment kompiliert werden - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Derzeitige Emulations-Geschwindigkeit. Werte höher oder niedriger als 100% zeigen, dass die Emulation scheller oder langsamer läuft als auf einer Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Wie viele Bilder pro Sekunde angezeigt werden variiert von Spiel zu Spiel und von Szene zu Szene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Zeit, die gebraucht wurde, um einen Switch-Frame zu emulieren, ohne Framelimit oder V-Sync. Für eine Emulation bei voller Geschwindigkeit sollte dieser Wert bei höchstens 16.67ms liegen. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Zuletzt geladene Dateien leeren - + &Continue &Fortsetzen - + &Pause &Pause - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu betreibt ein Speil - + Warning Outdated Game Format Warnung veraltetes Spielformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Du nutzt eine entpackte ROM-Ordnerstruktur für dieses Spiel, welches ein veraltetes Format ist und von anderen Formaten wie NCA, NAX, XCI oder NSP überholt wurde. Entpackte ROM-Ordner unterstützen keine Icons, Metadaten oder Updates.<br><br><a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Unser Wiki</a> enthält eine Erklärung der verschiedenen Formate, die yuzu unterstützt. Diese Nachricht wird nicht noch einmal angezeigt. - - + + Error while loading ROM! ROM konnte nicht geladen werden! - + The ROM format is not supported. ROM-Format wird nicht unterstützt. - + An error occurred initializing the video core. Beim Initialisieren des Video-Kerns ist ein Fehler aufgetreten. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM konnte nicht geladen werden! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Bitte folge der <a href='https://yuzu-emu.org/help/quickstart/'>yuzu-Schnellstart-Anleitung</a> um deine Dateien zu extrahieren.<br>Hilfe findest du im yuzu-Wiki</a> oder dem yuzu-Discord</a>. - + An unknown error occurred. Please see the log for more details. Ein unbekannter Fehler ist aufgetreten. Bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen. - + (64-bit) (64-Bit) - + (32-bit) (32-Bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Speicherdaten - + Mod Data Mod-Daten - + Error Opening %1 Folder Konnte Verzeichnis %1 nicht öffnen - - + + Folder does not exist! Verzeichnis existiert nicht! - + Error Opening Transferable Shader Cache Fehler beim Öffnen des transferierbaren Shader-Caches - + Failed to create the shader cache directory for this title. - + Contents Inhalte - + Update Update - + DLC DLC - + Remove Entry Eintrag entfernen - + Remove Installed Game %1? Installiertes Spiel %1 entfernen? - - - - - - + + + + + + Successfully Removed Erfolgreich entfernt - + Successfully removed the installed base game. Das Spiel wurde entfernt. - - - + + + Error Removing %1 Fehler beim Entfernen von %1 - + The base game is not installed in the NAND and cannot be removed. Das Spiel ist nicht im NAND installiert und kann somit nicht entfernt werden. - + Successfully removed the installed update. Das Update wurde entfernt. - + There is no update installed for this title. Es ist kein Update für diesen Titel installiert. - + There are no DLC installed for this title. Es sind keine DLC für diesen Titel installiert. - + Successfully removed %1 installed DLC. %1 DLC entfernt. - + Delete OpenGL Transferable Shader Cache? Transferierbaren OpenGL Shader Cache löschen? - + Delete Vulkan Transferable Shader Cache? Transferierbaren Vulkan Shader Cache löschen? - + Delete All Transferable Shader Caches? Alle transferierbaren Shader Caches löschen? - + Remove Custom Game Configuration? Spiel-Einstellungen entfernen? - + Remove File Datei entfernen - - + + Error Removing Transferable Shader Cache Fehler beim Entfernen - - + + A shader cache for this title does not exist. Es existiert kein Shader-Cache für diesen Titel. - + Successfully removed the transferable shader cache. Der transferierbare Shader-Cache wurde entfernt. - + Failed to remove the transferable shader cache. Konnte den transferierbaren Shader-Cache nicht entfernen. - - + + Error Removing Transferable Shader Caches Fehler beim Entfernen der transferierbaren Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Fehler beim Entfernen - + A custom configuration for this title does not exist. Es existieren keine Spiel-Einstellungen für dieses Spiel. - + Successfully removed the custom game configuration. Die Spiel-Einstellungen wurden entfernt. - + Failed to remove the custom game configuration. Die Spiel-Einstellungen konnten nicht entfernt werden. - - + + RomFS Extraction Failed! RomFS-Extraktion fehlgeschlagen! - + There was an error copying the RomFS files or the user cancelled the operation. Das RomFS konnte wegen eines Fehlers oder Abbruchs nicht kopiert werden. - + Full Komplett - + Skeleton Nur Ordnerstruktur - + Select RomFS Dump Mode RomFS Extraktions-Modus auswählen - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Bitte wähle, wie das RomFS gespeichert werden soll.<br>"Full" wird alle Dateien des Spiels extrahieren, während <br>"Skeleton" nur die Ordnerstruktur erstellt. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... RomFS wird extrahiert... - - + + Cancel Abbrechen - + RomFS Extraction Succeeded! RomFS wurde extrahiert! - + The operation completed successfully. Der Vorgang wurde erfolgreich abgeschlossen. - + Error Opening %1 Fehler beim Öffnen von %1 - + Select Directory Verzeichnis auswählen - + Properties Einstellungen - + The game properties could not be loaded. Spiel-Einstellungen konnten nicht geladen werden. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch-Programme (%1);;Alle Dateien (*.*) - + Load File Datei laden - + Open Extracted ROM Directory Öffne das extrahierte ROM-Verzeichnis - + Invalid Directory Selected Ungültiges Verzeichnis ausgewählt - + The directory you have selected does not contain a 'main' file. Das Verzeichnis, das du ausgewählt hast, enthält keine 'main'-Datei. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installierbares Switch-Programm (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Dateien installieren - + %n file(s) remaining %n Datei verbleibend%n Dateien verbleibend - + Installing file "%1"... Datei "%1" wird installiert... - - + + Install Results NAND-Installation - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Um Konflikte zu vermeiden, raten wir Nutzern davon ab, Spiele im NAND zu installieren. Bitte nutze diese Funktion nur zum Installieren von Updates und DLC. - + %n file(s) were newly installed %n file was newly installed @@ -4641,423 +4710,403 @@ Bitte nutze diese Funktion nur zum Installieren von Updates und DLC. - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemanwendung - + System Archive Systemarchiv - + System Application Update Systemanwendungsupdate - + Firmware Package (Type A) Firmware-Paket (Typ A) - + Firmware Package (Type B) Firmware-Paket (Typ B) - + Game Spiel - + Game Update Spiel-Update - + Game DLC Spiel-DLC - + Delta Title Delta-Titel - + Select NCA Install Type... Wähle den NCA-Installationstyp aus... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Bitte wähle, als was diese NCA installiert werden soll: (In den meisten Fällen sollte die Standardeinstellung 'Spiel' ausreichen.) - + Failed to Install Installation fehlgeschlagen - + The title type you selected for the NCA is invalid. Der Titel-Typ, den du für diese NCA ausgewählt hast, ist ungültig. - + File not found Datei nicht gefunden - + File "%1" not found Datei "%1" nicht gefunden - + OK OK - + Missing yuzu Account Fehlender yuzu-Account - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Um einen Kompatibilitätsbericht abzuschicken, musst du einen yuzu-Account mit yuzu verbinden.<br><br/>Um einen yuzu-Account zu verbinden, prüfe die Einstellungen unter Emulation &gt; Konfiguration &gt; Web. - + Error opening URL Fehler beim Öffnen der URL - + Unable to open the URL "%1". URL "%1" kann nicht geöffnet werden. - + TAS Recording TAS Aufnahme - + Overwrite file of player 1? Datei von Spieler 1 überschreiben? - + Invalid config detected Ungültige Konfiguration erkannt - + Handheld controller can't be used on docked mode. Pro controller will be selected. Handheld-Controller können nicht im Dock verwendet werden. Der Pro-Controller wird verwendet. - - + + Error Fehler - - + + The current game is not looking for amiibos - + Das aktuelle Spiel sucht nicht nach Amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + Das aktuelle Amiibo wurde entfernt - + Amiibo File (%1);; All Files (*.*) Amiibo-Datei (%1);; Alle Dateien (*.*) - + Load Amiibo Amiibo laden - - Error opening Amiibo data file - Fehler beim Öffnen der Amiibo Datei - - - - Unable to open Amiibo file "%1" for reading. - Die Amiibo Datei "%1" konnte nicht zum Lesen geöffnet werden. - - - - Error reading Amiibo data file - Fehler beim Lesen der Amiibo-Daten - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Amiibo-Daten können nicht vollständig gelesen werden. Es wurde erwartet, dass %1 Bytes gelesen werden, es konnten aber nur %2 Bytes gelesen werden. - - - + Error loading Amiibo data Fehler beim Laden der Amiibo-Daten - + Unable to load Amiibo data. Amiibo-Daten konnten nicht geladen werden. - + Capture Screenshot Screenshot aufnehmen - + PNG Image (*.png) PNG Bild (*.png) - + TAS state: Running %1/%2 TAS Zustand: Läuft %1/%2 - + TAS state: Recording %1 TAS Zustand: Aufnahme %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid TAS Zustand: Ungültig - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Skalierung: %1x - + Speed: %1% / %2% Geschwindigkeit: %1% / %2% - + Speed: %1% Geschwindigkeit: %1% - + Game: %1 FPS (Unlocked) Spiel: %1 FPS (Unbegrenzt) - + Game: %1 FPS Spiel: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HOCH - + GPU EXTREME GPU EXTREM - + GPU ERROR GPU FEHLER - + DOCKED - + DOCKED - + HANDHELD - + HANDHELD - + NEAREST NÄCHSTER - - + + BILINEAR BILINEAR - + BICUBIC BIKUBISCH - + GAUSSIAN - + GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Das Spiel, dass du versuchst zu spielen, benötigt bestimmte Dateien von deiner Switch-Konsole.<br/><br/>Um Informationen darüber zu erhalten, wie du diese Dateien von deiner Switch extrahieren kannst, prüfe bitte die folgenden Wiki-Seiten: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>System-Archive und Shared Fonts von einer Switch-Konsole extrahieren</a>.<br/><br/>Willst du zur Spiele-Liste zurückkehren und die Emulation beenden? Das Fortsetzen der Emulation könnte zu Spielfehlern, Abstürzen, beschädigten Speicherdaten und anderen Fehlern führen. - + yuzu was unable to locate a Switch system archive. %1 yuzu konnte ein Switch Systemarchiv nicht finden. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu konnte ein Switch Systemarchiv nicht finden: %1. %2 - + System Archive Not Found Systemarchiv nicht gefunden - + System Archive Missing Systemarchiv fehlt - + yuzu was unable to locate the Switch shared fonts. %1 yuzu konnte die Switch Shared Fonts nicht finden. %1 - + Shared Fonts Not Found Shared Fonts nicht gefunden - + Shared Font Missing Shared Font fehlt - + Fatal Error Schwerwiegender Fehler - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Ein schwerwiegender Fehler ist aufgetreten, bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen. Weitere Informationen: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Wie kann ich eine Log-Datei hochladen</a>.<br/><br/>Willst du zur Spiele-Liste zurückkehren und die Emulation beenden? Das Fortsetzen der Emulation könnte zu Spielfehlern, Abstürzen, beschädigten Speicherdaten und anderen Fehlern führen. - + Fatal Error encountered Fataler Fehler aufgetreten - + Confirm Key Rederivation Schlüsselableitung bestätigen - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5070,37 +5119,37 @@ This will delete your autogenerated key files and re-run the key derivation modu Dieser Prozess wird die generierten Schlüsseldateien löschen und die Schlüsselableitung neu starten. - + Missing fuses Fuses fehlen - + - Missing BOOT0 - BOOT0 fehlt - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main fehlt - + - Missing PRODINFO - PRODINFO fehlt - + Derivation Components Missing Derivationskomponenten fehlen - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5108,39 +5157,39 @@ on your system's performance. Dies könnte, je nach Leistung deines Systems, bis zu einer Minute dauern. - + Deriving Keys Schlüsselableitung - + Select RomFS Dump Target RomFS wählen - + Please select which RomFS you would like to dump. Wähle, welches RomFS du speichern möchtest. - + Are you sure you want to close yuzu? Bist du sicher, dass du yuzu beenden willst? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Bist du sicher, dass du die Emulation stoppen willst? Jeder nicht gespeicherte Fortschritt geht verloren. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5463,7 +5512,7 @@ Screen. Preferred Game - + Bevorzugtes Spiel @@ -5478,7 +5527,7 @@ Screen. (Leave blank for open game) - + (Leer lassen für offenes Spiel) @@ -5498,7 +5547,7 @@ Screen. Load Previous Ban List - + Vorherige Bann-Liste laden @@ -5519,12 +5568,12 @@ Screen. HostRoomWindow - + Error Fehler - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5533,11 +5582,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5559,112 +5609,111 @@ Debug Message: - Main Window Hauptfenster - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Screenshot aufnehmen - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + GPU-Genauigkeit ändern + + + + Continue/Pause Emulation + Emulation fortsetzen/pausieren - Continue/Pause Emulation - - - - Exit Fullscreen Vollbild verlassen - + Exit yuzu yuzu verlassen - + Fullscreen Vollbild - + Load File Datei laden - + Load/Remove Amiibo - + Amiibo laden/entfernen + + + + Restart Emulation + Emulation neustarten - Restart Emulation - + Stop Emulation + Emulation stoppen - Stop Emulation - + TAS Record + TAS aufnehmen - TAS Record - + TAS Reset + TAS neustarten - TAS Reset - + TAS Start/Stop + TAS starten/stoppen - TAS Start/Stop - - - - Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5744,7 +5793,7 @@ Debug Message: Public Room Browser - + Browser für öffentliche Räume @@ -5775,45 +5824,45 @@ Debug Message: Refresh Lobby - + Lobby aktualisieren - + Password Required to Join Passwort zum Joinen benötigt - + Password: Passwort: - + Room Name Raumname - + Preferred Game - + Bevorzugtes Spiel - + Host Host - + Players Spieler - + Refreshing - + Aktualisiere - + Refresh List Liste aktualisieren @@ -5973,7 +6022,7 @@ Debug Message: Browse Public Game Lobby - + Öffentliche Spiele-Lobbys durchsuchen @@ -5988,12 +6037,12 @@ Debug Message: Direct Connect to Room - + Direkte Verbindung zum Raum Show Current Room - + Aktuellen Raum anzeigen @@ -6090,7 +6139,7 @@ Debug Message: Refreshing - + Aktualisiere @@ -6100,7 +6149,7 @@ Debug Message: Subject - + Thema @@ -6110,7 +6159,7 @@ Debug Message: Forum Username - + Forum Benutzername @@ -6135,12 +6184,12 @@ Debug Message: Not Connected. Click here to find a room! - + Nicht verbunden! Hier klicken um Raum zu finden! - + Connected Verbunden @@ -6162,7 +6211,7 @@ Debug Message: - + New Messages Received Neue Nachrichten erhalten @@ -6172,17 +6221,17 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + Benutzername ist ungültig. Muss aus 4 bis 20 alphanumerischen Zeichen bestehen. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Raumname ist ungütig. Muss aus 4 bis 20 alphanumerischen Zeichen bestehen. Username is already in use or not valid. Please choose another. - + Benutzername wird bereits genutzt oder ist ungültig. Bitte wähle einen anderen. @@ -6247,12 +6296,12 @@ Debug Message: You have been kicked by the room host. - + Sie wurden vom Raum-Ersteller gekickt. IP address is already in use. Please choose another. - + IP-Addresse wird bereits genutzt. Bitte wählen Sie eine andere aus. @@ -6266,22 +6315,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + Spiel läuft bereits + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Raum verlassen - + You are about to close the room. Any network connections will be closed. - + Disconnect Verbindung trennen - + You are about to leave the room. Any network connections will be closed. @@ -6289,7 +6355,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Fehler @@ -6348,7 +6414,7 @@ p, li { white-space: pre-wrap; } %1 spielt %2 - + Not playing a game Spielt kein Spiel @@ -6403,7 +6469,7 @@ p, li { white-space: pre-wrap; } - + [not set] [nicht gesetzt] @@ -6418,10 +6484,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Achse %1%2 @@ -6435,9 +6501,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [unbekannt] @@ -6593,7 +6659,7 @@ p, li { white-space: pre-wrap; } [undefined] - + [undefiniert] @@ -6602,15 +6668,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [ungültig] - - + + %1%2Hat %3 @@ -6618,35 +6684,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Achse %3 - + %1%2Axis %3,%4,%5 %1%2Achse %3,%4,%5 - + %1%2Motion %3 %1%2Bewegung %3 - - + + %1%2Button %3 %1%2Knopf %3 - + [unused] [unbenutzt] @@ -6687,7 +6753,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/el.ts b/dist/languages/el.ts index 6369a8f0e1..b24943792e 100644 --- a/dist/languages/el.ts +++ b/dist/languages/el.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -755,200 +755,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub - + Port: Θύρα: - + Logging Καταγραφή Συμβάντων - + Global Log Filter Παγκόσμιο φίλτρο αρχείου καταγραφής - + Show Log in Console - + Open Log Location Άνοιγμα θέσης του αρχείου καταγραφής - + When checked, the max size of the log increases from 100 MB to 1 GB Όταν επιλεγεί, το μέγιστο μέγεθος του αρχείου καταγραφής αυξάνεται από 100 MB σε 1 GB - + Enable Extended Logging** Ενεργοποίηση Εκτεταμένης Καταγραφής** - + Homebrew Σπιτικό - + Arguments String - + Graphics Γραφικά - + When checked, the graphics API enters a slower debugging mode Όταν είναι επιλεγμένο, το API γραφικών εισέρχεται σε μια πιο αργή λειτουργία εντοπισμού σφαλμάτων - + Enable Graphics Debugging Ενεργοποίηση του Εντοπισμού Σφαλμάτων Γραφικών - + When checked, it enables Nsight Aftermath crash dumps Όταν είναι επιλεγμένο, ενεργοποιεί την ένδειξη σφαλμάτων Nsight Aftermath - + Enable Nsight Aftermath Ενεργοποίηση του Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Όταν επιλεγεί, θα απορρίψει όλους τους αρχικούς assembler shaders από την κρυφή μνήμη ή το παιχνίδι σκίασης δίσκου όπως βρέθηκε - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Όταν είναι επιλεγμένο, απενεργοποιεί τη μακροεντολή Just In Time compiler. Η ενεργοποίηση αυτού κάνει τα παιχνίδια να τρέχουν πιο αργά - + Disable Macro JIT Απενεργοποίηση του Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback Ενεργοποίηση Shader Feedback - + When checked, it executes shaders without loop logic changes Όταν είναι επιλεγμένο, εκτελεί shaders χωρίς αλλαγές στη λογική του βρόχου - + Disable Loop safety checks Απενεργοποίηση των ελέγχων ασφαλείας βρόχου - + Debugging Εντοπισμός Σφαλμάτων - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Προχωρημένα - + Kiosk (Quest) Mode - + Enable CPU Debugging Ενεργοποίηση Εντοπισμού Σφαλμάτων CPU - + Enable Debug Asserts Ενεργοποίηση Βεβαιώσεων Εντοπισμού Σφαλμάτων - + Enable Auto-Stub** Ενεργοποίηση Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Αυτό θα μηδενιστεί αυτόματα όταν το yuzu κλείσει. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1567,37 +1602,47 @@ This would ban both their forum username and their IP address. Χρήση Γοργού Ρυθμού GPU (Τέχνασμα) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Ανισοτροπικό Φιλτράρισμα: - + Automatic Αυτόματα - + Default Προεπιλεγμένο - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2089,7 +2134,7 @@ This would ban both their forum username and their IP address. - + Left Stick Αριστερό Stick @@ -2183,14 +2228,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2209,7 +2254,7 @@ This would ban both their forum username and their IP address. - + Plus Συν @@ -2222,15 +2267,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2287,231 +2332,236 @@ This would ban both their forum username and their IP address. - + Right Stick Δεξιός Μοχλός - - - - + + + + Clear Καθαρισμός - - - - - + + + + + [not set] [άδειο] - - - Toggle button - Κουμπί εναλλαγής - - - - + + Invert button Κουμπί αντιστροφής - - + + + Toggle button + Κουμπί εναλλαγής + + + + Invert axis Αντιστροφή άξονα - - - + + + Set threshold Ορισμός ορίου - - + + Choose a value between 0% and 100% Επιλέξτε μια τιμή μεταξύ 0% και 100% - + + Toggle axis + + + + Set gyro threshold Ρύθμιση κατωφλίου γυροσκοπίου - + Map Analog Stick Χαρτογράφηση Αναλογικού Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Αφού πατήσετε OK, μετακινήστε πρώτα το joystick σας οριζόντια και μετά κατακόρυφα. Για να αντιστρέψετε τους άξονες, μετακινήστε πρώτα το joystick κατακόρυφα και μετά οριζόντια. - + Center axis Κεντρικός άξονας - - + + Deadzone: %1% Νεκρή Ζώνη: %1% - - + + Modifier Range: %1% Εύρος Τροποποιητή: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Διπλά Joycons - + Left Joycon Αριστερό Joycon - + Right Joycon Δεξί Joycon - + Handheld Handheld - + GameCube Controller Χειριστήριο GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Χειριστήριο NES - + SNES Controller Χειριστήριο SNES - + N64 Controller Χειριστήριο N64 - + Sega Genesis Sega Genesis - + Start / Pause - + Z Z - + Control Stick - + C-Stick C-Stick - + Shake! - + [waiting] [αναμονή] - + New Profile Νέο Προφίλ - + Enter a profile name: Εισαγάγετε ένα όνομα προφίλ: - - + + Create Input Profile Δημιουργία Προφίλ Χειρισμού - + The given profile name is not valid! Το όνομα του προφίλ δεν είναι έγκυρο! - + Failed to create the input profile "%1" Η δημιουργία του προφίλ χειρισμού "%1" απέτυχε - + Delete Input Profile Διαγραφή Προφίλ Χειρισμού - + Failed to delete the input profile "%1" Η διαγραφή του προφίλ χειρισμού "%1" απέτυχε - + Load Input Profile Φόρτωση Προφίλ Χειρισμού - + Failed to load the input profile "%1" Η φόρτωση του προφίλ χειρισμού "%1" απέτυχε - + Save Input Profile Αποθήκευση Προφίλ Χειρισμού - + Failed to save the input profile "%1" Η αποθήκευση του προφίλ χειρισμού "%1" απέτυχε @@ -2766,42 +2816,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Προγραμματιστής - + Add-Ons Πρόσθετα - + General Γενικά - + System Σύστημα - + CPU CPU - + Graphics Γραφικά - + Adv. Graphics Προχ. Γραφικά - + Audio Ήχος - + Properties Ιδιότητες @@ -3447,7 +3497,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< RNG Seed - + RNG Seed @@ -3513,47 +3563,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Αυτό είναι ένα πειραματικό χαρακτηριστικό.<br/>Δεν θα αναπαράγει τέλεια καρέ με την τρέχουσα, ατελή μέθοδο συγχρονισμού. - + Settings Ρυθμίσεις - + Enable TAS features Ενεργοποίηση λειτουργιών TAS - + Loop script Σενάριο επανάληψης - + Pause execution during loads Παύση εκτέλεσης κατά τη διάρκεια φόρτωσης - + Script Directory Κατάλογος Σεναρίων - + Path Μονοπάτι - + ... ... @@ -3962,7 +4012,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Επαλήθευση @@ -3974,7 +4024,7 @@ Drag points to change position, or double-click table cells to edit values. Token: - + Διαπιστευτήριο: @@ -3984,7 +4034,7 @@ Drag points to change position, or double-click table cells to edit values. What is my token? - + Ποιο είναι το διακριτικό μου; @@ -4009,7 +4059,7 @@ Drag points to change position, or double-click table cells to edit values. Telemetry ID: - + Αναγνωριστικό τηλεμετρίας: @@ -4029,7 +4079,7 @@ Drag points to change position, or double-click table cells to edit values. <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> - + <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Μάθετε περισσότερα</span></a> @@ -4049,7 +4099,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified @@ -4064,17 +4114,36 @@ Drag points to change position, or double-click table cells to edit values. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + + + + Verification failed - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4143,12 +4212,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4156,907 +4225,892 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? - + Telemetry Τηλεμετρία - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... - + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Πόσα καρέ ανά δευτερόλεπτο εμφανίζει το παιχνίδι αυτή τη στιγμή. Αυτό διαφέρει από παιχνίδι σε παιχνίδι και από σκηνή σε σκηνή. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue &Συνέχεια - + &Pause &Παύση - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. - + Μη μεταφρασμένη συμβολοσειρά + +Χρησιμοποιείτε τη μορφή καταλόγου αποδομημένης ROM για αυτό το παιχνίδι, η οποία είναι μια ξεπερασμένη μορφή που έχει αντικατασταθεί από άλλες, όπως NCA, NAX, XCI ή NSP. Οι αποδομημένοι κατάλογοι ROM στερούνται εικονιδίων, μεταδεδομένων και υποστήριξης ενημερώσεων.<br><br> +Για μια εξήγηση των διαφόρων μορφών Switch που υποστηρίζει το yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'> δείτε το wiki μας </a>. Αυτό το μήνυμα δεν θα εμφανιστεί ξανά. - - + + Error while loading ROM! - + Σφάλμα κατά τη φόρτωση της ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + Εμφανίστηκε ένα απροσδιόριστο σφάλμα. Ανατρέξτε στο αρχείο καταγραφής για περισσότερες λεπτομέρειες. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Αποθήκευση δεδομένων - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! Ο φάκελος δεν υπάρχει! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - + Contents Περιεχόμενα - + Update Ενημέρωση - + DLC DLC - + Remove Entry - + Remove Installed Game %1? - - - - - - + + + + + + Successfully Removed - + Successfully removed the installed base game. - - - + + + Error Removing %1 - + The base game is not installed in the NAND and cannot be removed. - + Successfully removed the installed update. - + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File Αφαίρεση Αρχείου - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Επιλογή λειτουργίας απόρριψης RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + Μη αποθηκευμένη μετάφραση. +Παρακαλούμε επιλέξτε τον τρόπο με τον οποίο θα θέλατε να γίνει η απόρριψη της RomFS.<br> +Η επιλογή Πλήρης θα αντιγράψει όλα τα αρχεία στο νέο κατάλογο, ενώ η επιλογή <br> Σκελετός θα δημιουργήσει μόνο τη δομή του καταλόγου. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel Ακύρωση - + RomFS Extraction Succeeded! - + The operation completed successfully. Η επέμβαση ολοκληρώθηκε με επιτυχία. - + Error Opening %1 - + Select Directory Επιλογή καταλόγου - + Properties Ιδιότητες - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File Φόρτωση αρχείου - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results Αποτελέσματα εγκατάστασης - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Εφαρμογή συστήματος - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game Παιχνίδι - + Game Update Ενημέρωση παιχνιδιού - + Game DLC DLC παιχνιδιού - + Delta Title - + Select NCA Install Type... - + Επιλέξτε τον τύπο εγκατάστασης NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found Το αρχείο δεν βρέθηκε - + File "%1" not found Το αρχείο "%1" δεν βρέθηκε - + OK OK - + Missing yuzu Account - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. - + Error opening URL Σφάλμα κατα το άνοιγμα του URL - + Unable to open the URL "%1". Αδυναμία ανοίγματος του URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo Φόρτωση Amiibo - - Error opening Amiibo data file - - - - - Unable to open Amiibo file "%1" for reading. - - - - - Error reading Amiibo data file - - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - - - - + Error loading Amiibo data - + Σφάλμα φόρτωσης δεδομένων Amiibo - + Unable to load Amiibo data. Αδυναμία φόρτωσης Amiibo δεδομένων. - + Capture Screenshot - + Λήψη στιγμιότυπου οθόνης - + PNG Image (*.png) Εικόνα PBG (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Έναρξη - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Κλίμακα: %1x - + Speed: %1% / %2% Ταχύτητα: %1% / %2% - + Speed: %1% Ταχύτητα: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS - + Frame: %1 ms - + Καρέ: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR FSR - - + + NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found - + System Archive Missing - + yuzu was unable to locate the Switch shared fonts. %1 - + Shared Fonts Not Found - + Shared Font Missing - + Fatal Error Σοβαρό Σφάλμα - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + Fatal Error encountered Παρουσιάστηκε Σοβαρό Σφάλμα - + Confirm Key Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5067,76 +5121,76 @@ This will delete your autogenerated key files and re-run the key derivation modu - + Missing fuses - + - Missing BOOT0 - Λείπει το BOOT0 - + - Missing BCPKG2-1-Normal-Main - Λείπει το BCPKG2-1-Normal-Main - + - Missing PRODINFO - Λείπει το PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. - + Deriving Keys - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close yuzu? Είστε σίγουροι ότι θέλετε να κλείσετε το yuzu; - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5380,7 +5434,7 @@ workarounds. Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds. - + <html><head/><body><p>Το παιχνίδι λειτουργεί, αλλά με σημαντικά γραφικά ή ακουστικά σφάλματα. Αδύνατο να προχωρήσει σε συγκεκριμένες περιοχές λόγω σφαλμάτων ακόμα και με μικροδιορθώσεις.</p></body></html> @@ -5512,12 +5566,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5526,11 +5580,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5552,112 +5607,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot - + Λήψη στιγμιότυπου οθόνης - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Πλήρη Οθόνη - + Load File Φόρτωση αρχείου - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5771,42 +5825,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players - + Refreshing - + Refresh List @@ -6133,7 +6187,7 @@ Debug Message: - + Connected Συνδεδεμένο @@ -6155,7 +6209,7 @@ Debug Message: - + New Messages Received @@ -6259,22 +6313,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6282,7 +6353,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6337,7 +6408,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6392,7 +6463,7 @@ p, li { white-space: pre-wrap; } - + [not set] [μη ορισμένο] @@ -6407,12 +6478,12 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 - + Άξονας%1%2 @@ -6424,9 +6495,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [άγνωστο] @@ -6591,15 +6662,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6607,35 +6678,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [άδειο] @@ -6676,7 +6747,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 %1%2%3 @@ -7042,7 +7113,7 @@ p, li { white-space: pre-wrap; } Call stack - + Κλήση stack @@ -7068,7 +7139,7 @@ p, li { white-space: pre-wrap; } waiting for all objects - + αναμονή για όλα τα αντικείμενα @@ -7114,7 +7185,7 @@ p, li { white-space: pre-wrap; } waiting for objects - + αναμονή αντικειμένων @@ -7189,7 +7260,7 @@ p, li { white-space: pre-wrap; } priority = %1(current) / %2(normal) - + προτεραιότητα = %1(τρέχον) / %2(κανονικό) diff --git a/dist/languages/es.ts b/dist/languages/es.ts index 3b81336ebe..e84442fdc9 100644 --- a/dist/languages/es.ts +++ b/dist/languages/es.ts @@ -95,82 +95,82 @@ p, li { white-space: pre-wrap; } Enviar mensaje - + Members Miembros - + %1 has joined %1 se ha unido - + %1 has left %1 se ha ido - + %1 has been kicked %1 ha sido expulsado - + %1 has been banned - %1 ha sido baneado + %1 ha sido vetado - + %1 has been unbanned - %1 ha sido desbaneado + %1 se le ha retirado el veto - + View Profile Ver perfil - - + + Block Player Bloquear jugador - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Cuando bloqueas a un jugador, ya no recibirás mensajes de ellos. <br><br> ¿Estás seguro de que quieres bloquear a %1? - + Kick Expulsar - + Ban - Banear + Vetar - + Kick Player Expulsar jugador - + Are you sure you would like to <b>kick</b> %1? ¿Estás seguro de que quieres <b>expulsar</b> a %1? - + Ban Player - Banear jugador + Vetar jugador - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - ¿Estas seguro de que quieres <b>expulsar y banear a</b>%1? + ¿Estas seguro de que quieres <b>expulsar y vetar a</b>%1? Esto banearía su nombre del foro y su dirección IP. @@ -771,200 +771,235 @@ Esto banearía su nombre del foro y su dirección IP. ConfigureDebug - + Debugger Depurador - + Enable GDB Stub Activar GDB Stub - + Port: Puerto: - + Logging Registro - + Global Log Filter Filtro del registro global - + Show Log in Console Ver registro en consola - + Open Log Location Abrir ubicación del archivo de registro - + When checked, the max size of the log increases from 100 MB to 1 GB Cuando se marque, el tamaño maximo del registro aumenta de 100 MB a 1 GB - + Enable Extended Logging** Habilitar registro extendido** - + Homebrew Homebrew - + Arguments String Cadena de argumentos - + Graphics Gráficos - + When checked, the graphics API enters a slower debugging mode Cuando esté marcado, la API gráfica entrará en un modo de depuración más lento. - + Enable Graphics Debugging Activar depuración de gráficos - + When checked, it enables Nsight Aftermath crash dumps Cuando esté marcado, activará los volcados de los fallos Nsight Aftermath - + Enable Nsight Aftermath Activar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Al activarlo, esto volcará todos los sombreadores originales del ensamblador de la caché de sombreadores en disco o del juego encontrado - + Dump Game Shaders Volcar sombreadores del juego - + When checked, it will dump all the macro programs of the GPU Cuando esté activado, se volcarán todos los programas macro de la GPU - + Dump Maxwell Macros Volcar Macros Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Cuando esté marcado, se desactiva el compilador de macro Just In Time. Activar esto hace que los juegos se ejecuten más lento. - + Disable Macro JIT Desactivar macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Cuando esté marcado, yuzu hará un registro de las estadísticas del caché de tubería compilado - + Enable Shader Feedback Activar información de shaders - + When checked, it executes shaders without loop logic changes Cuando esté marcado, se ejecutarán los shaders sin cambios de bucles lógicos. - + Disable Loop safety checks Desactivar comprobaciones de seguridad de bucles - + Debugging Depuración - - Enable FS Access Log - Activar registro de acceso FS - - - - Dump Audio Commands To Console** - Volcar comandos de audio a la consola** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - Activa esta opción para mostrar en la consola la última lista de comandos de audio generada. Solo afecta a los juegos que utilizan el renderizador de audio. - - - + Enable Verbose Reporting Services** Activar servicios de reporte detallados** - + + Enable FS Access Log + Activar registro de acceso FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + Activa esta opción para mostrar en la consola la última lista de comandos de audio generada. Solo afecta a los juegos que utilizan el renderizador de audio. + + + + Dump Audio Commands To Console** + Volcar comandos de audio a la consola** + + + + Create Minidump After Crash + + + + Advanced Avanzado - + Kiosk (Quest) Mode Modo quiosco (Quest) - + Enable CPU Debugging Activar depuración de la CPU - + Enable Debug Asserts Activar alertas de depuración - + Enable Auto-Stub** Activar Auto-Stub** - + Enable All Controller Types Habilitar todo tipo de controles - + Disable Web Applet Desactivar Web applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Esto se reiniciará automáticamente cuando yuzu se cierre. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1583,37 +1618,47 @@ Esto banearía su nombre del foro y su dirección IP. Usar tiempo rápido en la GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtrado anisotrópico: - + Automatic Automático - + Default Valor predeterminado - + 2x x2 - + 4x x4 - + 8x x8 - + 16x x16 @@ -2105,7 +2150,7 @@ Esto banearía su nombre del foro y su dirección IP. - + Left Stick Palanca izquierda @@ -2199,14 +2244,14 @@ Esto banearía su nombre del foro y su dirección IP. - + L L - + ZL ZL @@ -2225,7 +2270,7 @@ Esto banearía su nombre del foro y su dirección IP. - + Plus Más @@ -2238,15 +2283,15 @@ Esto banearía su nombre del foro y su dirección IP. - + R R - + ZR ZR @@ -2303,231 +2348,236 @@ Esto banearía su nombre del foro y su dirección IP. - + Right Stick Palanca derecha - - - - + + + + Clear Borrar - - - - - + + + + + [not set] [no definido] - - - Toggle button - Alternar botón - - - - + + Invert button Invertir botón - - + + + Toggle button + Alternar botón + + + + Invert axis Invertir ejes - - - + + + Set threshold Configurar umbral - - + + Choose a value between 0% and 100% Seleccione un valor entre 0% y 100%. - + + Toggle axis + + + + Set gyro threshold Configurar umbral del Giroscopio - + Map Analog Stick Configuración de palanca analógico - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Después de pulsar OK, mueve primero el joystick de manera horizontal, y luego verticalmente. Para invertir los ejes, mueve primero el joystick de manera vertical, y luego horizontalmente. - + Center axis Centrar ejes - - + + Deadzone: %1% Punto muerto: %1% - - + + Modifier Range: %1% Rango del modificador: %1% - - + + Pro Controller Controlador Pro - + Dual Joycons Joycons duales - + Left Joycon Joycon izquierdo - + Right Joycon Joycon derecho - + Handheld Portátil - + GameCube Controller Controlador de GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Controlador NES - + SNES Controller Controlador SNES - + N64 Controller Controlador N64 - + Sega Genesis Sega Genesis - + Start / Pause Inicio / Pausa - + Z Z - + Control Stick Palanca de control - + C-Stick C-Stick - + Shake! ¡Agita! - + [waiting] [esperando] - + New Profile Nuevo perfil - + Enter a profile name: Introduce un nombre de perfil: - - + + Create Input Profile Crear perfil de entrada - + The given profile name is not valid! ¡El nombre de perfil introducido no es válido! - + Failed to create the input profile "%1" Error al crear el perfil de entrada "%1" - + Delete Input Profile Eliminar perfil de entrada - + Failed to delete the input profile "%1" Error al eliminar el perfil de entrada "%1" - + Load Input Profile Cargar perfil de entrada - + Failed to load the input profile "%1" Error al cargar el perfil de entrada "%1" - + Save Input Profile Guardar perfil de entrada - + Failed to save the input profile "%1" Error al guardar el perfil de entrada "%1" @@ -2782,42 +2832,42 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho Desarrollador - + Add-Ons Extras / Add-Ons - + General General - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos avanz. - + Audio Audio - + Properties Propiedades @@ -3529,47 +3579,47 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho <html><head/><body><p>Lee la entrada de los controles de los scripts en el mismo formato que los scripts TAS-nx.<br/>Para una mejor explicación, consulta la<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">página de ayuda</span></a> en la página web de yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Para comprobar qué teclas de acceso rápido controlan la reproducción/grabación, por favor, revisa la configuración de las Teclas de acceso rápido (Configuración -> General -> Teclas de acceso rápido) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. AVISO: Esto es una característica experimental.<br/>No se reproducirán los scripts perfectamente con el método actual e imperfecto de sincronización. - + Settings Ajustes - + Enable TAS features Activar características TAS - + Loop script Repetir script en bucle - + Pause execution during loads Pausar ejecución durante las cargas - + Script Directory Directorio de scripts - + Path Ruta - + ... ... @@ -3979,7 +4029,7 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de - + Verify Verificar @@ -4066,7 +4116,7 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de - + Unspecified Sin especificar @@ -4081,17 +4131,36 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de El token no se puede verificar. Los cambios realizados en tu token no se ha guardado. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verificando... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Error de verificación + + + Verification failed Error de verificación - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Error de verificación. Comprueba que has introducido el token correctamente, y que esté funcionando correctamente tu conexión a internet. @@ -4160,12 +4229,12 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de DirectConnectWindow - + Connecting Conectando - + Connect Conectar @@ -4173,488 +4242,488 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Los datos de uso anónimos se recogen</a> para ayudar a mejorar yuzu. <br/><br/>¿Deseas compartir tus datos de uso con nosotros? - + Telemetry Telemetría - + Broken Vulkan Installation Detected Se ha detectado una instalación corrupta de Vulkan - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. La inicialización de Vulkan ha fallado durante la ejecución. Haz clic <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>aquí para más información sobre como arreglar el problema</a>. - + Loading Web Applet... Cargando Web applet... - + Disable Web Applet Desactivar Web applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Deshabilitar el Applet Web puede causar comportamientos imprevistos y debería solo ser usado con Super Mario 3D All-Stars. ¿Estas seguro que quieres deshabilitar el Applet Web? (Puede ser reactivado en las configuraciones de Depuración.) - + The amount of shaders currently being built La cantidad de shaders que se están construyendo actualmente - + The current selected resolution scaling multiplier. El multiplicador de escala de resolución seleccionado actualmente. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. La velocidad de emulación actual. Los valores superiores o inferiores al 100% indican que la emulación se está ejecutando más rápido o más lento que en una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. La cantidad de fotogramas por segundo que se está mostrando el juego actualmente. Esto variará de un juego a otro y de una escena a otra. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tiempo que lleva emular un fotograma de la Switch, sin tener en cuenta la limitación de fotogramas o sincronización vertical. Para una emulación óptima, este valor debería ser como máximo de 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Eliminar archivos recientes - + &Continue &Continuar - + &Pause &Pausar - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu está ejecutando un juego - + Warning Outdated Game Format Advertencia: formato del juego obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Está utilizando el formato de directorio de ROM deconstruido para este juego, que es un formato desactualizado que ha sido reemplazado por otros, como los NCA, NAX, XCI o NSP. Los directorios de ROM deconstruidos carecen de íconos, metadatos y soporte de actualizaciones.<br><br>Para ver una explicación de los diversos formatos de Switch que soporta yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>echa un vistazo a nuestra wiki</a>. Este mensaje no se volverá a mostrar. - - + + Error while loading ROM! ¡Error al cargar la ROM! - + The ROM format is not supported. El formato de la ROM no es compatible. - + An error occurred initializing the video core. Se ha producido un error al inicializar el núcleo de video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu ha encontrado un error al ejecutar el núcleo de video. Esto suele ocurrir al no tener los controladores de la GPU actualizados, incluyendo los integrados. Por favor, revisa el registro para más detalles. Para más información sobre cómo acceder al registro, por favor, consulta la siguiente página: <a href='https://yuzu-emu.org/help/reference/log-files/'>Como cargar el archivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ¡Error al cargar la ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Por favor, sigue <a href='https://yuzu-emu.org/help/quickstart/'>la guía de inicio rápido de yuzu</a> para revolcar los archivos.<br>Puedes consultar la wiki de yuzu</a> o el Discord de yuzu</a> para obtener ayuda. - + An unknown error occurred. Please see the log for more details. Error desconocido. Por favor, consulte el archivo de registro para ver más detalles. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Datos de guardado - + Mod Data Datos de mods - + Error Opening %1 Folder Error al abrir la carpeta %1 - - + + Folder does not exist! ¡La carpeta no existe! - + Error Opening Transferable Shader Cache Error al abrir el caché transferible de shaders - + Failed to create the shader cache directory for this title. No se pudo crear el directorio de la caché de los shaders para este título. - + Contents Contenidos - + Update Actualización - + DLC DLC - + Remove Entry Eliminar entrada - + Remove Installed Game %1? ¿Eliminar el juego instalado %1? - - - - - - + + + + + + Successfully Removed Se ha eliminado con éxito - + Successfully removed the installed base game. Se ha eliminado con éxito el juego base instalado. - - - + + + Error Removing %1 Error al eliminar %1 - + The base game is not installed in the NAND and cannot be removed. El juego base no está instalado en el NAND y no se puede eliminar. - + Successfully removed the installed update. Se ha eliminado con éxito la actualización instalada. - + There is no update installed for this title. No hay ninguna actualización instalada para este título. - + There are no DLC installed for this title. No hay ningún DLC instalado para este título. - + Successfully removed %1 installed DLC. Se ha eliminado con éxito %1 DLC instalado(s). - + Delete OpenGL Transferable Shader Cache? ¿Deseas eliminar el caché transferible de shaders de OpenGL? - + Delete Vulkan Transferable Shader Cache? ¿Deseas eliminar el caché transferible de shaders de Vulkan? - + Delete All Transferable Shader Caches? ¿Deseas eliminar todo el caché transferible de shaders? - + Remove Custom Game Configuration? ¿Deseas eliminar la configuración personalizada del juego? - + Remove File Eliminar archivo - - + + Error Removing Transferable Shader Cache Error al eliminar la caché de shaders transferibles - - + + A shader cache for this title does not exist. No existe caché de shaders para este título. - + Successfully removed the transferable shader cache. El caché de shaders transferibles se ha eliminado con éxito. - + Failed to remove the transferable shader cache. No se ha podido eliminar la caché de shaders transferibles. - - + + Error Removing Transferable Shader Caches Error al eliminar las cachés de shaders transferibles - + Successfully removed the transferable shader caches. Cachés de shaders transferibles eliminadas con éxito. - + Failed to remove the transferable shader cache directory. No se ha podido eliminar el directorio de cachés de shaders transferibles. - - + + Error Removing Custom Configuration Error al eliminar la configuración personalizada del juego - + A custom configuration for this title does not exist. No existe una configuración personalizada para este título. - + Successfully removed the custom game configuration. Se eliminó con éxito la configuración personalizada del juego. - + Failed to remove the custom game configuration. No se ha podido eliminar la configuración personalizada del juego. - - + + RomFS Extraction Failed! ¡La extracción de RomFS ha fallado! - + There was an error copying the RomFS files or the user cancelled the operation. Se ha producido un error al copiar los archivos RomFS o el usuario ha cancelado la operación. - + Full Completo - + Skeleton Esquema - + Select RomFS Dump Mode Elegir método de volcado de RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Seleccione la forma en que quieras volcar el RomFS. <br>Copiará todos los archivos en el nuevo directorio <br> mientras que el esqueleto solo creará la estructura del directorio. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root No hay suficiente espacio en %1 para extraer el RomFS. Por favor, libera espacio o elige otro directorio de volcado en Emulación > Configuración > Sistema > Sistema de archivos > Raíz de volcado - + Extracting RomFS... Extrayendo RomFS... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! ¡La extracción RomFS ha tenido éxito! - + The operation completed successfully. La operación se completó con éxito. - + Error Opening %1 Error al intentar abrir %1 - + Select Directory Seleccionar directorio - + Properties Propiedades - + The game properties could not be loaded. No se pueden cargar las propiedades del juego. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Ejecutable de Switch (%1);;Todos los archivos (*.*) - + Load File Cargar archivo - + Open Extracted ROM Directory Abrir el directorio de la ROM extraída - + Invalid Directory Selected Directorio seleccionado no válido - + The directory you have selected does not contain a 'main' file. El directorio que ha seleccionado no contiene ningún archivo 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Archivo de Switch Instalable (*.nca *.nsp *.xci);;Archivo de contenidos de Nintendo (*.nca);;Paquete de envío de Nintendo (*.nsp);;Imagen de cartucho NX (*.xci) - + Install Files Instalar archivos - + %n file(s) remaining %n archivo(s) restantes%n archivo(s) restantes%n archivo(s) restantes - + Installing file "%1"... Instalando el archivo "%1"... - - + + Install Results Instalar resultados - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar posibles conflictos, no recomendamos a los usuarios que instalen juegos base en el NAND. Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + %n file(s) were newly installed %n archivo(s) recién instalado/s @@ -4663,7 +4732,7 @@ Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + %n file(s) were overwritten %n archivo(s) recién sobreescrito/s @@ -4672,7 +4741,7 @@ Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + %n file(s) failed to install %n archivo(s) no se instaló/instalaron @@ -4681,411 +4750,391 @@ Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + System Application Aplicación del sistema - + System Archive Archivo del sistema - + System Application Update Actualización de la aplicación del sistema - + Firmware Package (Type A) Paquete de firmware (Tipo A) - + Firmware Package (Type B) Paquete de firmware (Tipo B) - + Game Juego - + Game Update Actualización de juego - + Game DLC DLC del juego - + Delta Title Titulo delta - + Select NCA Install Type... Seleccione el tipo de instalación NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleccione el tipo de título en el que deseas instalar este NCA como: (En la mayoría de los casos, el 'Juego' predeterminado está bien). - + Failed to Install Fallo en la instalación - + The title type you selected for the NCA is invalid. El tipo de título que seleccionó para el NCA no es válido. - + File not found Archivo no encontrado - + File "%1" not found Archivo "%1" no encontrado - + OK Aceptar - + Missing yuzu Account Falta la cuenta de Yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Para enviar un caso de prueba de compatibilidad de juegos, debes vincular tu cuenta de yuzu.<br><br/> Para vincular tu cuenta de yuzu, ve a Emulación &gt; Configuración &gt; Web. - + Error opening URL Error al abrir la URL - + Unable to open the URL "%1". No se puede abrir la URL "%1". - + TAS Recording Grabación TAS - + Overwrite file of player 1? ¿Sobrescribir archivo del jugador 1? - + Invalid config detected Configuración no válida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. El controlador del modo portátil no puede ser usado en el modo sobremesa. Se seleccionará el controlador Pro en su lugar. - - + + Error Error - - + + The current game is not looking for amiibos El juego actual no está buscando amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed El amiibo actual ha sido eliminado - + Amiibo File (%1);; All Files (*.*) Archivo amiibo (%1);; Todos los archivos (*.*) - + Load Amiibo Cargar amiibo - - Error opening Amiibo data file - Error al abrir el archivo de datos amiibo - - - - Unable to open Amiibo file "%1" for reading. - No se puede abrir el archivo amiibo "%1" para leer. - - - - Error reading Amiibo data file - Error al leer el archivo de datos amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - No se puede leer completamente los datos Amiibo. Se esperaban leer %1 bytes, pero solo se puede leer %2 bytes. - - - + Error loading Amiibo data Error al cargar los datos Amiibo - + Unable to load Amiibo data. No se pueden cargar los datos Amiibo. - + Capture Screenshot Captura de pantalla - + PNG Image (*.png) Imagen PNG (*.png) - + TAS state: Running %1/%2 Estado TAS: ejecutando %1/%2 - + TAS state: Recording %1 Estado TAS: grabando %1 - + TAS state: Idle %1/%2 Estado TAS: inactivo %1/%2 - + TAS State: Invalid Estado TAS: nulo - + &Stop Running &Parar de ejecutar - + &Start &Iniciar - + Stop R&ecording Pausar g&rabación - + R&ecord G&rabar - + Building: %n shader(s) Creando: %n shader(s)Construyendo: %n shader(s)Construyendo: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escalado: %1x - + Speed: %1% / %2% Velocidad: %1% / %2% - + Speed: %1% Velocidad: %1% - + Game: %1 FPS (Unlocked) Juego: %1 FPS (desbloqueado) - + Game: %1 FPS Juego: %1 FPS - + Frame: %1 ms Fotogramas: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR GPU ERROR - + DOCKED SOBREMESA - + HANDHELD PORTÁTIL - + NEAREST PROXIMAL - - + + BILINEAR BILINEAL - + BICUBIC BICÚBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. El juego que estás intentando cargar requiere archivos adicionales de tu Switch antes de poder jugar. <br/><br/>Para obtener más información sobre cómo obtener estos archivos, ve a la siguiente página de la wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Volcar archivos del sistema y las fuentes compartidas desde una Consola Switch. </a>.<br/><br/>¿Quieres volver a la lista de juegos? Continuar con la emulación puede provocar fallos, datos de guardado corrompidos u otros errores. - + yuzu was unable to locate a Switch system archive. %1 yuzu no pudo localizar el archivo de sistema de la Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu no pudo localizar un archivo de sistema de la Switch: %1. %2 - + System Archive Not Found Archivo del sistema no encontrado - + System Archive Missing Faltan archivos del sistema - + yuzu was unable to locate the Switch shared fonts. %1 yuzu no pudo encontrar las fuentes compartidas de la Switch. %1 - + Shared Fonts Not Found Fuentes compartidas no encontradas - + Shared Font Missing Faltan fuentes compartidas - + Fatal Error Error fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu ha encontrado un error fatal, consulta el registro para obtener más detalles. Para obtener más información sobre cómo acceder al registro, consulta la siguiente página: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>¿Cómo cargar el archivo de registro?</a>.<br/><br/> Continuar con la emulación puede provocar fallos, datos de guardado corruptos u otros errores. - + Fatal Error encountered Error fatal encontrado - + Confirm Key Rederivation Confirmar la clave de rederivación - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5102,37 +5151,37 @@ es lo que quieres hacer si es necesario. Esto eliminará los archivos de las claves generadas automáticamente y volverá a ejecutar el módulo de derivación de claves. - + Missing fuses Faltan fuses - + - Missing BOOT0 - Falta BOOT0 - + - Missing BCPKG2-1-Normal-Main - Falta BCPKG2-1-Normal-Main - + - Missing PRODINFO - Falta PRODINFO - + Derivation Components Missing Faltan componentes de derivación - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Faltan las claves de encriptación. <br>Por favor, sigue <a href='https://yuzu-emu.org/help/quickstart/'>la guía rápida de yuzu</a> para obtener todas tus claves, firmware y juegos.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5141,39 +5190,39 @@ Esto puede llevar unos minutos dependiendo del rendimiento de su sistema. - + Deriving Keys Obtención de claves - + Select RomFS Dump Target Selecciona el destinatario para volcar el RomFS - + Please select which RomFS you would like to dump. Por favor, seleccione los RomFS que deseas volcar. - + Are you sure you want to close yuzu? ¿Estás seguro de que quieres cerrar yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. ¿Estás seguro de que quieres detener la emulación? Cualquier progreso no guardado se perderá. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5549,32 +5598,33 @@ de inicio. Host Room - Sala del anfitrión + Crear sala HostRoomWindow - + Error Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - Error al anunciar la sala al lobby público. Para poder publicar una sala en el lobby público, debe tener una cuenta válida de yuzu configurada en Emulación -> Configurar -> Web. Si no quieres publicar una sala en el lobby público, seleccione en su lugar Privada. + Error al anunciar la sala al lobby público. Para poder publicar una sala en el lobby público, debes tener una cuenta válida de yuzu configurada en Emulación -> Configurar -> Web. Si no quieres publicar una sala en el lobby público, seleccione en su lugar Privada. Mensaje de depuración: Hotkeys - + Audio Mute/Unmute Activar/Desactivar audio + @@ -5596,112 +5646,111 @@ Mensaje de depuración: - Main Window Ventana principal - + Audio Volume Down Bajar volumen del audio - + Audio Volume Up Subir volumen del audio - + Capture Screenshot Captura de pantalla - + Change Adapting Filter Cambiar filtro adaptable - + Change Docked Mode Cambiar a modo sobremesa - + Change GPU Accuracy Cambiar precisión de GPU - + Continue/Pause Emulation Continuar/Pausar emulación - + Exit Fullscreen Salir de pantalla completa - + Exit yuzu Cerrar yuzu - + Fullscreen Pantalla completa - + Load File Cargar archivo - + Load/Remove Amiibo Cargar/Eliminar Amiibo - + Restart Emulation Reiniciar emulación - + Stop Emulation Detener emulación - + TAS Record Grabar TAS - + TAS Reset Reiniciar TAS - + TAS Start/Stop Iniciar/detener TAS - + Toggle Filter Bar Alternar barra de filtro - + Toggle Framerate Limit Alternar limite de fotogramas - + Toggle Mouse Panning Alternar desplazamiento del ratón - + Toggle Status Bar Alternar barra de estado @@ -5816,42 +5865,42 @@ Mensaje de depuración: Actualizar lobby - + Password Required to Join Contraseña necesaria para unirse - + Password: Contraseña: - + Room Name Nombre de la sala - + Preferred Game Juego preferente - + Host Anfitrión - + Players Jugadores - + Refreshing Actualizando - + Refresh List Actualizar lista @@ -6133,7 +6182,7 @@ Mensaje de depuración: Unban - Desbanear + Quitar veto @@ -6178,7 +6227,7 @@ Mensaje de depuración: - + Connected Conectado @@ -6201,7 +6250,7 @@ Debug Message: Mensaje de depuración: - + New Messages Received Nuevos mensajes recibidos @@ -6246,42 +6295,42 @@ Mensaje de depuración: Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + No se ha podido conectar con el anfitrión. Comprueba que la configuración de la conexión es correcta. Si todavía no puedes conectarte, contacta con el anfitrión de la sala y verifica que el anfitrión tiene configurado correctamente el puerto externo direccionado. Unable to connect to the room because it is already full. - + No es posible conectarse a la sala debido a que ya se encuentra llena. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + La creación de la sala ha fallado. Por favor reintente. Reiniciar yuzu puede ser necesario. The host of the room has banned you. Speak with the host to unban you or try a different room. - + El anfitrión de la sala te ha vetado. Habla con el anfitrión para quitar el veto o prueba con una sala diferente. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + ¡No coinciden las versiones! Por favor, actualiza a la última versión de yuzu. Si el problema persiste, ponte en contacto con el anfitrión de la sala y pídele que actualice el servidor. Incorrect password. - + Contraseña incorrecta An unknown error occurred. If this error continues to occur, please open an issue - + Ha ocurrido un error desconocido. Si el error persiste, por favor, abre una solicitud de errores. Connection to room lost. Try to reconnect. - + Conexión a la sala perdida. Intente reconectarse. @@ -6291,44 +6340,62 @@ Mensaje de depuración: IP address is already in use. Please choose another. - + La dirección IP ya se encuentra en uso. Por favor escoja otra. You do not have enough permission to perform this action. - + No tiene permisos suficientes para realizar esta acción. The user you are trying to kick/ban could not be found. They may have left the room. + El usuario que estás intentando echar/vetar no se ha podido encontrar. +Es posible que haya abandonado la sala. + + + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Salir de la sala - + You are about to close the room. Any network connections will be closed. - + Estás por cerrar la sala. Las conexiones de red serán cerradas. - + Disconnect - + Desconectar - + You are about to leave the room. Any network connections will be closed. - + Estás por salir de la sala. Las conexiones de red serán cerradas. NetworkMessage::ErrorManager - + Error Error @@ -6379,17 +6446,17 @@ p, li { white-space: pre-wrap; } %1 is not playing a game - + %1 no está jugando un juego %1 is playing %2 - + %1 esta jugando %2 - + Not playing a game - + No está jugando un juego @@ -6442,7 +6509,7 @@ p, li { white-space: pre-wrap; } - + [not set] [no definido] @@ -6457,10 +6524,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eje %1%2 @@ -6474,9 +6541,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [desconocido] @@ -6641,15 +6708,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [inválido] - - + + %1%2Hat %3 %1%2Rotación %3 @@ -6657,35 +6724,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eje %3 - + %1%2Axis %3,%4,%5 %1%2Eje %3,%4,%5 - + %1%2Motion %3 %1%2Movimiento %3 - - + + %1%2Button %3 %1%2Botón %3 - + [unused] [no usado] @@ -6726,7 +6793,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/fr.ts b/dist/languages/fr.ts index d876accfe4..35e0292a91 100644 --- a/dist/languages/fr.ts +++ b/dist/languages/fr.ts @@ -25,12 +25,18 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu est un émulateur expérimental open-source pour la Nintendo Switch sous licence GPLv3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement.</span></p></body></html> <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Site Web</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Code Source </span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributeurs</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> @@ -76,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Fenêtre du Salon Send Chat Message - + Envoyer un message de chat Send Message - + Envoyer le message - + Members Membres - + %1 has joined %1 a rejoint - + %1 has left %1 a quitté - + %1 has been kicked - + %1 a été expulsé - + %1 has been banned - + %1 a été banni - + %1 has been unbanned - - - - - View Profile - + %1 a été débanni - - Block Player - + View Profile + Voir le profile - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + + + Block Player + Bloquer le joueur + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Lorsque vous bloquez un joueur, vous ne recevrez plus de messages de chat de sa part.<br><br>Êtes-vous sûr de vouloir bloquer %1 ? + + + Kick - - - - - Ban - - - - - Kick Player - - - - - Are you sure you would like to <b>kick</b> %1? - + Expulser - Ban Player - + Ban + Bannir - + + Kick Player + Expulser le joueur + + + + Are you sure you would like to <b>kick</b> %1? + Êtes-vous sûr de vouloir <b>expluser</b> %1 ? + + + + Ban Player + Bannir le joueur + + + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Êtes-vous sûr de vouloir <b>expluser et bannir </b> %1 ? + +Cela bannirait à la fois son nom d'utilisateur du forum et son adresse IP. @@ -172,7 +180,7 @@ This would ban both their forum username and their IP address. Room Window - + Fenêtre du Salon @@ -182,7 +190,7 @@ This would ban both their forum username and their IP address. Moderation... - + Modération... @@ -200,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + Déconnecté %1 (%2/%3 members) - connected - + %1 (%2/%3 membres) - connectés @@ -334,7 +342,7 @@ This would ban both their forum username and their IP address. Output Device - + Périphérique de sortie @@ -373,37 +381,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Configurer la caméra infrarouge Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Sélectionnez d'où provient l'image de la caméra émulée. Il peut s'agir d'une caméra virtuelle ou d'une caméra réelle. Camera Image Source: - + Source de l'image de la caméra : Input device: - + Périphérique d'entrée : Preview - + Aperçu Resolution: 320*240 - + Résolution : 320*240 Click to preview - + Cliquez pour prévisualiser @@ -726,7 +734,11 @@ Cette option améliore la vitesse en réduisant la précision des instructions f <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> - + + <div style="white-space: nowrap">Cette optimisation accélère les accès exclusifs à la mémoire par le programme invité.</div> + <div style="white-space: nowrap">Son activation entraîne l'exécution directe de lectures/écritures de la mémoire exclusive de l'invité dans la mémoire et l'utilisation du MMU de l'hôte.</div> + <div style="white-space: nowrap">La désactivation de cette option force tous les accès exclusifs à la mémoire à utiliser l'émulation logicielle MMU.</div> + @@ -739,12 +751,15 @@ Cette option améliore la vitesse en réduisant la précision des instructions f <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> - + + <div style="white-space: nowrap">Cette optimisation accélère les accès exclusifs à la mémoire par le programme invité.</div> + <div style="white-space: nowrap">L'activer réduit la surcharge de l'échec fastmem des accès exclusifs à la mémoire.</div> + Enable recompilation of exclusive memory instructions - + Activer la recompilation des instructions de mémoire exclusives @@ -755,200 +770,235 @@ Cette option améliore la vitesse en réduisant la précision des instructions f ConfigureDebug - + Debugger - + Débogueur - + Enable GDB Stub Activer le "stub" de GDB - + Port: Port : - + Logging S'enregistrer - + Global Log Filter Filtre de log global - + Show Log in Console Afficher le relevé d'événements dans la console - + Open Log Location Ouvrir l'emplacement du journal de logs - + When checked, the max size of the log increases from 100 MB to 1 GB Lorsque coché, la taille maximum du relevé d'événements augmente de 100 Mo à 1 Go - + Enable Extended Logging** Activer la Journalisation Étendue** - + Homebrew Homebrew - + Arguments String Chaîne d'arguments - + Graphics Graphismes - + When checked, the graphics API enters a slower debugging mode Lorsque coché, l'API graphique entre dans un mode de débogage plus lent - + Enable Graphics Debugging Activer le débogage des graphismes - + When checked, it enables Nsight Aftermath crash dumps Une fois cochée, cette option active les "crash dumps" pour Nsight Aftermath - + Enable Nsight Aftermath Activer Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Lorsqu'il est coché, il videra tous les shaders d'assemblage d'origine du cache de shader de disque ou du jeu tels qu'ils ont été trouvés - + Dump Game Shaders Récupérer Shaders Jeu - + When checked, it will dump all the macro programs of the GPU - + Lorsqu'il est coché, il videra tous les programmes de macro du GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Lorsque coché, désactive le compilateur de macros JIT. L'activer ralentit les jeux - + Disable Macro JIT Désactiver les macros JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Lorsque la case est cochée, yuzu enregistrera les journaux de statistiques à propos de la cache de pipeline compilée - + Enable Shader Feedback Activer le retour d'information des shaders - + When checked, it executes shaders without loop logic changes Lorsque la case est cochée, exécuter les shaders sans changer la boucle de logique - + Disable Loop safety checks Désactiver les vérifications de boucle - + Debugging Débogage - - Enable FS Access Log - Activer la journalisation des accès du système de fichiers - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Activer les services de rapport verbeux** - + + Enable FS Access Log + Activer la journalisation des accès du système de fichiers + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + Activez cette option pour afficher la dernière liste de commandes audio générée sur la console. N'affecte que les jeux utilisant le moteur de rendu audio. + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avancé - + Kiosk (Quest) Mode Mode Kiosk (Quest) - + Enable CPU Debugging Activer le Débogage CPU - + Enable Debug Asserts Activer les assertions de débogage - + Enable Auto-Stub** Activer l'Auto-Stub** - + Enable All Controller Types - + Activer tous les types de contrôleurs - + Disable Web Applet Désactiver l'applet web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Ces options seront réinitialisées automatiquement lorsque yuzu fermera. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1242,7 +1292,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Extended memory layout (6GB DRAM) - + Disposition de la mémoire étendue (6GB DRAM) @@ -1544,7 +1594,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Use VSync - + Utiliser VSync @@ -1567,37 +1617,47 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Utiliser le Temps GPU Rapide (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtrage anisotropique : - + Automatic Automatique - + Default Défaut - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1997,7 +2057,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Infrared Camera - + Caméra infrarouge @@ -2089,7 +2149,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + Left Stick Stick Gauche @@ -2183,14 +2243,14 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + L L - + ZL ZL @@ -2209,7 +2269,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + Plus Plus @@ -2222,15 +2282,15 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + R R - + ZR ZR @@ -2287,231 +2347,236 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + Right Stick Stick Droit - - - - + + + + Clear Effacer - - - - - + + + + + [not set] [non défini] - - - Toggle button - Bouton d'activation - - - - + + Invert button Inverser les boutons - - + + + Toggle button + Bouton d'activation + + + + Invert axis Inverser l'axe - - - + + + Set threshold Définir le seuil - - + + Choose a value between 0% and 100% Choisissez une valeur entre 0% et 100% - + + Toggle axis + + + + Set gyro threshold Définir le seuil du gyroscope - + Map Analog Stick Mapper le stick analogique - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Après avoir appuyé sur OK, bougez d'abord votre joystick horizontalement, puis verticalement. Pour inverser les axes, bougez d'abord votre joystick verticalement, puis horizontalement. - + Center axis - + Axe central - - + + Deadzone: %1% Zone morte : %1% - - + + Modifier Range: %1% Modification de la course : %1% - - + + Pro Controller Pro Controller - + Dual Joycons Deux Joycons - + Left Joycon Joycon de gauche - + Right Joycon Joycon de droit - + Handheld Mode Portable - + GameCube Controller Manette GameCube - + Poke Ball Plus Poké Ball Plus - + NES Controller Manette NES - + SNES Controller Manette SNES - + N64 Controller Manette N64 - + Sega Genesis Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Stick de contrôle - + C-Stick C-Stick - + Shake! Secouez ! - + [waiting] [en attente] - + New Profile Nouveau Profil - + Enter a profile name: Entrez un nom de profil : - - + + Create Input Profile Créer un profil d'entrée - + The given profile name is not valid! Le nom de profil donné est invalide ! - + Failed to create the input profile "%1" Échec de la création du profil d'entrée "%1" - + Delete Input Profile Supprimer le profil d'entrée - + Failed to delete the input profile "%1" Échec de la suppression du profil d'entrée "%1" - + Load Input Profile Charger le profil d'entrée - + Failed to load the input profile "%1" Échec du chargement du profil d'entrée "%1" - + Save Input Profile Sauvegarder le profil d'entrée - + Failed to save the input profile "%1" Échec de la sauvegarde du profil d'entrée "%1" @@ -2766,42 +2831,42 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h Développeur - + Add-Ons Extensions - + General Général - + System Système - + CPU CPU - + Graphics Graphiques - + Adv. Graphics Adv. Graphiques - + Audio Audio - + Properties Propriétés @@ -2995,7 +3060,7 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly. - + Si vous souhaitez utiliser cette manette, configurez le joueur 1 comme manette droite et le joueur 2 comme double joycon avant de lancer le jeu pour permettre à cette manette d'être détectée correctement. @@ -3006,13 +3071,13 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h Pull - + Tirer Push - + Pousser @@ -3513,47 +3578,47 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h <html><head/><body><p>Lit l'entrée du contrôleur à partir des scripts dans le même format que 'TAS-nx' <br/> Pour une explication plus détaillée, veuillez consulter le <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">page d'aide </span></a>sur le site Yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Pour vérifier quelles touches de raccourci contrôlent la lecture/l'enregistrement, veuillez vous reporter aux paramètres des touches de raccourci (Configurer -> Général -> Touches de raccourci). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. AVERTISSEMENT : Cette fonctionnalité est expérimentale.<br/>Elle n'exécutera pas les scripts à l'image près avec l'actuelle méthode, imparfaite, de synchronisation. - + Settings Paramètres - + Enable TAS features Activer les fonctions TAS - + Loop script Script de boucle - + Pause execution during loads Mettre en pause l'exécution pendant le chargement - + Script Directory Dossier de script - + Path Chemin - + ... ... @@ -3869,7 +3934,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Press any controller button to vibrate the controller. - + Appuyez sur n'importe quel bouton du contrôleur pour faire vibrer le contrôleur. @@ -3963,7 +4028,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce - + Verify Vérifier @@ -3990,7 +4055,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Web Service configuration can only be changed when a public room isn't being hosted. - + La configuration du service Web ne peut être modifiée que lorsqu'une salle publique n'est pas hébergée. @@ -4050,7 +4115,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce - + Unspecified Non-spécifié @@ -4065,17 +4130,36 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Le token n'a pas été vérifié. Le changement à votre token n'a pas été enregistré. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Vérification... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Échec de la vérification + + + Verification failed Échec de la vérification - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Échec de la vérification. Vérifiez si vous avez correctement entrez votre token, et que votre connection internet fonctionne. @@ -4113,7 +4197,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce <html><head/><body><p>IPv4 address of the host</p></body></html> - + <html><head/><body><p>Adresse IPv4 de l'hôte</p></body></html> @@ -4123,7 +4207,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce <html><head/><body><p>Port number the host is listening on</p></body></html> - + <html><head/><body><p>Numéro de port sur lequel l'hôte écoute</p></body></html> @@ -4138,928 +4222,909 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Connect - + Connecter DirectConnectWindow - + Connecting - + Connexion - + Connect - + Connecter GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Des données anonymes sont collectées</a> pour aider à améliorer yuzu. <br/><br/>Voulez-vous partager vos données d'utilisations avec nous ? - + Telemetry Télémétrie - + Broken Vulkan Installation Detected Installation Vulkan Cassée Détectée - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + L'initialisation de Vulkan a échoué lors du démarrage.<br><br>Cliquez <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>ici pour obtenir des instructions pour résoudre le problème</a>. - + Loading Web Applet... Chargement du Web Applet... - + Disable Web Applet Désactiver l'applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + La désactivation de l'applet Web peut entraîner un comportement indéfini et ne doit être utilisée qu'avec Super Mario 3D All-Stars. Voulez-vous vraiment désactiver l'applet Web ? +(Cela peut être réactivé dans les paramètres de débogage.) - + The amount of shaders currently being built La quantité de shaders en cours de construction - + The current selected resolution scaling multiplier. Le multiplicateur de mise à l'échelle de résolution actuellement sélectionné. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Valeur actuelle de la vitesse de l'émulation. Des valeurs plus hautes ou plus basses que 100% indique que l'émulation fonctionne plus vite ou plus lentement qu'une véritable Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Combien d'image par seconde le jeu est en train d'afficher. Ceci vas varier de jeu en jeu et de scènes en scènes. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Temps pris pour émuler une image par seconde de la switch, sans compter le limiteur d'image par seconde ou la synchronisation verticale. Pour une émulation à pleine vitesse, ceci devrait être au maximum à 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Effacer les fichiers récents - + &Continue &Continuer - + &Pause &Pause - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu exécute un jeu - + Warning Outdated Game Format Avertissement : Le Format de jeu est dépassé - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Vous utilisez un format de ROM déconstruite pour ce jeu, qui est donc un format dépassé qui à été remplacer par d'autre. Par exemple les formats NCA, NAX, XCI, ou NSP. Les destinations de ROM déconstruites manque des icônes, des métadonnée et du support de mise à jour.<br><br>Pour une explication des divers formats Switch que yuzu supporte, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Regardez dans le wiki</a>. Ce message ne sera pas montré une autre fois. - - + + Error while loading ROM! Erreur lors du chargement de la ROM ! - + The ROM format is not supported. Le format de la ROM n'est pas supporté. - + An error occurred initializing the video core. Une erreur s'est produite lors de l'initialisation du noyau dédié à la vidéo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu a rencontré une erreur en exécutant le cœur vidéo. Cela est généralement causé par des pilotes graphiques trop anciens. Veuillez consulter les logs pour plus d'informations. Pour savoir comment accéder aux logs, veuillez vous référer à la page suivante : <a href='https://yuzu-emu.org/help/reference/log-files/'>Comment partager un fichier de log </a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Erreur lors du chargement de la ROM ! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Veuillez suivre <a href='https://yuzu-emu.org/help/quickstart/'>le guide de démarrage rapide yuzu</a> pour retransférer vos fichiers.<br>Vous pouvez vous référer au wiki yuzu</a> ou le Discord yuzu</a> pour de l'assistance. - + An unknown error occurred. Please see the log for more details. Une erreur inconnue est survenue. Veuillez consulter le journal des logs pour plus de détails. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Enregistrer les données - + Mod Data Donnés du Mod - + Error Opening %1 Folder Erreur dans l'ouverture du dossier %1. - - + + Folder does not exist! Le dossier n'existe pas ! - + Error Opening Transferable Shader Cache Erreur lors de l'ouverture des Shader Cache Transferable - + Failed to create the shader cache directory for this title. Impossible de créer le dossier de cache du shader pour ce jeu. - + Contents Contenus - + Update Mise à jour - + DLC DLC - + Remove Entry Supprimer l'entrée - + Remove Installed Game %1? Supprimer le jeu installé %1? - - - - - - + + + + + + Successfully Removed Supprimé avec succès - + Successfully removed the installed base game. Suppression du jeu de base installé avec succès. - - - + + + Error Removing %1 Erreur lors de la suppression %1 - + The base game is not installed in the NAND and cannot be removed. Le jeu de base n'est pas installé dans la NAND et ne peut pas être supprimé. - + Successfully removed the installed update. Suppression de la mise à jour installée avec succès. - + There is no update installed for this title. Il n'y a pas de mise à jour installée pour ce titre. - + There are no DLC installed for this title. Il n'y a pas de DLC installé pour ce titre. - + Successfully removed %1 installed DLC. Suppression de %1 DLC installé(s) avec succès. - + Delete OpenGL Transferable Shader Cache? Supprimer la Cache OpenGL de Shader Transférable? - + Delete Vulkan Transferable Shader Cache? Supprimer la Cache Vulkan de Shader Transférable? - + Delete All Transferable Shader Caches? Supprimer Toutes les Caches de Shader Transférable? - + Remove Custom Game Configuration? Supprimer la configuration personnalisée du jeu? - + Remove File Supprimer fichier - - + + Error Removing Transferable Shader Cache Erreur lors de la suppression du cache de shader transférable - - + + A shader cache for this title does not exist. Un shader cache pour ce titre n'existe pas. - + Successfully removed the transferable shader cache. Suppression du cache de shader transférable avec succès. - + Failed to remove the transferable shader cache. Échec de la suppression du cache de shader transférable. - - + + Error Removing Transferable Shader Caches Erreur durant la Suppression des Caches de Shader Transférable - + Successfully removed the transferable shader caches. Suppression des caches de shader transférable effectuée avec succès. - + Failed to remove the transferable shader cache directory. Impossible de supprimer le dossier de la cache de shader transférable. - - + + Error Removing Custom Configuration Erreur lors de la suppression de la configuration personnalisée - + A custom configuration for this title does not exist. Il n'existe pas de configuration personnalisée pour ce titre. - + Successfully removed the custom game configuration. Suppression de la configuration de jeu personnalisée avec succès. - + Failed to remove the custom game configuration. Échec de la suppression de la configuration personnalisée du jeu. - - + + RomFS Extraction Failed! L'extraction de la RomFS a échoué ! - + There was an error copying the RomFS files or the user cancelled the operation. Une erreur s'est produite lors de la copie des fichiers RomFS ou l'utilisateur a annulé l'opération. - + Full Plein - + Skeleton Squelette - + Select RomFS Dump Mode Sélectionnez le mode d'extraction de la RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Veuillez sélectionner la manière dont vous souhaitez que le fichier RomFS soit extrait.<br>Full copiera tous les fichiers dans le nouveau répertoire, tandis que<br>skeleton créera uniquement la structure de répertoires. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Il n'y a pas assez d'espace libre dans %1 pour extraire la RomFS. Veuillez libérer de l'espace ou sélectionner un autre dossier d'extraction dans Émulation > Configurer > Système > Système de fichiers > Extraire la racine - + Extracting RomFS... Extraction de la RomFS ... - - + + Cancel Annuler - + RomFS Extraction Succeeded! Extraction de la RomFS réussi ! - + The operation completed successfully. L'opération s'est déroulée avec succès. - + Error Opening %1 Erreur lors de l'ouverture %1 - + Select Directory Sélectionner un répertoire - + Properties Propriétés - + The game properties could not be loaded. Les propriétés du jeu n'ont pas pu être chargées. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Exécutable Switch (%1);;Tous les fichiers (*.*) - + Load File Charger un fichier - + Open Extracted ROM Directory Ouvrir le dossier des ROM extraites - + Invalid Directory Selected Destination sélectionnée invalide - + The directory you have selected does not contain a 'main' file. Le répertoire que vous avez sélectionné ne contient pas de fichier "main". - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Fichier Switch installable (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installer les fichiers - + %n file(s) remaining %n fichier restant%n fichiers restants%n fichiers restants - + Installing file "%1"... Installation du fichier "%1" ... - - + + Install Results Résultats d'installation - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Pour éviter d'éventuels conflits, nous déconseillons aux utilisateurs d'installer des jeux de base sur la NAND. Veuillez n'utiliser cette fonctionnalité que pour installer des mises à jour et des DLC. - + %n file(s) were newly installed %n fichier a été nouvellement installé%n fichiers ont été nouvellement installés%n fichiers ont été nouvellement installés - + %n file(s) were overwritten %n fichier a été écrasé%n fichiers ont été écrasés%n fichiers ont été écrasés - + %n file(s) failed to install %n fichier n'a pas pu être installé%n fichiers n'ont pas pu être installés%n fichiers n'ont pas pu être installés - + System Application Application Système - + System Archive Archive Système - + System Application Update Mise à jour de l'application système - + Firmware Package (Type A) Paquet micrologiciel (Type A) - + Firmware Package (Type B) Paquet micrologiciel (Type B) - + Game Jeu - + Game Update Mise à jour de jeu - + Game DLC DLC de jeu - + Delta Title Titre Delta - + Select NCA Install Type... Sélectionner le type d'installation du NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Veuillez sélectionner le type de titre auquel vous voulez installer ce NCA : (Dans la plupart des cas, le titre par défaut : 'Jeu' est correct.) - + Failed to Install Échec de l'installation - + The title type you selected for the NCA is invalid. Le type de titre que vous avez sélectionné pour le NCA n'est pas valide. - + File not found Fichier non trouvé - + File "%1" not found Fichier "%1" non trouvé - + OK OK - + Missing yuzu Account Compte yuzu manquant - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Pour soumettre un test de compatibilité pour un jeu, vous devez lier votre compte yuzu.<br><br/>Pour lier votre compte yuzu, aller à Emulation &gt; Configuration&gt; Web. - + Error opening URL Erreur lors de l'ouverture de l'URL - + Unable to open the URL "%1". Impossible d'ouvrir l'URL "%1". - + TAS Recording Enregistrement TAS - + Overwrite file of player 1? Écraser le fichier du joueur 1 ? - + Invalid config detected Configuration invalide détectée - + Handheld controller can't be used on docked mode. Pro controller will be selected. Contrôleur portable ne peut pas être utilisé en mode téléviseur. La manette Pro sera sélectionnée. - - + + Error Erreur - - + + The current game is not looking for amiibos Le jeu actuel ne cherche pas d'amiibos. - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'amiibo actuel a été retiré - + Amiibo File (%1);; All Files (*.*) Fichier Amiibo (%1);; Tous les fichiers (*.*) - + Load Amiibo Charger un Amiibo - - Error opening Amiibo data file - Erreur lors de l'ouverture du fichier de données Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Impossible d'ouvrir le fichier Amiibo "%1" à lire. - - - - Error reading Amiibo data file - Erreur lors de la lecture du fichier de données Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Impossible de lire entièrement les données Amiibo. On s'attend à lire %1 octets, mais il n'a pu lire que %2 octets - - - + Error loading Amiibo data Erreur lors du chargement des données Amiibo - + Unable to load Amiibo data. Impossible de charger les données Amiibo. - + Capture Screenshot Capture d'écran - + PNG Image (*.png) Image PNG (*.png) - + TAS state: Running %1/%2 État du TAS : En cours d'exécution %1/%2 - + TAS state: Recording %1 État du TAS : Enregistrement %1 - + TAS state: Idle %1/%2 État du TAS : Inactif %1:%2 - + TAS State: Invalid État du TAS : Invalide - + &Stop Running &Stopper l'exécution - + &Start &Start - + Stop R&ecording Stopper l'en&registrement - + R&ecord En&registrer - + Building: %n shader(s) Compilation: %n shaderCompilation : %n shadersCompilation : %n shaders - + Scale: %1x %1 is the resolution scaling factor Échelle : %1x - + Speed: %1% / %2% Vitesse : %1% / %2% - + Speed: %1% Vitesse : %1% - + Game: %1 FPS (Unlocked) Jeu : %1 IPS (Débloqué) - + Game: %1 FPS Jeu : %1 FPS - + Frame: %1 ms Frame : %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HAUT - + GPU EXTREME GPU EXTRÊME - + GPU ERROR GPU ERREUR - + DOCKED MODE TV - + HANDHELD PORTABLE - + NEAREST PLUS PROCHE - - + + BILINEAR BILINÉAIRE - + BICUBIC BICUBIQUE - + GAUSSIAN GAUSSIEN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA AUCUN AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Le jeu que vous essayez de charger a besoin de fichiers additionnels que vous devez extraire depuis votre Switch avant de jouer.<br/><br/>Pour plus d'information sur l'extraction de ces fichiers, veuillez consulter la page du wiki suivante : <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Extraction des archives système et des Shared Fonts depuis la Switch</a>.<br/><br/>Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs. - + yuzu was unable to locate a Switch system archive. %1 yuzu n'a pas été capable de localiser un système d'archive Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu n'a pas été capable de localiser un système d'archive Switch. %1. %2 - + System Archive Not Found Archive système introuvable - + System Archive Missing Archive Système Manquante - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu n'a pas été capable de localiser les polices partagées de la Switch. %1 - + Shared Fonts Not Found Les polices partagées non pas été trouvées - + Shared Font Missing Police Partagée Manquante - + Fatal Error Erreur fatale - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu a rencontré une erreur fatale, veuillez consulter les logs pour plus de détails. Pour plus d'informations sur l'accès aux logs, veuillez consulter la page suivante : <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'> Comment télécharger le fichier des logs </a>.<br/><br/>Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs. - + Fatal Error encountered Erreur Fatale rencontrée - + Confirm Key Rederivation Confirmer la réinstallation de la clé - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5076,37 +5141,37 @@ et éventuellement faites des sauvegardes. Cela supprimera vos fichiers de clé générés automatiquement et ré exécutera le module d'installation de clé. - + Missing fuses Fusibles manquants - + - Missing BOOT0 - BOOT0 manquant - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main manquant - + - Missing PRODINFO - PRODINFO manquant - + Derivation Components Missing Composants de dérivation manquants - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Les clés de chiffrement sont manquantes. <br>Veuillez suivre <a href='https://yuzu-emu.org/help/quickstart/'>le guide de démarrage rapide yuzu</a> pour obtenir tous vos clés, firmware et jeux.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5115,39 +5180,39 @@ Cela peut prendre jusqu'à une minute en fonction des performances de votre système. - + Deriving Keys Installation des clés - + Select RomFS Dump Target Sélectionner la cible d'extraction du RomFS - + Please select which RomFS you would like to dump. Veuillez sélectionner quel RomFS vous voulez extraire. - + Are you sure you want to close yuzu? Êtes vous sûr de vouloir fermer yuzu ? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Êtes-vous sûr d'arrêter l'émulation ? Tout progrès non enregistré sera perdu. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5486,7 +5551,7 @@ Screen. (Leave blank for open game) - + (Laisser vide pour ouvrir le jeu) @@ -5506,7 +5571,7 @@ Screen. Load Previous Ban List - + Charger la liste de bannissement précédente @@ -5527,25 +5592,27 @@ Screen. HostRoomWindow - + Error Erreur - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + Échec de l'annonce de la salle dans le hall public. Pour héberger une salle publiquement, vous devez avoir un compte yuzu valide configuré dans Emulation -> Configurer -> Web. Si vous ne souhaitez pas publier une salle dans le hall public, sélectionnez plutôt Non Répertorié. +Message de débogage : Hotkeys - + Audio Mute/Unmute - + Désactiver/Activer le Son + @@ -5567,114 +5634,113 @@ Debug Message: - Main Window - + Fenêtre Principale + + + + Audio Volume Down + Baisser le volume audio - Audio Volume Down - + Audio Volume Up + Augmenter le volume audio - Audio Volume Up - - - - Capture Screenshot Prendre une capture d'ecran - + Change Adapting Filter - + Modifier le filtre d'adaptation + + + + Change Docked Mode + Changer le mode de la station d'accueil - Change Docked Mode - + Change GPU Accuracy + Modifier la précision du GPU - Change GPU Accuracy - - - - Continue/Pause Emulation Continuer/Suspendre l'Émulation - + Exit Fullscreen Quitter le plein écran - + Exit yuzu Quitter yuzu - + Fullscreen Plein écran - + Load File Charger un fichier - + Load/Remove Amiibo Charger/Supprimer un Amiibo - + Restart Emulation Redémarrer l'Émulation - + Stop Emulation Arrêter l'Émulation - + TAS Record - + Enregistrement TAS + + + + TAS Reset + Réinitialiser le TAS - TAS Reset - + TAS Start/Stop + Démarrer/Arrêter le TAS - TAS Start/Stop - + Toggle Filter Bar + Activer la barre de filtre - Toggle Filter Bar - + Toggle Framerate Limit + Activer la limite de fréquence d'images - Toggle Framerate Limit - + Toggle Mouse Panning + Activer le panoramique de la souris - Toggle Mouse Panning - - - - Toggle Status Bar - + Activer la barre d'état @@ -5787,42 +5853,42 @@ Debug Message: - + Password Required to Join - + Mot de passe requis pour rejoindre - + Password: Mot de passe: - + Room Name Nom du salon - + Preferred Game Jeu préféré - + Host Hôte - + Players Joueurs - + Refreshing Rafraîchissement - + Refresh List Rafraîchir la liste @@ -5997,12 +6063,12 @@ Debug Message: Direct Connect to Room - + Connexion directe au salon Show Current Room - + Afficher le salon actuel @@ -6088,12 +6154,12 @@ Debug Message: Moderation - + Modération Ban List - + Liste de bannissement @@ -6104,22 +6170,22 @@ Debug Message: Unban - + Débannir Subject - + Sujet Type - + Type Forum Username - + Nom d'utilisateur du forum @@ -6138,18 +6204,18 @@ Debug Message: Current connection status - + État actuel de la connexion Not Connected. Click here to find a room! - + Pas connecté. Cliquez ici pour trouver un salon ! - + Connected Connecté @@ -6171,9 +6237,9 @@ Debug Message: - + New Messages Received - + Nouveaux messages reçus @@ -6181,62 +6247,62 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + Le nom d'utilisateur n'est pas valide. Doit être de 4 à 20 caractères alphanumériques. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Le nom du salon n'est pas valide. Doit être de 4 à 20 caractères alphanumériques. Username is already in use or not valid. Please choose another. - + Le nom d'utilisateur est déjà utilisé ou n'est pas valide. Veuillez en sélectionner un autre. IP is not a valid IPv4 address. - + IP n'est pas une adresse IPv4 valide. Port must be a number between 0 to 65535. - + Le port doit être un nombre compris entre 0 et 65535. You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + Vous devez choisir un jeu préféré pour héberger un salon. Si vous n'avez pas encore de jeux dans votre liste de jeux, ajoutez un dossier de jeu en cliquant sur l'icône plus dans la liste de jeux. Unable to find an internet connection. Check your internet settings. - + Impossible de trouver une connexion Internet. Vérifiez vos paramètres Internet. Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + Impossible de se connecter à l'hôte. Vérifiez que les paramètres de connexion sont corrects. Si vous ne parvenez toujours pas à vous connecter, contactez l'hôte de la salle et vérifiez que l'hôte a correctement configuré le port externe redirigé. Unable to connect to the room because it is already full. - + Impossible de se connecter au salon car il est déjà plein. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + La création d'un salon a échoué. Veuillez réessayer. Peut être que vous devriez redémarrer yuzu. The host of the room has banned you. Speak with the host to unban you or try a different room. - + L'hôte du salon vous a banni. Parlez à l'hôte pour vous débannir ou essayez un autre salon. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + Décalage de version ! Veuillez mettre à jour la dernière version de yuzu. Si le problème persiste, contactez l'hôte de la salle et demandez lui de mettre à jour le serveur. @@ -6251,54 +6317,74 @@ Debug Message: Connection to room lost. Try to reconnect. - + Connexion au salon perdue. Essayez de vous reconnecter. You have been kicked by the room host. - + Vous avez été expulsé par l'hôte du salon. IP address is already in use. Please choose another. - + L'adresse IP est déjà utilisée. Veuillez en sélectionner une autre. You do not have enough permission to perform this action. - + Vous ne disposez pas des autorisations suffisantes pour effectuer cette action. The user you are trying to kick/ban could not be found. They may have left the room. - + L'utilisateur que vous essayez d'exclure/bannir est introuvable. +Il a peut-être quitté la salon. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Aucune interface réseau n'est sélectionnée. +Allez dans Configurer -> Système -> Réseau et faites une sélection. + + + + Game already running + Jeu déjà en cours + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Rejoindre un salon lorsque le jeu est déjà en cours d'exécution est déconseillé et peut empêcher le salon de fonctionner correctement. +Continuer quand même ? + + + Leave Room Quitter le salon - + You are about to close the room. Any network connections will be closed. - + Vous êtes sur le point de fermer le salon. Toutes les connexions réseau seront fermées. - + Disconnect - + Déconnecter - + You are about to leave the room. Any network connections will be closed. - + Vous êtes sur le point de quitter le salon. Toutes les connexions réseau seront fermées. NetworkMessage::ErrorManager - + Error Erreur @@ -6349,15 +6435,15 @@ p, li { white-space: pre-wrap; } %1 is not playing a game - + %1 ne joue pas à un jeu %1 is playing %2 - + %1 joue à %2 - + Not playing a game Ne joue pas à un jeu @@ -6412,7 +6498,7 @@ p, li { white-space: pre-wrap; } - + [not set] [non défini] @@ -6427,10 +6513,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axe %1%2 @@ -6444,9 +6530,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [inconnu] @@ -6611,15 +6697,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [invalide] - - + + %1%2Hat %3 %1%2Chapeau %3 @@ -6627,35 +6713,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Axe %3 - + %1%2Axis %3,%4,%5 %1%2Axe %3,%4,%5 - + %1%2Motion %3 %1%2Mouvement %3 - - + + %1%2Button %3 %1%2Bouton %3 - + [unused] [inutilisé] @@ -6696,7 +6782,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/id.ts b/dist/languages/id.ts index 7a9b2a6c0b..da09ad1139 100644 --- a/dist/languages/id.ts +++ b/dist/languages/id.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -730,200 +730,235 @@ Memungkinkan berbagai macam optimasi IR. ConfigureDebug - + Debugger - + Enable GDB Stub Aktifkan GDB Stub - + Port: Port: - + Logging Pencatatan - + Global Log Filter Catatan Penyaring Global - + Show Log in Console Tampilkan Catatan Di Konsol - + Open Log Location Buka Lokasi Catatan - + When checked, the max size of the log increases from 100 MB to 1 GB Saat dicentang, ukuran maksimum log meningkat dari 100 MB menjadi 1 GB - + Enable Extended Logging** Aktifkan Pencatatan Yang Diperluas** - + Homebrew Homebrew - + Arguments String String Argumen - + Graphics Grafis - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Nyalakan Pengawakutuan Grafis - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath Aktifkan Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Saat dicentang, ini menonaktifkan kompiler makro Just In Time. Mengaktifkan ini membuat game berjalan lebih lambat - + Disable Macro JIT Matikan Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Saat dinyalakan, yuzu akan mencatat statstik tentang pipeline cache yang disusun - + Enable Shader Feedback Nyalakan Umpan Balik Shader - + When checked, it executes shaders without loop logic changes Saat dinyalakan, akan menjalankan shader tanpa perubahan logika loop - + Disable Loop safety checks Matikan cek keamanan Loop - + Debugging Pengawakutuan - - Enable FS Access Log - Nyalakan Log Akses FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Nyalakan Layanan Laporan Bertele-tele** - + + Enable FS Access Log + Nyalakan Log Akses FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Lanjutan - + Kiosk (Quest) Mode Mode Kiosk (Pencarian) - + Enable CPU Debugging Nyalakan Pengawakutuan CPU - + Enable Debug Asserts Nyalakan Awakutu Assert - + Enable Auto-Stub** - + Enable All Controller Types Aktifkan Semua Jenis Controller - + Disable Web Applet Matikan Applet Web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Ini akan diatur ulang secara otomatis ketika yuzu ditutup. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1542,37 +1577,47 @@ Memungkinkan berbagai macam optimasi IR. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropic Filtering: - + Automatic Otomatis - + Default Bawaan - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2064,7 +2109,7 @@ Memungkinkan berbagai macam optimasi IR. - + Left Stick Stik Kiri @@ -2158,14 +2203,14 @@ Memungkinkan berbagai macam optimasi IR. - + L L - + ZL ZL @@ -2184,7 +2229,7 @@ Memungkinkan berbagai macam optimasi IR. - + Plus Tambah @@ -2197,15 +2242,15 @@ Memungkinkan berbagai macam optimasi IR. - + R R - + ZR ZR @@ -2262,231 +2307,236 @@ Memungkinkan berbagai macam optimasi IR. - + Right Stick Stik Kanan - - - - + + + + Clear Bersihkan - - - - - + + + + + [not set] [belum diatur] - - - Toggle button - Atur tombol - - - - + + Invert button Balikkan tombol - - + + + Toggle button + Atur tombol + + + + Invert axis Balikkan poros - - - + + + Set threshold Atur batasan - - + + Choose a value between 0% and 100% Pilih sebuah angka diantara 0% dan 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Petakan Stik Analog - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Setelah menekan OK, pertama gerakkan joystik secara mendatar, lalu tegak lurus. Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu mendatar. - + Center axis - - + + Deadzone: %1% Titik Mati: %1% - - + + Modifier Range: %1% Rentang Pengubah: %1% - - + + Pro Controller Kontroler Pro - + Dual Joycons Joycon Dual - + Left Joycon Joycon Kiri - + Right Joycon Joycon Kanan - + Handheld Jinjing - + GameCube Controller Kontroler GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Kontroler NES - + SNES Controller Kontroler SNES - + N64 Controller Kontroler N64 - + Sega Genesis Sega Genesis - + Start / Pause Mulai / Jeda - + Z Z - + Control Stick Stik Kendali - + C-Stick C-Stick - + Shake! Getarkan! - + [waiting] [menunggu] - + New Profile Profil Baru - + Enter a profile name: Masukkan nama profil: - - + + Create Input Profile Ciptakan Profil Masukan - + The given profile name is not valid! Nama profil yang diberi tidak sah! - + Failed to create the input profile "%1" Gagal membuat profil masukan "%1" - + Delete Input Profile Hapus Profil Masukan - + Failed to delete the input profile "%1" Gagal menghapus profil masukan "%1" - + Load Input Profile Muat Profil Masukan - + Failed to load the input profile "%1" Gagal memuat profil masukan "%1" - + Save Input Profile Simpat Profil Masukan - + Failed to save the input profile "%1" Gagal menyimpan profil masukan "%1" @@ -2741,42 +2791,42 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda Pengembang - + Add-Ons Pengaya (Add-On) - + General Umum - + System Sistem - + CPU CPU - + Graphics Grafis - + Adv. Graphics Ljtan. Grafik - + Audio Audio - + Properties Properti @@ -3488,47 +3538,47 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings Pengaturan - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Jalur - + ... ... @@ -3937,7 +3987,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Verifikasi @@ -4024,7 +4074,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified Tak dispesifikasikan @@ -4039,17 +4089,36 @@ Drag points to change position, or double-click table cells to edit values.Token tidak terverifikasi. Perubahan ke token Anda tak tersimpan - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Memverifikasi... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verifikasi gagal + + + Verification failed Verifikasi gagal - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Pemverifikasian gagal. Periksa apakah token yang dimasukkan sudah benar dan apakah koneksi internet Anda berjalan. @@ -4118,12 +4187,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4131,911 +4200,891 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Data anonim dikumpulkan</a> untuk membantu yuzu. <br/><br/>Apa Anda ingin membagi data penggunaan dengan kami? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Memuat Applet Web... - + Disable Web Applet Matikan Applet Web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Jumlah shader yang sedang dibuat - + The current selected resolution scaling multiplier. Pengali skala resolusi yang terpilih. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Kecepatan emulasi saat ini. Nilai yang lebih tinggi atau rendah dari 100% menandakan pengemulasian berjalan lebih cepat atau lambat dibanding Switch aslinya. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Berapa banyak frame per second (bingkai per detik) permainan akan ditampilkan. Ini akan berubah dari berbagai permainan dan pemandangan. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Waktu yang diperlukan untuk mengemulasikan bingkai Switch, tak menghitung pembatas bingkai atau v-sync. Agar emulasi berkecepatan penuh, ini harus 16.67 mdtk. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Bersihkan Berkas Baru-baru Ini - + &Continue &Lanjutkan - + &Pause &Jeda - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu sedang menjalankan game - + Warning Outdated Game Format Peringatan Format Permainan yang Usang - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Anda menggunakan format direktori ROM yang sudah didekonstruksi untuk permainan ini, yang mana itu merupakan format lawas yang sudah tergantikan oleh yang lain seperti NCA, NAX, XCI, atau NSP. Direktori ROM yang sudah didekonstruksi kekurangan ikon, metadata, dan dukungan pembaruan.<br><br>Untuk penjelasan berbagai format Switch yang didukung yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>periksa wiki kami</a>. Pesan ini tidak akan ditampilkan lagi. - - + + Error while loading ROM! Kesalahan ketika memuat ROM! - + The ROM format is not supported. Format ROM tak didukung. - + An error occurred initializing the video core. Terjadi kesalahan ketika menginisialisasi inti video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu telah mengalami error saat menjalankan inti video. Ini biasanya disebabkan oleh pemicu piranti (driver) GPU yang usang, termasuk yang terintegrasi. Mohon lihat catatan untuk informasi lebih rinci. Untuk informasi cara mengakses catatan, mohon lihat halaman berikut: <a href='https://yuzu-emu.org/help/reference/log-files/'>Cara Mengupload Berkas Catatan</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Terjadi kesalahan yang tak diketahui. Mohon lihat catatan untuk informasi lebih rinci. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Simpan Data - + Mod Data Mod Data - + Error Opening %1 Folder Gagal Membuka Folder %1 - - + + Folder does not exist! Folder tak ada! - + Error Opening Transferable Shader Cache Gagal Ketika Membuka Tembolok Shader yang Dapat Ditransfer - + Failed to create the shader cache directory for this title. - + Contents Konten - + Update Perbaharui - + DLC DLC - + Remove Entry Hapus Masukan - + Remove Installed Game %1? - - - - - - + + + + + + Successfully Removed - + Successfully removed the installed base game. - - - + + + Error Removing %1 - + The base game is not installed in the NAND and cannot be removed. - + Successfully removed the installed update. - + There is no update installed for this title. - + There are no DLC installed for this title. Tidak ada DLC yang terinstall untuk judul ini. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File Hapus File - - + + Error Removing Transferable Shader Cache Kesalahan Menghapus Transferable Shader Cache - - + + A shader cache for this title does not exist. Cache shader bagi judul ini tidak ada - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Kesalahan Menghapus Konfigurasi Buatan - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! Pengekstrakan RomFS Gagal! - + There was an error copying the RomFS files or the user cancelled the operation. Terjadi kesalahan ketika menyalin berkas RomFS atau dibatalkan oleh pengguna. - + Full Penuh - + Skeleton Skeleton - + Select RomFS Dump Mode Pilih Mode Dump RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Mohon pilih cara RomFS akan di-dump.<br>FPenuh akan menyalin seluruh berkas ke dalam direktori baru sementara <br>jerangkong hanya akan menciptakan struktur direktorinya saja. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Mengekstrak RomFS... - - + + Cancel Batal - + RomFS Extraction Succeeded! Pengekstrakan RomFS Berhasil! - + The operation completed successfully. Operasi selesai dengan sukses, - + Error Opening %1 Gagal membuka %1 - + Select Directory Pilih Direktori - + Properties Properti - + The game properties could not be loaded. Properti permainan tak dapat dimuat. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Eksekutabel Switch (%1);;Semua Berkas (*.*) - + Load File Muat Berkas - + Open Extracted ROM Directory Buka Direktori ROM Terekstrak - + Invalid Directory Selected Direktori Terpilih Tidak Sah - + The directory you have selected does not contain a 'main' file. Direktori yang Anda pilih tak memiliki berkas 'utama.' - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Install File - + %n file(s) remaining - + Installing file "%1"... Memasang berkas "%1"... - - + + Install Results Hasil Install - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed %n file(s) baru diinstall - + %n file(s) were overwritten %n file(s) telah ditimpa - + %n file(s) failed to install %n file(s) gagal di install - + System Application Aplikasi Sistem - + System Archive Arsip Sistem - + System Application Update Pembaruan Aplikasi Sistem - + Firmware Package (Type A) Paket Perangkat Tegar (Tipe A) - + Firmware Package (Type B) Paket Perangkat Tegar (Tipe B) - + Game Permainan - + Game Update Pembaruan Permainan - + Game DLC DLC Permainan - + Delta Title Judul Delta - + Select NCA Install Type... Pilih Tipe Pemasangan NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Mohon pilih jenis judul yang Anda ingin pasang sebagai NCA ini: (Dalam kebanyakan kasus, pilihan bawaan 'Permainan' tidak apa-apa`.) - + Failed to Install Gagal Memasang - + The title type you selected for the NCA is invalid. Jenis judul yang Anda pilih untuk NCA tidak sah. - + File not found Berkas tak ditemukan - + File "%1" not found Berkas "%1" tak ditemukan - + OK OK - + Missing yuzu Account Akun yuzu Hilang - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Agar dapat mengirimkan berkas uju kompatibilitas permainan, Anda harus menautkan akun yuzu Anda.<br><br/>TUntuk mennautkan akun yuzu Anda, pergi ke Emulasi &gt; Konfigurasi &gt; Web. - + Error opening URL Kesalahan saat membuka URL - + Unable to open the URL "%1". Tidak dapat membuka URL "%1". - + TAS Recording Rekaman TAS - + Overwrite file of player 1? Timpa file pemain 1? - + Invalid config detected Konfigurasi tidak sah terdeteksi - + Handheld controller can't be used on docked mode. Pro controller will be selected. Kontroller jinjing tidak bisa digunakan dalam mode dock. Kontroller Pro akan dipilih - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Berkas Amiibo (%1);; Semua Berkas (*.*) - + Load Amiibo Muat Amiibo - - Error opening Amiibo data file - Gagal membuka berkas data Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Tak dapat membuka berkas Amiibo "%1" untuk dibaca. - - - - Error reading Amiibo data file - Gagal membaca berkas data Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Tak dapat membaca data Amiibo sepenuhnya. Diperkirakan membaca %1 bita, namun hanya terbaca %2 bita. - - - + Error loading Amiibo data Gagal memuat data Amiibo - + Unable to load Amiibo data. Tak dapat memuat data Amiibo - + Capture Screenshot Tangkapan Layar - + PNG Image (*.png) Berkas PNG (*.png) - + TAS state: Running %1/%2 Status TAS: Berjalan %1/%2 - + TAS state: Recording %1 Status TAS: Merekam %1 - + TAS state: Idle %1/%2 Status TAS: Diam %1/%2 - + TAS State: Invalid Status TAS: Tidak Valid - + &Stop Running &Matikan - + &Start &Mulai - + Stop R&ecording Berhenti Mer&ekam - + R&ecord R&ekam - + Building: %n shader(s) Membangun: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Kecepatan: %1% / %2% - + Speed: %1% Kecepatan: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Permainan: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU TINGGI - + GPU EXTREME GPU EKSTRIM - + GPU ERROR KESALAHAN GPU - + DOCKED - + HANDHELD - + NEAREST NEAREST - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA TANPA AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found - + System Archive Missing - + yuzu was unable to locate the Switch shared fonts. %1 - + Shared Fonts Not Found - + Shared Font Missing - + Fatal Error Kesalahan Fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + Fatal Error encountered - + Confirm Key Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5046,76 +5095,76 @@ This will delete your autogenerated key files and re-run the key derivation modu - + Missing fuses - + - Missing BOOT0 - Kehilangan BOOT0 - + - Missing BCPKG2-1-Normal-Main - Kehilangan BCPKG2-1-Normal-Main - + - Missing PRODINFO - Kehilangan PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. - + Deriving Keys - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close yuzu? Apakah anda yakin ingin menutup yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5490,12 +5539,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5504,11 +5553,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5530,112 +5580,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Tangkapan Layar - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen - + Load File Muat Berkas - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5749,42 +5798,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Pemain - + Refreshing - + Refresh List @@ -6111,7 +6160,7 @@ Debug Message: - + Connected Terhubung @@ -6133,7 +6182,7 @@ Debug Message: - + New Messages Received @@ -6237,22 +6286,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6260,7 +6326,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6315,7 +6381,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6370,7 +6436,7 @@ p, li { white-space: pre-wrap; } - + [not set] [belum diatur] @@ -6385,10 +6451,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 @@ -6402,9 +6468,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] @@ -6569,15 +6635,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6585,35 +6651,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 %1%2Gerakan %3 - - + + %1%2Button %3 - + [unused] @@ -6654,7 +6720,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/it.ts b/dist/languages/it.ts index 76d639a4cd..5995053de4 100644 --- a/dist/languages/it.ts +++ b/dist/languages/it.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } Invia messaggio - + Members Membri - + %1 has joined %1 è entrato - + %1 has left %1 è uscito - + %1 has been kicked %1 è stato espulso - + %1 has been banned %1 è stato bannato - + %1 has been unbanned %1 non è più bannato - + View Profile Visualizza profilo - - + + Block Player Blocca giocatore - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Quando blocchi un giocatore, non riceverai più messaggi da quel giocatore.<br><br>Sei sicuro di voler bloccare %1? - + Kick Espelli - + Ban Banna - + Kick Player Espelli giocatore - + Are you sure you would like to <b>kick</b> %1? Sei sicuro di voler <b>espellere</b> %1? - + Ban Player Banna giocatore - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -282,7 +282,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Questo gioco è completamente ingiocabile a causa di gravi errori audio o video. E' impossibile proseguire oltre la schermata iniziale.</p></body></html> + <html><head/><body><p>Il gioco è del tutto ingiocabile a causa di considerevoli glitch audio o video. È impossibile proseguire oltre la schermata iniziale.</p></body></html> @@ -297,7 +297,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Indipendentemente da velocità o prestazioni, come ti è sembrato giocare questo gioco dall'inizio alla fine su questa versione di yuzu?</p></body></html> + <html><head/><body><p>Indipendentemente dalla velocità dalle prestazioni, come ti è sembrato giocare questo gioco dall'inizio alla fine su questa versione di yuzu?</p></body></html> @@ -666,13 +666,13 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - <div>Abilita le ottimizzazioni dell'IR che comportano una propagazione costante.</div> + <div>Abilita le ottimizzazioni dell'IR che comportano la propagazione delle costanti.</div> Enable constant propagation - Abilita la propagazione costante + Abilita la propagazione delle costanti @@ -758,200 +758,235 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. ConfigureDebug - + Debugger Debugger - + Enable GDB Stub Abilita stub GDB - + Port: Porta: - + Logging Logging - + Global Log Filter Filtro log globale - + Show Log in Console Mostra i log nella console - + Open Log Location Apri cartella dei log - + When checked, the max size of the log increases from 100 MB to 1 GB - Quando selezionata, la dimensione massima del log aumenterà da 100 MB a 1 GB + Quando l'opzione è selezionata, la dimensione massima del log aumenterà da 100 MB a 1 GB - + Enable Extended Logging** Abilita il log esteso** - + Homebrew Homebrew - + Arguments String Stringa degli Argomenti - + Graphics Grafica - + When checked, the graphics API enters a slower debugging mode - Quando selezionata, l'API entra in modalità di debug lento + Quando l'opzione è selezionata, l'API grafica entra in una modalità di debug più lenta - + Enable Graphics Debugging Abilita Debugging Grafica - + When checked, it enables Nsight Aftermath crash dumps - Se spuntato, abilita i crash dump di Nsight Aftermath + Quando l'opzione è selezionata, abilita i crash dump di Nsight Aftermath - + Enable Nsight Aftermath Abilita Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + Estrai shader del gioco - + When checked, it will dump all the macro programs of the GPU - + Quando l'opzione è selezionata, verranno estratti tutti i programmi macro della GPU - + Dump Maxwell Macros - + Estrai macro Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - Quando selezionato, disabilita il macro-compilatore JIT. Abilitalo per rendere i giochi più lenti + Quando l'opzione è selezionata, disabilita il compilatore Just-In-Time delle macro. Abilitare questa opzione rende i giochi più lenti - + Disable Macro JIT - Disabilita Macro JIT + Disabilita JIT macro - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Debug - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + Abilita log di accesso al FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avanzate - + Kiosk (Quest) Mode Modalità Kiosk (Quest) - + Enable CPU Debugging Abilita il debug della CPU - + Enable Debug Asserts Abilita le asserzioni di debug - + Enable Auto-Stub** - + Enable All Controller Types Abilita tutti i tipi di controller - + Disable Web Applet Disabilita l'applet web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **L'opzione verrà automaticamente ripristinata alla chiusura di yuzu. + + + Restart Required + Riavvio richiesto + + + + yuzu is required to restart in order to apply this setting. + yuzu dev'essere riavviato affinché questa opzione venga applicata. + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1285,7 +1320,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. This reset all settings and remove all per-game configurations. This will not delete game directories, profiles, or input profiles. Proceed? - Questo resetta tutte le impostazioni e rimuove tutte le configurazioni per gioco. Questo, non cancellerà le cartelle di gioco, i profili o i profili di input. Vuoi Procedere ? + Tutte le impostazioni verranno ripristinate e tutte le configurazioni dei giochi verranno rimosse. Le cartelle di gioco, i profili e i profili di input non saranno cancellati. Vuoi procedere? @@ -1308,7 +1343,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Shader Backend: - + Backend degli shader: @@ -1570,37 +1605,47 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtro anisotropico: - + Automatic Automatico - + Default Predefinito - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1673,7 +1718,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Invalid - + Non valido @@ -2092,7 +2137,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + Left Stick Levetta sinistra @@ -2186,14 +2231,14 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + L L - + ZL ZL @@ -2212,7 +2257,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + Plus Più @@ -2225,15 +2270,15 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + R R - + ZR ZR @@ -2290,231 +2335,236 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + Right Stick Levetta destra - - - - + + + + Clear Cancella - - - - - + + + + + [not set] [non impostato] - - - Toggle button - Premi il bottone - - - - + + Invert button - + Inverti pulsante - - + + + Toggle button + Premi il pulsante + + + + Invert axis Inverti assi - - - + + + Set threshold Imposta soglia - - + + Choose a value between 0% and 100% Scegli un valore compreso tra 0% e 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Mappa la levetta analogica - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Dopo aver premuto OK, prima muovi la levetta orizzontalmente, e poi verticalmente. Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalmente. - + Center axis - - + + Deadzone: %1% Zona morta: %1% - - + + Modifier Range: %1% Modifica raggio: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Due Joycon - + Left Joycon Joycon sinistro - + Right Joycon Joycon destro - + Handheld Portatile - + GameCube Controller Controller GameCube - + Poke Ball Plus Poké Ball Plus - + NES Controller Controller NES - + SNES Controller Controller SNES - + N64 Controller Controller N64 - + Sega Genesis Sega Genesis - + Start / Pause Avvia / Metti in pausa - + Z Z - + Control Stick Levetta di Controllo - + C-Stick Levetta C - + Shake! Scuoti! - + [waiting] [in attesa] - + New Profile Nuovo profilo - + Enter a profile name: Inserisci un nome profilo: - - + + Create Input Profile Crea un profilo di Input - + The given profile name is not valid! Il nome profilo dato non è valido! - + Failed to create the input profile "%1" Impossibile creare il profilo di input "%1" - + Delete Input Profile Elimina un profilo di Input - + Failed to delete the input profile "%1" Impossibile eliminare il profilo di input "%1" - + Load Input Profile Carica un profilo di input - + Failed to load the input profile "%1" Impossibile caricare il profilo di input "%1" - + Save Input Profile Salva un profilo di Input - + Failed to save the input profile "%1" Impossibile creare il profilo di input "%1" @@ -2769,42 +2819,42 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Sviluppatore - + Add-Ons Add-on - + General Generale - + System Sistema - + CPU CPU - + Graphics Grafica - + Adv. Graphics Grafiche Avanzate - + Audio Audio - + Properties Proprietà @@ -3516,47 +3566,47 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme <html><head/><body><p>Gli script vengono letti seguendo lo stesso formato degli script di TAS-nx.<br/>Per saperne di più, puoi consultare <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">questa pagina</span></a> sul sito di yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. IMPORTANTE: questa funzione è ancora in fase sperimentale.<br/>Gli script NON verranno riprodotti perfettamente. - + Settings Impostazioni - + Enable TAS features Attiva le funzioni di TASing - + Loop script - + Pause execution during loads - + Script Directory Cartella degli script - + Path Percorso - + ... ... @@ -3966,7 +4016,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab - + Verify Verifica @@ -4028,7 +4078,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Show Current Game in your Discord Status - Mostra il Gioco Attuale nel tuo Stato di Discord + Mostra il gioco attuale nel tuo stato di Discord @@ -4053,7 +4103,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab - + Unspecified Non specificato @@ -4068,17 +4118,36 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Il token non è stato verificato. La modifica al token non è stata salvata. - + + Unverified, please click Verify before saving configuration + Tooltip + Non verificato, clicca su "Verifica" prima di salvare la configurazione + + + + Verifying... Verifica in corso... - + + Verified + Tooltip + Verificato + + + + Verification failed + Tooltip + Verifica fallita + + + Verification failed Verifica fallita - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verifica fallita. Controlla di aver inserito il token correttamente, e che la tua connessione a internet sia funzionante. @@ -4147,12 +4216,12 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab DirectConnectWindow - + Connecting Connessione in corso - + Connect Connetti @@ -4160,494 +4229,494 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Vengono raccolti dati anonimi</a> per aiutarci a migliorare yuzu. <br/><br/>Desideri condividere i tuoi dati di utilizzo con noi? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Rilevata installazione di Vulkan non funzionante - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + L'inizializzazione di Vulkan è fallita durante l'avvio.<br><br>Clicca <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>qui per istruzioni su come risolvere il problema</a>. - + Loading Web Applet... Caricamento dell'applet web... - + Disable Web Applet Disabilita l'applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Il numero di shaders al momento in costruzione - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocità corrente dell'emulazione. Valori più alti o più bassi di 100% indicano che l'emulazione sta funzionando più velocemente o lentamente rispetto a una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quanti frame al secondo il gioco mostra attualmente. Questo varia da gioco a gioco e da situazione a situazione. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo utilizzato per emulare un frame della Switch, non contando i limiti ai frame o il v-sync. Per un'emulazione alla massima velocità, il valore dev'essere al massimo 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Cancella i file recenti - + &Continue &Continua - + &Pause &Pausa - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format - Avviso Formato di Gioco Obsoleto + Formato del gioco obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Stai usando una cartella con dentro una ROM decostruita come formato per avviare questo gioco, è un formato obsoleto ed è stato sostituito da altri come NCA, NAX, XCI o NSP. Le ROM decostruite non hanno icone, metadata e non supportano gli aggiornamenti. <br><br>Per una spiegazione sui vari formati di Switch che yuzu supporta, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>controlla la nostra wiki</a>. Questo messaggio non verrà più mostrato. - - + + Error while loading ROM! Errore nel caricamento della ROM! - + The ROM format is not supported. Il formato della ROM non è supportato. - + An error occurred initializing the video core. E' stato riscontrato un errore nell'inizializzazione del core video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Errore nel caricamento della ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Per favore segui <a href='https://yuzu-emu.org/help/quickstart/'>la guida rapida di yuzu</a> per rifare il dump dei file.<br>Puoi fare riferimento alla wiki di yuzu</a> o al canale Discord di yuzu</a> per aiuto. - + An unknown error occurred. Please see the log for more details. Si è verificato un errore sconosciuto. Visualizza il log per maggiori dettagli. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Dati di salvataggio - + Mod Data Dati delle mod - + Error Opening %1 Folder Errore nell'apertura della cartella %1 - - + + Folder does not exist! La cartella non esiste! - + Error Opening Transferable Shader Cache Errore nell'apertura della cache trasferibile degli shader - + Failed to create the shader cache directory for this title. Impossibile creare la cartella della cache degli shader per questo titolo. - + Contents Contenuti - + Update Aggiornamento - + DLC DLC - + Remove Entry Rimuovi voce - + Remove Installed Game %1? Vuoi rimuovere i %1 installati del gioco? - - - - - - + + + + + + Successfully Removed Rimosso con successo - + Successfully removed the installed base game. Rimosso con successo il gioco base installato - - - + + + Error Removing %1 Errore durante la rimozione %1 - + The base game is not installed in the NAND and cannot be removed. Il gioco base non è installato su NAND e non può essere rimosso. - + Successfully removed the installed update. Aggiornamento rimosso on successo. - + There is no update installed for this title. Non c'è alcun aggiornamento installato per questo gioco. - + There are no DLC installed for this title. Non c'è alcun DLC installato per questo gioco. - + Successfully removed %1 installed DLC. Rimossi con successo %1 DLC installati. - + Delete OpenGL Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader OpenGL? - + Delete Vulkan Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader Vulkan? - + Delete All Transferable Shader Caches? Vuoi rimuovere tutte le cache trasferibili degli shader? - + Remove Custom Game Configuration? Vuoi rimuovere la configurazione personalizzata del gioco? - + Remove File Rimuovi file - - + + Error Removing Transferable Shader Cache Errore nella rimozione della cache trasferibile degli shader - - + + A shader cache for this title does not exist. Per questo titolo non esiste una cache degli shader. - + Successfully removed the transferable shader cache. La cache trasferibile degli shader è stata rimossa con successo. - + Failed to remove the transferable shader cache. Impossibile rimuovere la cache trasferibile degli shader. - - + + Error Removing Transferable Shader Caches Errore nella rimozione delle cache trasferibili degli shader - + Successfully removed the transferable shader caches. Le cache trasferibili degli shader sono state rimosse con successo. - + Failed to remove the transferable shader cache directory. Impossibile rimuovere la cartella della cache trasferibile degli shader. - - + + Error Removing Custom Configuration Errore nella rimozione della configurazione personalizzata - + A custom configuration for this title does not exist. Non esiste una configurazione personalizzata per questo gioco. - + Successfully removed the custom game configuration. La configurazione personalizzata del gioco è stata rimossa con successo. - + Failed to remove the custom game configuration. Impossibile rimuovere la configurazione personalizzata del gioco. - - + + RomFS Extraction Failed! Estrazione RomFS fallita! - + There was an error copying the RomFS files or the user cancelled the operation. C'è stato un errore nella copia dei file del RomFS o l'operazione è stata annullata dall'utente. - + Full Completa - + Skeleton Cartelle - + Select RomFS Dump Mode Seleziona la modalità di estrazione della RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Seleziona come vorresti estrarre la RomFS. <br>La modalità Completa copierà tutti i file in una nuova cartella mentre<br>la modalità Cartelle creerà solamente le cartelle e le sottocartelle. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Estrazione RomFS in corso... - - + + Cancel Annulla - + RomFS Extraction Succeeded! Estrazione RomFS riuscita! - + The operation completed successfully. L'operazione è stata completata con successo. - + Error Opening %1 Errore nell'apertura di %1 - + Select Directory Seleziona cartella - + Properties Proprietà - + The game properties could not be loaded. Le proprietà del gioco non sono potute essere caricate. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Eseguibile Switch (%1);;Tutti i File (*.*) - + Load File Carica file - + Open Extracted ROM Directory Apri Cartella ROM Estratta - + Invalid Directory Selected Cartella selezionata non valida - + The directory you have selected does not contain a 'main' file. La cartella che hai selezionato non contiene un file "main". - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) File installabili Switch (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installa file - + %n file(s) remaining %n file rimanente%n file rimanenti%n file rimanenti - + Installing file "%1"... Installazione del file "%1"... - - + + Install Results Risultati dell'installazione - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Per evitare possibli conflitti, scoraggiamo gli utenti dall'installare giochi base su NAND. Per favore, usare questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten %n file è stato sovrascritto @@ -4656,418 +4725,398 @@ Per favore, usare questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) failed to install - + System Application Applicazione di sistema - + System Archive Archivio di sistema - + System Application Update Aggiornamento di un'applicazione di sistema - + Firmware Package (Type A) Pacchetto Firmware (Tipo A) - + Firmware Package (Type B) Pacchetto Firmware (Tipo B) - + Game Gioco - + Game Update Aggiornamento di gioco - + Game DLC DLC - + Delta Title Titolo Delta - + Select NCA Install Type... Seleziona il Tipo di Installazione NCA - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleziona il tipo del file NCA da installare: (Nella maggior parte dei casi, il predefinito 'Gioco' va bene.) - + Failed to Install Installazione fallita - + The title type you selected for the NCA is invalid. Il tipo che hai selezionato per l'NCA non è valido. - + File not found File non trovato - + File "%1" not found File "%1" non trovato - + OK OK - + Missing yuzu Account Account di yuzu non trovato - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Per segnalare la compatibilità di un gioco, devi collegare il tuo account yuzu. <br><br/>Per collegare il tuo account yuzu, vai su Emulazione &gt; Configurazione &gt; Web. - + Error opening URL Errore aprendo l'URL - + Unable to open the URL "%1". Impossibile aprire l'URL "% 1". - + TAS Recording - + Overwrite file of player 1? Vuoi sovrascrivere il file del giocatore 1? - + Invalid config detected Trovata configurazione invalida - + Handheld controller can't be used on docked mode. Pro controller will be selected. Il controller portatile non può essere utilizzato in modalità docked. Verrà selezionato il controller Pro. - - + + Error Errore - - + + The current game is not looking for amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'Amiibo corrente è stato rimosso - + Amiibo File (%1);; All Files (*.*) File Amiibo (%1);; Tutti I File (*.*) - + Load Amiibo Carica Amiibo - - Error opening Amiibo data file - Errore nell'apertura del file dati Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Impossibile aprire e leggere il file Amiibo "%1". - - - - Error reading Amiibo data file - Errore nella lettura dei dati del file Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Impossibile leggere tutti i dati dell'Amiibo. E' stato possibile leggere solamente %2 byte di %1. - - - + Error loading Amiibo data Errore nel caricamento dei dati dell'Amiibo. - + Unable to load Amiibo data. Impossibile caricare i dati dell'Amiibo. - + Capture Screenshot Cattura Screenshot - + PNG Image (*.png) Immagine PNG (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Avvia - + Stop R&ecording Interrompi r&egistrazione - + R&ecord R&egistra - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Velocità: %1% / %2% - + Speed: %1% Velocità: %1% - + Game: %1 FPS (Unlocked) Gioco: %1 FPS (Sbloccati) - + Game: %1 FPS Gioco: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMALE - + GPU HIGH GPU ALTA - + GPU EXTREME GPU ESTREMA - + GPU ERROR ERRORE GPU - + DOCKED - + HANDHELD PORTATILE - + NEAREST NEAREST - - + + BILINEAR BILINEARE - + BICUBIC BICUBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Il gioco che stai provando a caricare richiede ulteriori file che devono essere estratti dalla tua Switch prima di poter giocare. <br/><br/>Per maggiori informazioni sull'estrazione di questi file, visualizza la seguente pagina della wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Estrazione degli archivi di sistema e dei font condivisi da una console Switch</a>.<br/><br/>Vuoi uscire e tornare alla lista dei giochi? Proseguendo l'emulazione si potrebbero verificare arresti anomali, salvataggi corrotti o altri bug. - + yuzu was unable to locate a Switch system archive. %1 yuzu non ha potuto individuare un archivio di sistema della Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu non ha potuto individuare un archivio di sistema della Switch: %1. %2 - + System Archive Not Found Archivio di sistema non trovato - + System Archive Missing Archivio di sistema mancante - + yuzu was unable to locate the Switch shared fonts. %1 yuzu non ha potuto individuare i font condivisi della Switch. %1 - + Shared Fonts Not Found Font condivisi non trovati - + Shared Font Missing Font condivisi mancanti - + Fatal Error Errore Fatale - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu ha riscontrato un errore fatale, visualizza il log per maggiori dettagli. Per maggiori informazioni su come accedere al log, visualizza la seguente pagina: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Come caricare il file di log</a>.<br/><br/>Vuoi uscire e tornare alla lista dei giochi? Proseguendo l'emulazione si potrebbero verificare arresti anomali, salvataggi corrotti o altri bug. - + Fatal Error encountered Errore Fatale riscontrato - + Confirm Key Rederivation - Conferma Riderivazione Chiave + Conferma ri-derivazione chiavi - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5084,37 +5133,37 @@ e facoltativamente fai dei backup. Questo eliminerà i tuoi file di chiavi autogenerati e ri-avvierà il processo di derivazione delle chiavi. - + Missing fuses Fusi mancanti - + - Missing BOOT0 - BOOT0 mancante - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main mancante - + - Missing PRODINFO - PRODINFO mancante - + Derivation Components Missing Componenti di derivazione mancanti - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5123,39 +5172,39 @@ Questa operazione potrebbe durare fino a un minuto in base alle prestazioni del tuo sistema. - + Deriving Keys Derivazione chiavi - + Select RomFS Dump Target Seleziona Target dell'Estrazione del RomFS - + Please select which RomFS you would like to dump. Seleziona quale RomFS vorresti estrarre. - + Are you sure you want to close yuzu? Sei sicuro di voler chiudere yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Sei sicuro di voler arrestare l'emulazione? Tutti i progressi non salvati verranno perduti. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5284,7 +5333,7 @@ Desideri uscire comunque? Dump RomFS to SDMC - + Estrai RomFS su SDMC @@ -5416,8 +5465,8 @@ a causa della presenza di glitch anche utilizzando degli espedienti. Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen. - Il gioco è completamente ingiocabile a causa di errori gravi audio o video. È impossibile proseguire oltre la Schermata -Iniziale. + Il gioco è del tutto ingiocabile a causa di considerevoli glitch audio o video. +È impossibile proseguire oltre la schermata iniziale. @@ -5453,7 +5502,7 @@ Iniziale. %1 of %n result(s) - + %1 di %n risultato%1 di %n risultati%1 di %n risultati @@ -5537,12 +5586,12 @@ Iniziale. HostRoomWindow - + Error Errore - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Impossibile annunciare la stanza alla lobby pubblica. Per ospitare una stanza pubblicamente, devi avere un account yuzu valido configurato in Emulazione -> Configura -> Web. Se non desideri pubblicare una stanza nella lobby pubblica, seleziona Non in lista. @@ -5552,11 +5601,12 @@ Messaggio di debug: Hotkeys - + Audio Mute/Unmute + @@ -5578,112 +5628,111 @@ Messaggio di debug: - Main Window Finestra principale - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Cattura Screenshot - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Esci da yuzu + + + + Fullscreen + Schermo intero - Fullscreen - Schermo Intero - - - Load File Carica file - + Load/Remove Amiibo Carica/Rimuovi Amiibo - + Restart Emulation - + Riavvia l'emulazione - + Stop Emulation Arresta l'emulazione - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5798,42 +5847,42 @@ Messaggio di debug: Aggiorna lobby - + Password Required to Join Password richiesta per entrare - + Password: Password: - + Room Name Nome stanza - + Preferred Game Gioco preferito - + Host Host - + Players Giocatori - + Refreshing Aggiornamento in corso - + Refresh List Aggiorna lista @@ -6160,7 +6209,7 @@ Messaggio di debug: - + Connected Connesso @@ -6183,7 +6232,7 @@ Debug Message: Messaggio di debug: - + New Messages Received Nuovi messaggi ricevuti @@ -6288,22 +6337,39 @@ They may have left the room. Potrebbe aver abbandonato la stanza. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Esci dalla stanza - + You are about to close the room. Any network connections will be closed. Stai per chiudere la stanza. Ogni connessione di rete verrà chiusa. - + Disconnect Disconnetti - + You are about to leave the room. Any network connections will be closed. Stai per uscire dalla stanza. Ogni connessione di rete verrà chiusa. @@ -6311,7 +6377,7 @@ Potrebbe aver abbandonato la stanza. NetworkMessage::ErrorManager - + Error Errore @@ -6370,7 +6436,7 @@ p, li { white-space: pre-wrap; } %1 sta giocando a %2 - + Not playing a game Non in gioco @@ -6425,7 +6491,7 @@ p, li { white-space: pre-wrap; } - + [not set] [non impostato] @@ -6440,10 +6506,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Asse %1%2 @@ -6457,9 +6523,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [sconosciuto] @@ -6624,15 +6690,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6640,35 +6706,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Asse %3 - + %1%2Axis %3,%4,%5 - + %1%2Asse %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + %1%2Pulsante %3 - + [unused] [inutilizzato] @@ -6709,7 +6775,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/ja_JP.ts b/dist/languages/ja_JP.ts index 3c66d5da2c..b621cdbf22 100644 --- a/dist/languages/ja_JP.ts +++ b/dist/languages/ja_JP.ts @@ -82,7 +82,7 @@ p, li { white-space: pre-wrap; } Room Window - + ルームウインドウ @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } メッセージを送る - + Members メンバー - + %1 has joined %1 が参加しました - + %1 has left %1 が退出しました - + %1 has been kicked %1 はキックされました - + %1 has been banned %1 はBanされました - + %1 has been unbanned %1 はBan解除されました - + View Profile プロフィールを見る - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? ブロックすると、そのプレイヤーからのチャットメッセージが届かなくなります。<br><br>%1を本当にブロックしますか? - + Kick キック - + Ban Ban - + Kick Player プレイヤーをキック - + Are you sure you would like to <b>kick</b> %1? %1 を<b>キック</b>しますがよろしいですか? - + Ban Player プレイヤーをBan - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -180,7 +180,7 @@ This would ban both their forum username and their IP address. Room Window - + ルームウインドウ @@ -213,7 +213,7 @@ This would ban both their forum username and their IP address. %1 (%2/%3 members) - connected - + %1 (%2/%3 メンバー) - 接続済み @@ -380,7 +380,7 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + 赤外線カメラを設定 @@ -771,200 +771,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger デバッガ― - + Enable GDB Stub GDBスタブの有効化 - + Port: ポート: - + Logging ログ - + Global Log Filter グローバルログフィルター - + Show Log in Console コンソールにログを表示 - + Open Log Location ログ出力フォルダを開く - + When checked, the max size of the log increases from 100 MB to 1 GB チェックすると、ログの最大サイズが100MBから1GBに増加します。 - + Enable Extended Logging** 拡張ログの有効化** - + Homebrew Homebrew - + Arguments String 引数 - + Graphics グラフィック - + When checked, the graphics API enters a slower debugging mode チェックすると、 グラフィックAPIはより遅いデバッグモードになります。 - + Enable Graphics Debugging グラフィックデバッグの有効化 - + When checked, it enables Nsight Aftermath crash dumps チェックすると、Nsight Aftermathのクラッシュダンプが有効になります - + Enable Nsight Aftermath Nsight Aftermathの有効化 - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found チェックすると、ディスクシェーダーキャッシュまたはゲームからオリジナルのアセンブラーシェーダーをすべてダンプします - + Dump Game Shaders ゲームシェーダーをダンプ - + When checked, it will dump all the macro programs of the GPU チェックすると、GPUのすべてのマクロプログラムをダンプします - + Dump Maxwell Macros Maxwellマクロをダンプ - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower チェックすると、マクロのJust In Timeコンパイラが無効になります。有効にすると、ゲームの動作が遅くなります。 - + Disable Macro JIT Macro JITを無効化 - + When checked, yuzu will log statistics about the compiled pipeline cache チェックすると、コンパイルしたパイプラインキャッシュの統計情報をロギングします - + Enable Shader Feedback シェーダフィードバックの有効j化 - + When checked, it executes shaders without loop logic changes チェックすると、ループロジックを変更せずにシェーダーを実行します。 - + Disable Loop safety checks ループ安全性チェックの無効化 - + Debugging デバッグ - - Enable FS Access Log - FSアクセスログの有効化 - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - これを有効にすると、最新のオーディオコマンドリストがコンソールに出力されます。オーディオレンダラーを使用するゲームにのみ影響します。 - - - + Enable Verbose Reporting Services** 詳細なレポートサービスの有効化** - + + Enable FS Access Log + FSアクセスログの有効化 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + これを有効にすると、最新のオーディオコマンドリストがコンソールに出力されます。オーディオレンダラーを使用するゲームにのみ影響します。 + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + クラッシュ時にミニダンプを生成 + + + Advanced 高度 - + Kiosk (Quest) Mode Kiosk (Quest) Mode - + Enable CPU Debugging CPUデバッグの有効化 - + Enable Debug Asserts デバッグアサートの有効化 - + Enable Auto-Stub** 自動スタブの有効化** - + Enable All Controller Types すべてのコントローラタイプを有効にする - + Disable Web Applet Webアプレットの無効化 - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. ** yuzuを終了したときに自動的にリセットされます。 + + + Restart Required + 再起動が必要 + + + + yuzu is required to restart in order to apply this setting. + この設定を適用するには yuzu を再起動する必要があります. + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1583,37 +1618,47 @@ This would ban both their forum username and their IP address. 高速なGPUタイミングを有効化(ハック) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 悲観的なバッファフラッシュを有効にします. このオプションは, 変更されていないバッファを強制的にフラッシュさせるので, パフォーマンスが低下する可能性があります. + + + + Use pessimistic buffer flushes (Hack) + 悲観的なバッファフラッシュを使用 (ハック) + + + Anisotropic Filtering: 異方性フィルタリング: - + Automatic 自動 - + Default デフォルト - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2013,7 +2058,7 @@ This would ban both their forum username and their IP address. Infrared Camera - + 赤外線カメラ @@ -2105,7 +2150,7 @@ This would ban both their forum username and their IP address. - + Left Stick Lスティック @@ -2199,14 +2244,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2225,7 +2270,7 @@ This would ban both their forum username and their IP address. - + Plus + @@ -2238,15 +2283,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2303,231 +2348,236 @@ This would ban both their forum username and their IP address. - + Right Stick Rスティック - - - - + + + + Clear クリア - - - - - + + + + + [not set] [未設定] - - - Toggle button - - - - - + + Invert button ボタンを反転 - - + + + Toggle button + + + + + Invert axis 軸を反転 - - - + + + Set threshold しきい値を設定 - - + + Choose a value between 0% and 100% 0%から100%の間の値を選択してください - + + Toggle axis + + + + Set gyro threshold ジャイロのしきい値を設定 - + Map Analog Stick アナログスティックをマップ - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. OKを押した後、スティックを水平方向に動かし、次に垂直方向に動かしてください。 軸を反転させる場合、 最初に垂直方向に動かし、次に水平方向に動かしてください。 - + Center axis - - + + Deadzone: %1% デッドゾーン:%1% - - + + Modifier Range: %1% 変更範囲:%1% - - + + Pro Controller Proコントローラ - + Dual Joycons Joy-Con(L/R) - + Left Joycon Joy-Con(L) - + Right Joycon Joy-Con(R) - + Handheld 携帯モード - + GameCube Controller ゲームキューブコントローラ - + Poke Ball Plus モンスターボールプラス - + NES Controller ファミコン・コントローラー - + SNES Controller スーパーファミコン・コントローラー - + N64 Controller ニンテンドウ64・コントローラー - + Sega Genesis メガドライブ - + Start / Pause スタート/ ポーズ - + Z Z - + Control Stick - + C-Stick Cスティック - + Shake! 振ってください - + [waiting] [待機中] - + New Profile 新規プロファイル - + Enter a profile name: プロファイル名を入力: - - + + Create Input Profile 入力プロファイルを作成 - + The given profile name is not valid! プロファイル名が無効です! - + Failed to create the input profile "%1" 入力プロファイル "%1" の作成に失敗しました - + Delete Input Profile 入力プロファイルを削除 - + Failed to delete the input profile "%1" 入力プロファイル "%1" の削除に失敗しました - + Load Input Profile 入力プロファイルをロード - + Failed to load the input profile "%1" 入力プロファイル "%1" のロードに失敗しました - + Save Input Profile 入力プロファイルをセーブ - + Failed to save the input profile "%1" 入力プロファイル "%1" のセーブに失敗しました @@ -2565,7 +2615,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< UDP Calibration: - + UDP補正: @@ -2782,42 +2832,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 開発元 - + Add-Ons アドオン - + General 全般 - + System システム - + CPU CPU - + Graphics グラフィック - + Adv. Graphics 高度なグラフィック - + Audio サウンド - + Properties プロパティ @@ -3529,47 +3579,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>TAS-nxスクリプトと同じフォーマットでスクリプトからコントローラ入力を読み込みます。<br/>より詳細な説明は、yuzuホームページの<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">ヘルプ</span></a>を参照してください。</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). どのホットキーで再生/録音を制御するかは、ホットキーの設定(設定>一般>ホットキー)を参照してください。 - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 警告:実験的な機能です。<br/>現状では完全な再生や同期はできません。 - + Settings 設定 - + Enable TAS features TAS機能の有効化 - + Loop script スクリプトを繰り返し実行 - + Pause execution during loads ロード中は実行を一時停止 - + Script Directory スクリプトディレクトリ - + Path パス - + ... ... @@ -3979,7 +4029,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 検証 @@ -4066,7 +4116,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 未設定 @@ -4081,17 +4131,36 @@ Drag points to change position, or double-click table cells to edit values.トークンは検証されていません。トークンの変更はまだ保存されていません。 - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... 検証中... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + 検証に失敗 + + + Verification failed 検証に失敗 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 検証に失敗しました。トークンが正しく入力されていること、およびインターネット接続が機能していることを確認してください。 @@ -4114,7 +4183,7 @@ Drag points to change position, or double-click table cells to edit values. Direct Connect - + ダイレクト接続 @@ -4144,7 +4213,7 @@ Drag points to change position, or double-click table cells to edit values. Nickname - + ニックネーム @@ -4160,12 +4229,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting 接続中 - + Connect 接続 @@ -4173,913 +4242,893 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? yuzuを改善するための<a href='https://yuzu-emu.org/help/feature/telemetry/'>匿名データが収集されました</a>。<br/><br/>統計情報データを共有しますか? - + Telemetry テレメトリ - + Broken Vulkan Installation Detected 壊れたVulkanのインストールが検出されました。 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. ブート時にVulkanの初期化に失敗しました。<br><br>この問題を解決するための手順は<a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>こちら</a>。 - + Loading Web Applet... Webアプレットをロード中... - + Disable Web Applet Webアプレットの無効化 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Webアプレットを無効にすると、未定義の動作になる可能性があるため、スーパーマリオ3Dオールスターズでのみ使用するようにしてください。本当にWebアプレットを無効化しますか? (デバッグ設定で再度有効にすることができます)。 - + The amount of shaders currently being built ビルド中のシェーダー数 - + The current selected resolution scaling multiplier. 現在選択されている解像度の倍率。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 現在のエミュレーション速度。値が100%より高いか低い場合、エミュレーション速度がSwitchより速いか遅いことを示します。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. ゲームが現在表示している1秒あたりのフレーム数。これはゲームごと、シーンごとに異なります。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Switchフレームをエミュレートするのにかかる時間で、フレームリミットやV-Syncは含まれません。フルスピードエミュレーションの場合、最大で16.67ミリ秒になります。 - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files 最近のファイルをクリア(&C) - + &Continue 再開(&C) - + &Pause 中断(&P) - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzuはゲームを起動しています - + Warning Outdated Game Format 古いゲームフォーマットの警告 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. このゲームでは、分解されたROMディレクトリフォーマットを使用しています。これは、NCA、NAX、XCI、またはNSPなどに取って代わられた古いフォーマットです。分解されたROMディレクトリには、アイコン、メタデータ、およびアップデートサポートがありません。<br><br>yuzuがサポートするSwitchフォーマットの説明については、<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>wikiをチェックしてください</a>。このメッセージは二度と表示されません。 - - + + Error while loading ROM! ROMロード中にエラーが発生しました! - + The ROM format is not supported. このROMフォーマットはサポートされていません。 - + An error occurred initializing the video core. ビデオコア初期化中にエラーが発生しました。 - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzuは、ビデオコアの実行中にエラーが発生しました。これは通常、内蔵GPUも含め、古いGPUドライバが原因です。詳しくはログをご覧ください。ログへのアクセス方法については、以下のページをご覧ください:<a href='https://yuzu-emu.org/help/reference/log-files/'>ログファイルのアップロード方法について</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. ROMのロード中にエラー! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br><a href='https://yuzu-emu.org/help/quickstart/'>yuzuクイックスタートガイド</a>を参照してファイルを再ダンプしてください。<br>またはyuzu wiki及び</a>yuzu Discord</a>を参照するとよいでしょう。 - + An unknown error occurred. Please see the log for more details. 不明なエラーが発生しました。詳細はログを確認して下さい。 - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data データのセーブ - + Mod Data Modデータ - + Error Opening %1 Folder ”%1”フォルダを開けませんでした - - + + Folder does not exist! フォルダが存在しません! - + Error Opening Transferable Shader Cache シェーダキャッシュを開けませんでした - + Failed to create the shader cache directory for this title. このタイトル用のシェーダキャッシュディレクトリの作成に失敗しました - + Contents コンテンツ - + Update アップデート - + DLC DLC - + Remove Entry エントリ削除 - + Remove Installed Game %1? インストールされているゲーム%1を削除しますか? - - - - - - + + + + + + Successfully Removed 削除しました - + Successfully removed the installed base game. インストールされたゲームを正常に削除しました。 - - - + + + Error Removing %1 %1削除エラー - + The base game is not installed in the NAND and cannot be removed. ゲームはNANDにインストールされていないため、削除できません。 - + Successfully removed the installed update. インストールされたアップデートを正常に削除しました。 - + There is no update installed for this title. このタイトルのアップデートはインストールされていません。 - + There are no DLC installed for this title. このタイトルにはDLCがインストールされていません。 - + Successfully removed %1 installed DLC. %1にインストールされたDLCを正常に削除しました。 - + Delete OpenGL Transferable Shader Cache? 転送可能なOpenGLシェーダキャッシュを削除しますか? - + Delete Vulkan Transferable Shader Cache? 転送可能なVulkanシェーダキャッシュを削除しますか? - + Delete All Transferable Shader Caches? 転送可能なすべてのシェーダキャッシュを削除しますか? - + Remove Custom Game Configuration? このタイトルのカスタム設定を削除しますか? - + Remove File ファイル削除 - - + + Error Removing Transferable Shader Cache 転送可能なシェーダーキャッシュの削除エラー - - + + A shader cache for this title does not exist. このタイトル用のシェーダキャッシュは存在しません。 - + Successfully removed the transferable shader cache. 転送可能なシェーダーキャッシュが正常に削除されました。 - + Failed to remove the transferable shader cache. 転送可能なシェーダーキャッシュを削除できませんでした。 - - + + Error Removing Transferable Shader Caches 転送可能なシェーダキャッシュの削除エラー - + Successfully removed the transferable shader caches. 転送可能なシェーダキャッシュを正常に削除しました。 - + Failed to remove the transferable shader cache directory. 転送可能なシェーダキャッシュディレクトリの削除に失敗しました。 - - + + Error Removing Custom Configuration カスタム設定の削除エラー - + A custom configuration for this title does not exist. このタイトルのカスタム設定は存在しません。 - + Successfully removed the custom game configuration. カスタム設定を正常に削除しました。 - + Failed to remove the custom game configuration. カスタム設定の削除に失敗しました。 - - + + RomFS Extraction Failed! RomFSの解析に失敗しました! - + There was an error copying the RomFS files or the user cancelled the operation. RomFSファイルをコピー中にエラーが発生したか、ユーザー操作によりキャンセルされました。 - + Full フル - + Skeleton スケルトン - + Select RomFS Dump Mode RomFSダンプモードの選択 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. RomFSのダンプ方法を選択してください。<br>”完全”はすべてのファイルが新しいディレクトリにコピーされます。<br>”スケルトン”はディレクトリ構造を作成するだけです。 - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 に RomFS を展開するための十分な空き領域がありません。Emulation > Configure > System > Filesystem > Dump Root で、空き容量を確保するか、別のダンプディレクトリを選択してください。 - + Extracting RomFS... RomFSを解析中... - - + + Cancel キャンセル - + RomFS Extraction Succeeded! RomFS解析成功! - + The operation completed successfully. 操作は成功しました。 - + Error Opening %1 ”%1”を開けませんでした - + Select Directory ディレクトリの選択 - + Properties プロパティ - + The game properties could not be loaded. ゲームプロパティをロード出来ませんでした。 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch実行ファイル (%1);;すべてのファイル (*.*) - + Load File ファイルのロード - + Open Extracted ROM Directory 展開されているROMディレクトリを開く - + Invalid Directory Selected 無効なディレクトリが選択されました - + The directory you have selected does not contain a 'main' file. 選択されたディレクトリに”main”ファイルが見つかりませんでした。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) インストール可能なスイッチファイル (*.nca *.nsp *.xci);;任天堂コンテンツアーカイブ (*.nca);;任天堂サブミッションパッケージ (*.nsp);;NXカートリッジイメージ (*.xci) - + Install Files ファイルのインストール - + %n file(s) remaining 残り %n ファイル - + Installing file "%1"... "%1"ファイルをインストールしています・・・ - - + + Install Results インストール結果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 競合を避けるため、NANDにゲーム本体をインストールすることはお勧めしません。 この機能は、アップデートやDLCのインストールにのみ使用してください。 - + %n file(s) were newly installed %n ファイルが新たにインストールされました - + %n file(s) were overwritten %n ファイルが上書きされました - + %n file(s) failed to install %n ファイルのインストールに失敗しました - + System Application システムアプリケーション - + System Archive システムアーカイブ - + System Application Update システムアプリケーションアップデート - + Firmware Package (Type A) ファームウェアパッケージ(Type A) - + Firmware Package (Type B) ファームウェアパッケージ(Type B) - + Game ゲーム - + Game Update ゲームアップデート - + Game DLC ゲームDLC - + Delta Title 差分タイトル - + Select NCA Install Type... NCAインストール種別を選択・・・ - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) インストールするNCAタイトル種別を選択して下さい: (ほとんどの場合、デフォルトの”ゲーム”で問題ありません。) - + Failed to Install インストール失敗 - + The title type you selected for the NCA is invalid. 選択されたNCAのタイトル種別が無効です。 - + File not found ファイルが存在しません - + File "%1" not found ファイル”%1”が存在しません - + OK OK - + Missing yuzu Account yuzuアカウントが存在しません - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. ゲームの互換性テストケースを送信するには、yuzuアカウントをリンクする必要があります。<br><br/>yuzuアカウントをリンクするには、エミュレーション > 設定 > Web から行います。 - + Error opening URL URLオープンエラー - + Unable to open the URL "%1". URL"%1"を開けません。 - + TAS Recording TAS 記録中 - + Overwrite file of player 1? プレイヤー1のファイルを上書きしますか? - + Invalid config detected 無効な設定を検出しました - + Handheld controller can't be used on docked mode. Pro controller will be selected. 携帯コントローラはドックモードで使用できないため、Proコントローラが選択されます。 - - + + Error エラー - - + + The current game is not looking for amiibos 現在のゲームはamiiboを要求しません - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + 現在の amiibo は削除されました - + Amiibo File (%1);; All Files (*.*) amiiboファイル (%1);;すべてのファイル (*.*) - + Load Amiibo amiiboのロード - - Error opening Amiibo data file - amiiboデータファイルを開けませんでした - - - - Unable to open Amiibo file "%1" for reading. - amiiboデータファイル”%1”を読み込めませんでした。 - - - - Error reading Amiibo data file - amiiboデータファイルを読み込み中にエラーが発生した - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - amiiboデータを完全には読み取ることができませんでした。%1バイトを読み込もうとしましたが、%2バイトしか読み取れませんでした。 - - - + Error loading Amiibo data amiiboデータ読み込み中にエラーが発生しました - + Unable to load Amiibo data. amiiboデータをロードできませんでした。 - + Capture Screenshot スクリーンショットのキャプチャ - + PNG Image (*.png) PNG画像 (*.png) - + TAS state: Running %1/%2 TAS 状態: 実行中 %1/%2 - + TAS state: Recording %1 TAS 状態: 記録中 %1 - + TAS state: Idle %1/%2 TAS 状態: アイドル %1/%2 - + TAS State: Invalid TAS 状態: 無効 - + &Stop Running 実行停止(&S) - + &Start 実行(&S) - + Stop R&ecording 記録停止(&R) - + R&ecord 記録(&R) - + Building: %n shader(s) 構築中: %n シェーダー - + Scale: %1x %1 is the resolution scaling factor 拡大率: %1x - + Speed: %1% / %2% 速度:%1% / %2% - + Speed: %1% 速度:%1% - + Game: %1 FPS (Unlocked) Game: %1 FPS(制限解除) - + Game: %1 FPS ゲーム:%1 FPS - + Frame: %1 ms フレーム:%1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HIGH - + GPU EXTREME GPU EXTREME - + GPU ERROR GPU ERROR - + DOCKED DOCKED - + HANDHELD HANDHELD - + NEAREST NEAREST - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. ロードしようとしているゲームはプレイする前に、追加のファイルを必要とします。それはSwitchからダンプする必要があります。<br/><br/>これらのファイルのダンプの詳細については、次のWikiページを参照してください:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>スイッチコンソールからのシステムアーカイブと共有フォントをダンプする</a>。<br/><br/>ゲームリストに戻りますか?エミュレーションを続けると、クラッシュ、保存データの破損、またはその他のバグが発生する可能性があります。 - + yuzu was unable to locate a Switch system archive. %1 yuzuはSwitchのシステムアーカイブ "%1" を見つけられませんでした。 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzuはSwitchのシステムアーカイブ "%1" "%2" を見つけられませんでした。 - + System Archive Not Found システムアーカイブが見つかりません - + System Archive Missing システムアーカイブが見つかりません - + yuzu was unable to locate the Switch shared fonts. %1 yuzuはSwitchの共有フォント "%1" を見つけられませんでした。 - + Shared Fonts Not Found 共有フォントが存在しません - + Shared Font Missing 共有フォントが存在しません - + Fatal Error 致命的なエラー - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzuが致命的なエラーを検出しました。詳細については、ログを参照してください。ログへのアクセスの詳細については、次のページを参照してください。<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>ログファイルをアップロードする方法</a>。<br/><br/>ゲームリストに戻りますか?エミュレーションを続けると、クラッシュ、保存データの破損、またはその他のバグが発生する可能性があります。 - + Fatal Error encountered 致命的なエラー発生 - + Confirm Key Rederivation キーの再取得確認 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5096,37 +5145,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 実行すると、自動生成された鍵ファイルが削除され、鍵生成モジュールが再実行されます。 - + Missing fuses ヒューズがありません - + - Missing BOOT0 - BOOT0がありません - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Mainがありません - + - Missing PRODINFO - PRODINFOがありません - + Derivation Components Missing 派生コンポーネントがありません - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 暗号化キーがありません。<br>キー、ファームウェア、ゲームを取得するには<a href='https://yuzu-emu.org/help/quickstart/'>yuzu クイックスタートガイド</a>を参照ください。<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5135,39 +5184,39 @@ on your system's performance. 1分以上かかります。 - + Deriving Keys 派生キー - + Select RomFS Dump Target RomFSダンプターゲットの選択 - + Please select which RomFS you would like to dump. ダンプしたいRomFSを選択して下さい。 - + Are you sure you want to close yuzu? yuzuを終了しますか? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. エミュレーションを停止しますか?セーブされていない進行状況は失われます。 - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5493,7 +5542,7 @@ Screen. Max Players - + 最大プレイヤー数 @@ -5538,18 +5587,18 @@ Screen. Host Room - + ホストルーム HostRoomWindow - + Error エラー - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 公開ロビーへの部屋のアナウンスに失敗しました。部屋を公開するためには、Emulation -> Configure -> Web で有効なyuzuアカウントが設定されている必要があります。もし、部屋を公開ロビーに公開したくないのであれば、代わりに非公開を選択してください。 @@ -5559,11 +5608,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute 音声ミュート/解除 + @@ -5585,114 +5635,113 @@ Debug Message: - Main Window メイン画面 - + Audio Volume Down 音量を下げる - + Audio Volume Up 音量を上げる - + Capture Screenshot スクリーンショットを撮る - + Change Adapting Filter アダプティングフィルターの変更 - + Change Docked Mode ドックモードを変更 - + Change GPU Accuracy GPU精度を変更 - + Continue/Pause Emulation エミュレーションの一時停止/再開 - + Exit Fullscreen フルスクリーンをやめる - + Exit yuzu yuzuを終了 - + Fullscreen フルスクリーン - + Load File ファイルのロード - + Load/Remove Amiibo 読み込み/解除 Amiibo - + Restart Emulation エミュレーションをリスタート - + Stop Emulation エミュレーションをやめる - + TAS Record TAS 記録 - + TAS Reset TAS リセット - + TAS Start/Stop TAS 開始/停止 - + Toggle Filter Bar - + フィルタバー切り替え + + + + Toggle Framerate Limit + フレームレート制限切り替え - Toggle Framerate Limit - - - - Toggle Mouse Panning - + Toggle Status Bar - + ステータスバー切り替え @@ -5771,13 +5820,13 @@ Debug Message: Public Room Browser - + 公開ルームブラウザ Nickname - + ニックネーム @@ -5805,42 +5854,42 @@ Debug Message: ロビー更新 - + Password Required to Join 参加にはパスワードが必要です。 - + Password: パスワード: - + Room Name ルーム名 - + Preferred Game - + Host ホスト - + Players プレイヤー - + Refreshing 更新中 - + Refresh List リスト更新 @@ -6035,7 +6084,7 @@ Debug Message: Load/Remove &Amiibo... - + &Amiibo をロード/削除... @@ -6070,7 +6119,7 @@ Debug Message: &Configure TAS... - TASを設定... (%C) + TASを設定... (&C) @@ -6137,7 +6186,7 @@ Debug Message: Forum Username - + フォーラムのユーザ名 @@ -6167,7 +6216,7 @@ Debug Message: - + Connected 接続の状態 @@ -6175,7 +6224,7 @@ Debug Message: Not Connected - + 未接続 @@ -6190,7 +6239,7 @@ Debug Message: デバッグメッセージ: - + New Messages Received 新たなメッセージを受信しました @@ -6225,7 +6274,7 @@ Debug Message: You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + ルームをホスティングするには, 優先ゲームを選択する必要があります. リストにまだゲームがない場合は, リストのプラスアイコンをクリックしてゲームフォルダを追加してください. @@ -6295,22 +6344,41 @@ They may have left the room. 退室した可能性があります。 - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + ネットワークインタフェースが選択されていません. +設定 -> システム -> ネットワーク で使用するネットワークを選択してください. + + + + Game already running + ゲーム進行中 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + ゲーム進行中にルームに参加することはお勧めしません. ルーム機能が正しく動作しない原因になります. +とにかく実行しますか? + + + Leave Room ルームを離れる - + You are about to close the room. Any network connections will be closed. 部屋を閉じようとしています。ネットワーク接続がすべて終了します。 - + Disconnect 切断 - + You are about to leave the room. Any network connections will be closed. 部屋を退出しようとしています。ネットワーク接続はすべて終了します。 @@ -6318,7 +6386,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error エラー @@ -6377,7 +6445,7 @@ p, li { white-space: pre-wrap; } %1は%2をプレイ中です - + Not playing a game @@ -6432,7 +6500,7 @@ p, li { white-space: pre-wrap; } - + [not set] [未設定] @@ -6447,10 +6515,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 軸 %1%2 @@ -6464,9 +6532,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [不明] @@ -6631,15 +6699,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [無効] - - + + %1%2Hat %3 @@ -6647,35 +6715,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 %1%2ボタン %3 - + [unused] [未使用] @@ -6693,7 +6761,7 @@ p, li { white-space: pre-wrap; } Wheel Indicates the mouse wheel - + ホイール @@ -6716,7 +6784,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 %1%2%3 diff --git a/dist/languages/ko_KR.ts b/dist/languages/ko_KR.ts index 9dcae2b885..4d3167febc 100644 --- a/dist/languages/ko_KR.ts +++ b/dist/languages/ko_KR.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">웹사이트</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">소스 코드</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">기여자</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">라이센스</span></a></p></body></html> @@ -82,95 +82,95 @@ p, li { white-space: pre-wrap; } Room Window - + 방의 창 Send Chat Message - + 채팅 메시지 보내기 Send Message - + 메시지 보내기 - + Members - + 회원 - + %1 has joined - + %1이(가) 참여하였습니다 - + %1 has left - + %1이(가) 떠났습니다 - + %1 has been kicked - + %1이(가) 추방되었습니다 - + %1 has been banned - + %1이(가) 차단되었습니다 - + %1 has been unbanned - - - - - View Profile - + %1이(가) 차단 해제되었습니다 - - Block Player - + View Profile + 프로필 보기 - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + + + Block Player + 차단된 플레이어 + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + 플레이어를 차단하면 더 이상 채팅 메시지를 받을 수 없습니다.<br><br>%1을(를) 차단하겠습니까? + + + Kick - - - - - Ban - - - - - Kick Player - - - - - Are you sure you would like to <b>kick</b> %1? - + 추방 - Ban Player - + Ban + 차단 - + + Kick Player + 추방된 플레이어 + + + + Are you sure you would like to <b>kick</b> %1? + %1을(를) <b>추방</b>하겠습니까? + + + + Ban Player + 차단된 플레이어 + + + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + %1을(를) <b>추방</b>하겠습니까? 이렇게 하면 포럼 사용자 이름과 IP 주소가 모두 금지됩니다. @@ -178,22 +178,22 @@ This would ban both their forum username and their IP address. Room Window - + 방의 창 Room Description - + 방 설명 Moderation... - + 조정... Leave Room - + 방 나가기 @@ -206,12 +206,12 @@ This would ban both their forum username and their IP address. Disconnected - + 연결 끊김 %1 (%2/%3 members) - connected - + %1 (%2/%3 회원) - 연결됨 @@ -339,7 +339,7 @@ This would ban both their forum username and their IP address. Output Device - + 출력 장치 @@ -378,37 +378,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + 적외선 카메라 구성 Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + 에뮬레이트된 카메라의 이미지 출처를 선택합니다. 가상 카메라일 수도 있고 실제 카메라일 수도 있습니다. Camera Image Source: - + 카메라 이미지 출처: Input device: - + 입력 장치: Preview - + 미리보기 Resolution: 320*240 - + 해상도: 320*240 Click to preview - + 클릭하여 미리보기 @@ -768,200 +768,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger 디버거 - + Enable GDB Stub GDB Stub 활성화 - + Port: 포트: - + Logging 로깅 - + Global Log Filter 전역 로그 필터 - + Show Log in Console 콘솔에 로그 표시 - + Open Log Location 로그 경로 열기 - + When checked, the max size of the log increases from 100 MB to 1 GB 이 옵션을 활성화 시, 로그 파일의 최대 용량이 100MB에서 1GB로 증가합니다. - + Enable Extended Logging** 확장된 로깅 활성화** - + Homebrew 홈브류 - + Arguments String 실행인수 - + Graphics 그래픽 - + When checked, the graphics API enters a slower debugging mode 이 옵션을 활성화 시, 그래픽 API가 디버깅 모드로 진입하여 속도가 느려집니다. - + Enable Graphics Debugging 그래픽 디버깅 활성화 - + When checked, it enables Nsight Aftermath crash dumps 선택하면 Nsight Aftermath 크래시 덤프가 활성화됩니다. - + Enable Nsight Aftermath Nsight Aftermath 활성화 - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found 선택하면 디스크 셰이더 캐시 또는 게임에서 찾은 모든 원본 어셈블러 셰이더를 덤프합니다. - + Dump Game Shaders 게임 셰이더 덤프 - + When checked, it will dump all the macro programs of the GPU 체크하면 GPU의 모든 매크로 프로그램을 덤프합니다. - + Dump Maxwell Macros Maxwell 매크로 덤프 - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower 이 옵션에 체크할 시, 매크로 JIT 컴파일러를 비활성화 합니다. 게임 속도가 느려지니 유의하십시오. - + Disable Macro JIT Macro JIT 비활성화 - + When checked, yuzu will log statistics about the compiled pipeline cache 선택하면 yuzu는 컴파일된 파이프라인 캐시에 대한 통계를 기록합니다. - + Enable Shader Feedback 셰이더 피드백 활성화 - + When checked, it executes shaders without loop logic changes 체크 시 루프 로직 변경 없이 셰이더 실행 - + Disable Loop safety checks 루프 안전 검사 비활성화 - + Debugging 디버깅 - - Enable FS Access Log - FS 액세스 로그 활성화 - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** 자세한 리포팅 서비스 활성화** - + + Enable FS Access Log + FS 액세스 로그 활성화 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + 이 옵션을 활성화하면 가장 최근에 생성된 오디오 명령어 목록을 콘솔에 출력할 수 있습니다. 오디오 렌더러를 사용하는 게임에만 영향을 줍니다. + + + + Dump Audio Commands To Console** + 콘솔에 오디오 명령어 덤프 + + + + Create Minidump After Crash + 충돌후 미니덤프 생성 + + + Advanced 고급 - + Kiosk (Quest) Mode Kiosk (Quest) 모드 - + Enable CPU Debugging CPU 디버깅 활성화 - + Enable Debug Asserts 디버그 에러 검출 활성화 - + Enable Auto-Stub** 자동 스텁 활성화** - + Enable All Controller Types 모든 컨트롤러 유형 활성화 - + Disable Web Applet 웹 애플릿 비활성화 - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Yuzu가 종료되면 자동으로 재설정됩니다. + + + Restart Required + 재시작 필요 + + + + yuzu is required to restart in order to apply this setting. + 이 설정을 적용하려면 yuzu를 다시 시작해야 합니다. + + + + Web applet not compiled + 웹 애플릿이 컴파일되지 않음 + + + + MiniDump creation not compiled + MiniDump 생성이 컴파일되지 않음 + ConfigureDebugController @@ -1557,7 +1592,7 @@ This would ban both their forum username and their IP address. Use VSync - + 수직 동기화 사용 @@ -1580,37 +1615,47 @@ This would ban both their forum username and their IP address. 빠른 GPU 시간 사용(Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 비관적 버퍼 플러시를 활성화합니다. 이 옵션은 수정되지 않은 버퍼를 강제로 비우므로 성능이 저하될 수 있습니다. + + + + Use pessimistic buffer flushes (Hack) + 비관적 버퍼 플러시 사용(Hack) + + + Anisotropic Filtering: 비등방성 필터링: - + Automatic 자동 - + Default 기본값 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2010,7 +2055,7 @@ This would ban both their forum username and their IP address. Infrared Camera - + 적외선 카메라 @@ -2102,7 +2147,7 @@ This would ban both their forum username and their IP address. - + Left Stick L 스틱 @@ -2196,14 +2241,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2222,7 +2267,7 @@ This would ban both their forum username and their IP address. - + Plus + @@ -2235,15 +2280,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2300,231 +2345,236 @@ This would ban both their forum username and their IP address. - + Right Stick R 스틱 - - - - + + + + Clear 초기화 - - - - - + + + + + [not set] [설정 안 됨] - - - Toggle button - 토글 버튼 - - - - + + Invert button 버튼 반전 - - + + + Toggle button + 토글 버튼 + + + + Invert axis 축 뒤집기 - - - + + + Set threshold 임계값 설정 - - + + Choose a value between 0% and 100% 0%에서 100% 안의 값을 고르세요 - + + Toggle axis + axis 토글 + + + Set gyro threshold 자이로 임계값 설정 - + Map Analog Stick 아날로그 스틱 맵핑 - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. OK 버튼을 누른 후에 먼저 조이스틱을 수평으로 움직이고, 그 다음 수직으로 움직이세요. 축을 뒤집으려면 수직으로 먼저 움직인 뒤에 수평으로 움직이세요. - + Center axis 중심축 - - + + Deadzone: %1% 데드존: %1% - - + + Modifier Range: %1% 수정자 범위: %1% - - + + Pro Controller 프로 컨트롤러 - + Dual Joycons 듀얼 조이콘 - + Left Joycon 왼쪽 조이콘 - + Right Joycon 오른쪽 조이콘 - + Handheld 휴대 모드 - + GameCube Controller GameCube 컨트롤러 - + Poke Ball Plus 몬스터볼 Plus - + NES Controller NES 컨트롤러 - + SNES Controller SNES 컨트롤러 - + N64 Controller N64 컨트롤러 - + Sega Genesis 세가 제네시스 - + Start / Pause 시작 / 일시중지 - + Z Z - + Control Stick 컨트롤 스틱 - + C-Stick C-Stick - + Shake! 흔드세요! - + [waiting] [대기중] - + New Profile 새 프로필 - + Enter a profile name: 프로필 이름을 입력하세요: - - + + Create Input Profile 입력 프로필 생성 - + The given profile name is not valid! 해당 프로필 이름은 사용할 수 없습니다! - + Failed to create the input profile "%1" "%1" 입력 프로필 생성 실패 - + Delete Input Profile 입력 프로필 삭제 - + Failed to delete the input profile "%1" "%1" 입력 프로필 삭제 실패 - + Load Input Profile 입력 프로필 불러오기 - + Failed to load the input profile "%1" "%1" 입력 프로필 불러오기 실패 - + Save Input Profile 입력 프로필 저장 - + Failed to save the input profile "%1" "%1" 입력 프로필 저장 실패 @@ -2779,42 +2829,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 개발자 - + Add-Ons 부가 기능 - + General 일반 - + System 시스템 - + CPU CPU - + Graphics 그래픽 - + Adv. Graphics 고급 그래픽 - + Audio 오디오 - + Properties 속성 @@ -3526,47 +3576,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>TAS-nx 스크립트와 동일한 형식의 스크립트에서 컨트롤러 입력을 읽습니다.<br/>더 자세한 설명은 yuzu 웹사이트에 있는 <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a>를 참조하세요.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). 재생/녹화를 제어하는 ​​단축키를 확인하려면 단축키 설정(설정 -> 일반 -> 단축키)을 참조하십시오. - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 경고: 이것은 실험적인 기능입니다.<br/>현재의 불완전한 동기화 방법으로는 스크립트 프레임을 완벽하게 재생하지 않습니다. - + Settings 설정 - + Enable TAS features TAS 기능 활성화 - + Loop script 반복 스크립트 - + Pause execution during loads 로드 중 실행 일시중지 - + Script Directory 스크립트 주소 - + Path 주소 - + ... ... @@ -3976,7 +4026,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 인증 @@ -4003,7 +4053,7 @@ Drag points to change position, or double-click table cells to edit values. Web Service configuration can only be changed when a public room isn't being hosted. - + 웹 서비스 구성은 공개 방이 호스팅되지 않을 때만 변경할 수 있습니다. @@ -4063,7 +4113,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 불특정 @@ -4078,17 +4128,36 @@ Drag points to change position, or double-click table cells to edit values.토큰이 확인되지 않았습니다. 토큰 변경 사항이 저장되지 않을 것입니다. - + + Unverified, please click Verify before saving configuration + Tooltip + 인증되지 않음, 구성을 저장하기 전에 인증을 클릭하십시오. + + + + Verifying... 인증 중... - + + Verified + Tooltip + 인증됨 + + + + Verification failed + Tooltip + 인증 실패 + + + Verification failed 인증 실패 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 인증 실패. 토큰을 올바르게 입력했는지, 그리고 인터넷이 연결되어 있는지 확인하십시오. @@ -4111,972 +4180,952 @@ Drag points to change position, or double-click table cells to edit values. Direct Connect - + 직접 연결 IP Address - + IP 주소 IP - + IP <html><head/><body><p>IPv4 address of the host</p></body></html> - + <html><head/><body><p>호스트의 IPv4 주소</p></body></html> Port - + 포트 <html><head/><body><p>Port number the host is listening on</p></body></html> - + <html><head/><body><p>호스트가 수신 대기 중인 포트 번호</p></body></html> Nickname - + 별명 Password - + 비밀번호 Connect - + 연결 DirectConnectWindow - + Connecting - + 연결중 - + Connect - + 연결 GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? yuzu를 개선하기 위해 <a href='https://yuzu-emu.org/help/feature/telemetry/'>익명 데이터가 수집됩니다.</a> <br/><br/>사용 데이터를 공유하시겠습니까? - + Telemetry 원격 측정 - + Broken Vulkan Installation Detected 망가진 Vulkan 설치 감지됨 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + 부팅하는 동안 Vulkan 초기화에 실패했습니다.<br><br>문제 해결 지침을 보려면 <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>여기</a>를 클릭하세요. - + Loading Web Applet... 웹 애플릿을 로드하는 중... - + Disable Web Applet 웹 애플릿 비활성화 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 웹 애플릿을 비활성화하면 정의되지 않은 동작이 발생할 수 있으며 Super Mario 3D All-Stars에서만 사용해야 합니다. 웹 애플릿을 비활성화하시겠습니까? (디버그 설정에서 다시 활성화할 수 있습니다.) - + The amount of shaders currently being built 현재 생성중인 셰이더의 양 - + The current selected resolution scaling multiplier. 현재 선택된 해상도 배율입니다. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 현재 에뮬레이션 속도. 100%보다 높거나 낮은 값은 에뮬레이션이 Switch보다 빠르거나 느린 것을 나타냅니다. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 게임이 현재 표시하고 있는 초당 프레임 수입니다. 이것은 게임마다 다르고 장면마다 다릅니다. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 프레임 제한이나 수직 동기화를 계산하지 않고 Switch 프레임을 에뮬레이션 하는 데 걸린 시간. 최대 속도로 에뮬레이트 중일 때에는 대부분 16.67 ms 근처입니다. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files Clear Recent Files(&C) - + &Continue 재개(&C) - + &Pause 일시중지(&P) - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu가 게임을 실행중입니다 - + Warning Outdated Game Format 오래된 게임 포맷 경고 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. 이 게임 파일은 '분해된 ROM 디렉토리'라는 오래된 포맷을 사용하고 있습니다. 해당 포맷은 NCA, NAX, XCI 또는 NSP와 같은 다른 포맷으로 대체되었으며 분해된 ROM 디렉토리에는 아이콘, 메타 데이터 및 업데이트가 지원되지 않습니다.<br><br>yuzu가 지원하는 다양한 Switch 포맷에 대한 설명은 <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>위키를 확인하세요.</a> 이 메시지는 다시 표시되지 않습니다. - - + + Error while loading ROM! ROM 로드 중 오류 발생! - + The ROM format is not supported. 지원되지 않는 롬 포맷입니다. - + An error occurred initializing the video core. 비디오 코어를 초기화하는 동안 오류가 발생했습니다. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. 비디오 코어를 실행하는 동안 yuzu에 오류가 발생했습니다. 이것은 일반적으로 통합 드라이버를 포함하여 오래된 GPU 드라이버로 인해 발생합니다. 자세한 내용은 로그를 참조하십시오. 로그 액세스에 대한 자세한 내용은 <a href='https://yuzu-emu.org/help/reference/log-files/'>로그 파일 업로드 방법</a> 페이지를 참조하세요. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM 불러오는 중 오류 발생! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>파일들을 다시 덤프하기 위해<a href='https://yuzu-emu.org/help/quickstart/'>yuzu 빠른 시작 가이드</a> 를 따라주세요.<br>도움이 필요할 시 yuzu 위키</a> 를 참고하거나 yuzu 디스코드</a> 를 이용해보세요. - + An unknown error occurred. Please see the log for more details. 알 수 없는 오류가 발생했습니다. 자세한 내용은 로그를 참고하십시오. - + (64-bit) (64비트) - + (32-bit) (32비트) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data 세이브 데이터 - + Mod Data 모드 데이터 - + Error Opening %1 Folder %1 폴더 열기 오류 - - + + Folder does not exist! 폴더가 존재하지 않습니다! - + Error Opening Transferable Shader Cache 전송 가능한 셰이더 캐시 열기 오류 - + Failed to create the shader cache directory for this title. 이 타이틀에 대한 셰이더 캐시 디렉토리를 생성하지 못했습니다. - + Contents 컨텐츠 - + Update 업데이트 - + DLC DLC - + Remove Entry 항목 제거 - + Remove Installed Game %1? 설치된 게임을 삭제 %1? - - - - - - + + + + + + Successfully Removed 삭제 완료 - + Successfully removed the installed base game. 설치된 기본 게임을 성공적으로 제거했습니다. - - - + + + Error Removing %1 삭제 중 오류 발생 %1 - + The base game is not installed in the NAND and cannot be removed. 기본 게임은 NAND에 설치되어 있지 않으며 제거 할 수 없습니다. - + Successfully removed the installed update. 설치된 업데이트를 성공적으로 제거했습니다. - + There is no update installed for this title. 이 타이틀에 대해 설치된 업데이트가 없습니다. - + There are no DLC installed for this title. 이 타이틀에 설치된 DLC가 없습니다. - + Successfully removed %1 installed DLC. 설치된 %1 DLC를 성공적으로 제거했습니다. - + Delete OpenGL Transferable Shader Cache? OpenGL 전송 가능한 셰이더 캐시를 삭제하시겠습니까? - + Delete Vulkan Transferable Shader Cache? Vulkan 전송 가능한 셰이더 캐시를 삭제하시겠습니까? - + Delete All Transferable Shader Caches? 모든 전송 가능한 셰이더 캐시를 삭제하시겠습니까? - + Remove Custom Game Configuration? 사용자 지정 게임 구성을 제거 하시겠습니까? - + Remove File 파일 제거 - - + + Error Removing Transferable Shader Cache 전송 가능한 셰이더 캐시 제거 오류 - - + + A shader cache for this title does not exist. 이 타이틀에 대한 셰이더 캐시가 존재하지 않습니다. - + Successfully removed the transferable shader cache. 전송 가능한 셰이더 캐시를 성공적으로 제거했습니다. - + Failed to remove the transferable shader cache. 전송 가능한 셰이더 캐시를 제거하지 못했습니다. - - + + Error Removing Transferable Shader Caches 전송 가능한 셰이더 캐시 제거 오류 - + Successfully removed the transferable shader caches. 전송 가능한 셰이더 캐시를 성공적으로 제거했습니다. - + Failed to remove the transferable shader cache directory. 전송 가능한 셰이더 캐시 디렉토리를 제거하지 못했습니다. - - + + Error Removing Custom Configuration 사용자 지정 구성 제거 오류 - + A custom configuration for this title does not exist. 이 타이틀에 대한 사용자 지정 구성이 존재하지 않습니다. - + Successfully removed the custom game configuration. 사용자 지정 게임 구성을 성공적으로 제거했습니다. - + Failed to remove the custom game configuration. 사용자 지정 게임 구성을 제거하지 못했습니다. - - + + RomFS Extraction Failed! RomFS 추출 실패! - + There was an error copying the RomFS files or the user cancelled the operation. RomFS 파일을 복사하는 중에 오류가 발생했거나 사용자가 작업을 취소했습니다. - + Full 전체 - + Skeleton 뼈대 - + Select RomFS Dump Mode RomFS 덤프 모드 선택 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. RomFS 덤프 방법을 선택하십시오.<br>전체는 모든 파일을 새 디렉토리에 복사하고<br>뼈대는 디렉토리 구조 만 생성합니다. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1에 RomFS를 추출하기에 충분한 여유 공간이 없습니다. 공간을 확보하거나 에뮬레이견 > 설정 > 시스템 > 파일시스템 > 덤프 경로에서 다른 덤프 디렉토리를 선택하십시오. - + Extracting RomFS... RomFS 추출 중... - - + + Cancel 취소 - + RomFS Extraction Succeeded! RomFS 추출이 성공했습니다! - + The operation completed successfully. 작업이 성공적으로 완료되었습니다. - + Error Opening %1 %1 열기 오류 - + Select Directory 경로 선택 - + Properties 속성 - + The game properties could not be loaded. 게임 속성을 로드 할 수 없습니다. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 실행파일 (%1);;모든 파일 (*.*) - + Load File 파일 로드 - + Open Extracted ROM Directory 추출된 ROM 디렉토리 열기 - + Invalid Directory Selected 잘못된 디렉토리 선택 - + The directory you have selected does not contain a 'main' file. 선택한 디렉토리에 'main'파일이 없습니다. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 설치 가능한 Switch 파일 (*.nca *.nsp *.xci);;Nintendo 컨텐츠 아카이브 (*.nca);;Nintendo 서브미션 패키지 (*.nsp);;NX 카트리지 이미지 (*.xci) - + Install Files 파일 설치 - + %n file(s) remaining %n개의 파일이 남음 - + Installing file "%1"... 파일 "%1" 설치 중... - - + + Install Results 설치 결과 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 충돌을 피하기 위해, 낸드에 베이스 게임을 설치하는 것을 권장하지 않습니다. 이 기능은 업데이트나 DLC를 설치할 때에만 사용해주세요. - + %n file(s) were newly installed %n개의 파일이 새로 설치되었습니다. - + %n file(s) were overwritten %n개의 파일을 덮어썼습니다. - + %n file(s) failed to install %n개의 파일을 설치하지 못했습니다. - + System Application 시스템 애플리케이션 - + System Archive 시스템 아카이브 - + System Application Update 시스템 애플리케이션 업데이트 - + Firmware Package (Type A) 펌웨어 패키지 (A타입) - + Firmware Package (Type B) 펌웨어 패키지 (B타입) - + Game 게임 - + Game Update 게임 업데이트 - + Game DLC 게임 DLC - + Delta Title 델타 타이틀 - + Select NCA Install Type... NCA 설치 유형 선택... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 이 NCA를 설치할 타이틀 유형을 선택하세요: (대부분의 경우 기본값인 '게임'이 괜찮습니다.) - + Failed to Install 설치 실패 - + The title type you selected for the NCA is invalid. NCA 타이틀 유형이 유효하지 않습니다. - + File not found 파일을 찾을 수 없음 - + File "%1" not found 파일 "%1"을 찾을 수 없습니다 - + OK OK - + Missing yuzu Account yuzu 계정 누락 - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. 게임 호환성 테스트 결과를 제출하려면 yuzu 계정을 연결해야합니다.<br><br/>yuzu 계정을 연결하려면 에뮬레이션 &gt; 설정 &gt; 웹으로 가세요. - + Error opening URL URL 열기 오류 - + Unable to open the URL "%1". URL "%1"을 열 수 없습니다. - + TAS Recording TAS 레코딩 - + Overwrite file of player 1? 플레이어 1의 파일을 덮어쓰시겠습니까? - + Invalid config detected 유효하지 않은 설정 감지 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 휴대 모드용 컨트롤러는 거치 모드에서 사용할 수 없습니다. 프로 컨트롤러로 대신 선택됩니다. - - + + Error 오류 - - + + The current game is not looking for amiibos 현재 게임은 amiibo를 찾고 있지 않습니다 - - + + Amiibo Amiibo - - + + The current amiibo has been removed 현재 amiibo가 제거되었습니다. - + Amiibo File (%1);; All Files (*.*) Amiibo 파일 (%1);; 모든 파일 (*.*) - + Load Amiibo Amiibo 로드 - - Error opening Amiibo data file - Amiibo 데이터 파일 열기 오류 - - - - Unable to open Amiibo file "%1" for reading. - Amiibo 파일 "%1"을(를) 읽을 수 없습니다. - - - - Error reading Amiibo data file - Amiibo 데이터 파일 읽기 오류 - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Amiibo 데이터를 완전히 읽을 수 없습니다. %1 바이트를 읽으려고 했지만 %2 바이트만 읽을 수 있었습니다. - - - + Error loading Amiibo data Amiibo 데이터 로드 오류 - + Unable to load Amiibo data. Amiibo 데이터를 로드할 수 없습니다. - + Capture Screenshot 스크린샷 캡처 - + PNG Image (*.png) PNG 이미지 (*.png) - + TAS state: Running %1/%2 TAS 상태: %1/%2 실행 중 - + TAS state: Recording %1 TAS 상태: 레코딩 %1 - + TAS state: Idle %1/%2 TAS 상태: 유휴 %1/%2 - + TAS State: Invalid TAS 상태: 유효하지 않음 - + &Stop Running 실행 중지(&S) - + &Start 시작(&S) - + Stop R&ecording 레코딩 중지(&e) - + R&ecord 레코드(&R) - + Building: %n shader(s) 빌드중: %n개 셰이더 - + Scale: %1x %1 is the resolution scaling factor 스케일: %1x - + Speed: %1% / %2% 속도: %1% / %2% - + Speed: %1% 속도: %1% - + Game: %1 FPS (Unlocked) 게임: %1 FPS (제한없음) - + Game: %1 FPS 게임: %1 FPS - + Frame: %1 ms 프레임: %1 ms - + GPU NORMAL GPU 보통 - + GPU HIGH GPU 높음 - + GPU EXTREME GPU 굉장함 - + GPU ERROR GPU 오류 - + DOCKED 거치 모드 - + HANDHELD 휴대 모드 - + NEAREST NEAREST - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA AA 없음 - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 해당 게임은 플레이하기 전에 Switch 기기에서 추가 파일을 덤프해야합니다.<br/><br/>이러한 파일 덤프에 대한 자세한 내용은 다음 위키 페이지를 참조하십시오: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Switch 콘솔에서 시스템 아카이브 및 공유 글꼴 덤프</a>.<br/><br/>게임 목록으로 돌아가시겠습니까? 이를 무시하고 에뮬레이션을 계속하면 충돌, 저장 데이터 손상 또는 기타 버그가 발생할 수 있습니다. - + yuzu was unable to locate a Switch system archive. %1 yuzu가 Switch 시스템 아카이브를 찾을 수 없습니다. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu가 Switch 시스템 아카이브를 찾을 수 없습니다: %1. %2 - + System Archive Not Found 시스템 아카이브를 찾을 수 없음 - + System Archive Missing 시스템 아카이브 누락 - + yuzu was unable to locate the Switch shared fonts. %1 yuzu가 Switch 공유 글꼴을 찾을 수 없습니다. %1 - + Shared Fonts Not Found 공유 글꼴을 찾을 수 없음 - + Shared Font Missing 공유 글꼴 누락 - + Fatal Error 치명적인 오류 - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 치명적인 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오. 로그 액세스에 대한 자세한 내용은 다음 페이지를 참조하십시오: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>로그 파일을 업로드하는 방법</a>.<br/><br/>게임 목록으로 돌아가시겠습니까? 이를 무시하고 에뮬레이션을 계속하면 충돌, 저장 데이터 손상 또는 기타 버그가 발생할 수 있습니다. - + Fatal Error encountered 치명적인 오류 발생 - + Confirm Key Rederivation 키 재생성 확인 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5093,37 +5142,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 자동 생성되었던 키 파일들이 삭제되고 키 생성 모듈이 다시 실행됩니다. - + Missing fuses fuses 누락 - + - Missing BOOT0 - BOOT0 누락 - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main 누락 - + - Missing PRODINFO - PRODINFO 누락 - + Derivation Components Missing 파생 구성 요소 누락 - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 암호화 키가 없습니다. <br>모든 키, 펌웨어 및 게임을 얻으려면 <a href='https://yuzu-emu.org/help/quickstart/'>yuzu 빠른 시작 가이드</a>를 따르세요.<br><br> <small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5132,39 +5181,39 @@ on your system's performance. 소요될 수 있습니다. - + Deriving Keys 파생 키 - + Select RomFS Dump Target RomFS 덤프 대상 선택 - + Please select which RomFS you would like to dump. 덤프할 RomFS를 선택하십시오. - + Are you sure you want to close yuzu? yuzu를 닫으시겠습니까? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 에뮬레이션을 중지하시겠습니까? 모든 저장되지 않은 진행 상황은 사라집니다. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5480,22 +5529,22 @@ Screen. Create Room - + 방 만들기 Room Name - + 방 이름 Preferred Game - + 선호하는 게임 Max Players - + 최대 플레이어 @@ -5505,66 +5554,68 @@ Screen. (Leave blank for open game) - + (열린 게임은 공백으로 둠) Password - + 비밀번호 Port - + 포트 Room Description - + 방 설명 Load Previous Ban List - + 이전 차단 목록 불러오기 Public - + 공개 Unlisted - + 비공개 Host Room - + 호스트 방 HostRoomWindow - + Error 오류 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + 공개 로비에 방을 알리지 못했습니다. 방을 공개적으로 호스트하려면 에뮬레이션 -> 구성 -> 웹에서 유효한 yuzu 계정이 구성되어 있어야 합니다. 공개 로비에 방을 게시하지 않으려면 대신 목록에 없음을 선택하세요. +디버그 메시지: Hotkeys - + Audio Mute/Unmute 오디오 음소거/음소거 해제 + @@ -5586,112 +5637,111 @@ Debug Message: - Main Window 메인 윈도우 - + Audio Volume Down 오디오 볼륨 낮추기 - + Audio Volume Up 오디오 볼륨 키우기 - + Capture Screenshot 스크린샷 캡처 - + Change Adapting Filter 적응형 필터 변경 - + Change Docked Mode 독 모드 변경 - + Change GPU Accuracy GPU 정확성 변경 - + Continue/Pause Emulation 재개/에뮬레이션 일시중지 - + Exit Fullscreen 전체화면 종료 - + Exit yuzu yuzu 종료 - + Fullscreen 전체화면 - + Load File 파일 로드 - + Load/Remove Amiibo Amiibo 로드/제거 - + Restart Emulation 에뮬레이션 재시작 - + Stop Emulation 에뮬레이션 중단 - + TAS Record TAS 기록 - + TAS Reset TAS 리셋 - + TAS Start/Stop TAS 시작/멈춤 - + Toggle Filter Bar 상태 표시줄 전환 - + Toggle Framerate Limit 프레임속도 제한 토글 - + Toggle Mouse Panning 마우스 패닝 활성화 - + Toggle Status Bar 상태 표시줄 전환 @@ -5772,78 +5822,78 @@ Debug Message: Public Room Browser - + 공개 방 찾아보기 Nickname - + 별명 Filters - + 필터 Search - + 검색 Games I Own - + 게임 I 주인 Hide Full Rooms - + 전체 방 숨기기 Refresh Lobby - + 로비 새로 고침 - + Password Required to Join - + 가입에 필요한 비밀번호 - + Password: - + 비밀번호: - + Room Name - + 방 이름 - + Preferred Game - + 선호하는 게임 - + Host - + 호스트 - + Players - + 플레이어 - + Refreshing - + 새로 고치는 중 - + Refresh List - + 새로 고침 목록 @@ -6001,27 +6051,27 @@ Debug Message: Browse Public Game Lobby - + 공개 게임 로비 찾아보기 Create Room - + 방 만들기 Leave Room - + 방 나가기 Direct Connect to Room - + 방에 직접 연결 Show Current Room - + 현재 방 보기 @@ -6107,48 +6157,48 @@ Debug Message: Moderation - + 조정 Ban List - + 차단 목록 Refreshing - + 새로 고치는 중 Unban - + 차단 해재 Subject - + 주제 Type - + 유형 Forum Username - + 포럼 사용자이름 IP Address - + IP 주소 Refresh - + 새로 고침 @@ -6157,18 +6207,18 @@ Debug Message: Current connection status - + 현재 연결 상태 Not Connected. Click here to find a room! - + 연결되지 않았습니다. 방을 찾으려면 여기를 클릭하세요! - + Connected 연결됨 @@ -6176,7 +6226,7 @@ Debug Message: Not Connected - + 연결되지 않음 @@ -6187,12 +6237,13 @@ Debug Message: Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - + 방 정보를 업데이트하지 못했습니다. 인터넷 연결을 확인하고 방을 다시 호스팅해 보세요. +디버그 메시지: - + New Messages Received - + 수신된 새 메시지 @@ -6200,124 +6251,144 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + 사용자 이름이 유효하지 않습니다. 4~20자의 영숫자여야 합니다. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + 방 이름이 유효하지 않습니다. 4~20자의 영숫자여야 합니다. Username is already in use or not valid. Please choose another. - + 사용자 이름은 이미 사용 중이거나 유효하지 않습니다. 다른 것을 선택하세요. IP is not a valid IPv4 address. - + IP는 유효한 IPv4 주소가 아닙니다. Port must be a number between 0 to 65535. - + 포트는 0에서 65535 사이의 숫자여야 합니다. You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + 방을 호스팅하려면 선호하는 게임을 선택해야 합니다. 아직 게임 목록에 게임이 없으면 게임 목록에서 더하기 아이콘을 클릭하여 게임 폴더를 추가하세요. Unable to find an internet connection. Check your internet settings. - + 인터넷 연결을 찾을 수 없습니다. 인터넷 설정을 확인하세요. Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + 호스트에 연결할 수 없습니다. 연결 설정이 올바른지 확인하세요. 여전히 연결할 수 없으면 방 주인에게 연락하여 호스트가 전달된 외부 포트로 올바르게 구성되었는지 확인하세요. Unable to connect to the room because it is already full. - + 이미 꽉 찼기 때문에 방에 연결할 수 없습니다. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + 방을 만들지 못했습니다. 다시 시도하세요. yuzu를 다시 시작해야 할 수도 있습니다. The host of the room has banned you. Speak with the host to unban you or try a different room. - + 방 주인이 당신을 차단했습니다. 주인과 대화하여 차단을 해제하거나 다른 방을 사용해 보세요. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + 버전이 불일치합니다! 최신 버전의 yuzu로 업데이트하세요. 문제가 지속되면 방 주인에게 연락하여 서버 업데이트를 요청하세요. Incorrect password. - + 잘못된 비밀번호입니다. An unknown error occurred. If this error continues to occur, please open an issue - + 알 수없는 오류가 발생했습니다. 이 오류가 계속 발생하면 문제를 알려주세요. Connection to room lost. Try to reconnect. - + 방과의 연결이 끊어졌습니다. 다시 연결해 보세요. You have been kicked by the room host. - + 방 주인에게 추방당했습니다. IP address is already in use. Please choose another. - + IP 주소는 이미 사용 중입니다. 다른 것을 선택하세요. You do not have enough permission to perform this action. - + 이 작업을 수행할 수 있는 권한이 없습니다. The user you are trying to kick/ban could not be found. They may have left the room. - + 추방/금지하려는 사용자를 찾을 수 없습니다. +방을 나갔을 수 있습니다. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + 네트워크 인터페이스가 선택되지 않았습니다. +구성 -> 시스템 -> 네트워크로 이동하여 선택하십시오. + + + + Game already running + 게임이 이미 실행 중입니다 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + 게임이 이미 실행 중일 때 방에 참여하는 것은 권장되지 않으며 방 기능이 제대로 작동하지 않을 수 있습니다. +그래도 진행하시겠습니까? + + + Leave Room - + 방 나가기 - + You are about to close the room. Any network connections will be closed. - + 방을 닫으려고 합니다. 모든 네트워크 연결이 닫힙니다. - + Disconnect - + 연결 해제 - + You are about to leave the room. Any network connections will be closed. - + 방을 떠나려고 합니다. 모든 네트워크 연결이 닫힙니다. NetworkMessage::ErrorManager - + Error 오류 @@ -6368,17 +6439,17 @@ p, li { white-space: pre-wrap; } %1 is not playing a game - + %1은(는) 게임을 하고 있지 않습니다 %1 is playing %2 - + %1이(가) %2을(를) 플레이 중입니다 - + Not playing a game - + 게임을 하지 않음 @@ -6431,7 +6502,7 @@ p, li { white-space: pre-wrap; } - + [not set] [설정 안 됨] @@ -6446,10 +6517,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 축 %1%2 @@ -6463,9 +6534,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [알 수 없음] @@ -6630,15 +6701,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [유효하지않음] - - + + %1%2Hat %3 %1%2방향키 %3 @@ -6646,35 +6717,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Axis %3 - + %1%2Axis %3,%4,%5 %1%2Axis %3,%4,%5 - + %1%2Motion %3 %1%2모션 %3 - - + + %1%2Button %3 %1%2버튼 %3 - + [unused] [미사용] @@ -6715,7 +6786,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/nb.ts b/dist/languages/nb.ts index a5e68cec3d..79ada55b2f 100644 --- a/dist/languages/nb.ts +++ b/dist/languages/nb.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -746,200 +746,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Aktiver GDB Stub - + Port: Port: - + Logging Loggføring - + Global Log Filter Global Loggfilter - + Show Log in Console Vis logg i konsollen - + Open Log Location Åpne Logg-Plassering - + When checked, the max size of the log increases from 100 MB to 1 GB Når dette er på øker maksstørrelsen til loggen fra 100 MB til 1 GB - + Enable Extended Logging** Slå på utvidet loggføring** - + Homebrew Homebrew - + Arguments String Argument streng - + Graphics Grafikk - + When checked, the graphics API enters a slower debugging mode Når dette er på går grafikk–API-et inn i en tregere feilsøkingsmodus - + Enable Graphics Debugging Slå på Grafikkfeilsøking - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback Slå på shader-tilbakemelding - + When checked, it executes shaders without loop logic changes Når dette er på kjører shader-e uten endring i løkkelogikk - + Disable Loop safety checks - + Debugging Feilsøking - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avansert - + Kiosk (Quest) Mode - + Enable CPU Debugging Slå på prosessorfeilsøking - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet Slå av web-applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Dette blir automatisk tilbakestilt når yuzu lukkes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1558,37 +1593,47 @@ This would ban both their forum username and their IP address. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropisk filtrering: - + Automatic Automatisk - + Default Standard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2080,7 +2125,7 @@ This would ban both their forum username and their IP address. - + Left Stick Venstre Pinne @@ -2174,14 +2219,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2200,7 +2245,7 @@ This would ban both their forum username and their IP address. - + Plus Pluss @@ -2213,15 +2258,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2278,231 +2323,236 @@ This would ban both their forum username and their IP address. - + Right Stick Høyre Pinne - - - - + + + + Clear Fjern - - - - - + + + + + [not set] [ikke satt] - - - Toggle button - Veksle knapp - - - - + + Invert button Inverter knapp - - + + + Toggle button + Veksle knapp + + + + Invert axis Inverter akse - - - + + + Set threshold Set grense - - + + Choose a value between 0% and 100% Velg en verdi mellom 0% og 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Etter du har trykker på OK, flytt først stikken horisontalt, og så vertikalt. For å invertere aksene, flytt først stikken vertikalt, og så horistonalt. - + Center axis Senterakse - - + + Deadzone: %1% Dødsone: %1% - - + + Modifier Range: %1% Modifikatorområde: %1% - - + + Pro Controller Pro-Kontroller - + Dual Joycons Doble Joycons - + Left Joycon Venstre Joycon - + Right Joycon Høyre Joycon - + Handheld Håndholdt - + GameCube Controller GameCube-kontroller - + Poke Ball Plus Poke Ball Plus - + NES Controller NES-kontroller - + SNES Controller SNES-kontroller - + N64 Controller N64-kontroller - + Sega Genesis Sega Genesis - + Start / Pause Start / paus - + Z Z - + Control Stick Kontrollstikke - + C-Stick C-stikke - + Shake! Rist! - + [waiting] [venter] - + New Profile Ny Profil - + Enter a profile name: Skriv inn et profilnavn: - - + + Create Input Profile Lag inndataprofil - + The given profile name is not valid! Det oppgitte profilenavnet er ugyldig! - + Failed to create the input profile "%1" Klarte ikke lage inndataprofil "%1" - + Delete Input Profile Slett inndataprofil - + Failed to delete the input profile "%1" Klarte ikke slette inndataprofil "%1" - + Load Input Profile Last inn inndataprofil - + Failed to load the input profile "%1" Klarte ikke laste inn inndataprofil "%1" - + Save Input Profile Lagre inndataprofil - + Failed to save the input profile "%1" Klarte ikke lagre inndataprofil "%1" @@ -2757,42 +2807,42 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt.Utvikler - + Add-Ons Tillegg - + General Generelt - + System System - + CPU CPU - + Graphics Grafikk - + Adv. Graphics Avn. Grafikk - + Audio Lyd - + Properties Egenskaper @@ -3504,47 +3554,47 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt.<html><head/><body><p>Leser kontrollerinndata fra script i samme format som TAS-nx–script.<br/>For en mer detaljert beskrivelse, se <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">hjelpesiden</span></a> på yuzus hjemmeside.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). For å sjekke hvilke hurtigtaster som styrer avspilling/innspilling, se hurtigtastinnstillingene (Konfigurer -> Generelt -> Hurtigtaster). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ADVARSEL: Dette er eksperimentell funksjonalitet.<br/>Script spilles ikke tilbake med perfekt bildetiming med den nåværende, ikke-perfekte synkemetoden. - + Settings Innstillinger - + Enable TAS features Slå på TAS-funksjonalitet - + Loop script - + Pause execution during loads Sett kjøring på vent under lasting - + Script Directory Skriptmappe - + Path Sti - + ... ... @@ -3954,7 +4004,7 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + Verify Verifiser @@ -4041,7 +4091,7 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + Unspecified Uspesifisert @@ -4056,17 +4106,36 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifiserer... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verifisering feilet + + + Verification failed Verifisering feilet - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4135,12 +4204,12 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re DirectConnectWindow - + Connecting - + Connect @@ -4148,486 +4217,486 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonym data blir samlet inn</a>for å hjelpe til med å forbedre yuzu.<br/><br/>Vil du dele din bruksdata med oss? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Laster web-applet... - + Disable Web Applet Slå av web-applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Antall shader-e som bygges for øyeblikket - + The current selected resolution scaling multiplier. Den valgte oppløsningsskaleringsfaktoren. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Nåværende emuleringshastighet. Verdier høyere eller lavere en 100% indikerer at emuleringen kjører raskere eller tregere enn en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hvor mange bilder per sekund spiller viser. Dette vil variere fra spill til spill og scene til scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid det tar for å emulere et Switch bilde. Teller ikke med bildebegrensing eller v-sync. For full-hastighet emulering burde dette være 16.67 ms. på det høyeste. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Paus - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Et spill kjører i yuzu - + Warning Outdated Game Format Advarsel: Utdatert Spillformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Du bruker en dekonstruert ROM-mappe for dette spillet, som er et utdatert format som har blitt erstattet av andre formater som NCA, NAX, XCI, eller NSP. Dekonstruerte ROM-mapper mangler ikoner, metadata, og oppdateringsstøtte.<br><br>For en forklaring på diverse Switch-formater som yuzu støtter,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>sjekk vår wiki</a>. Denne meldingen vil ikke bli vist igjen. - - + + Error while loading ROM! Feil under innlasting av ROM! - + The ROM format is not supported. Dette ROM-formatet er ikke støttet. - + An error occurred initializing the video core. En feil oppstod under initialisering av videokjernen. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu har oppdaget en feil under kjøring av videokjernen. Dette er vanligvis forårsaket av utdaterte GPU-drivere, inkludert for integrert grafikk. Vennligst sjekk loggen for flere detaljer. For mer informasjon om å finne loggen, besøk følgende side: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Uploadd the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Feil under lasting av ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. En ukjent feil oppstod. Se loggen for flere detaljer. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Lagre Data - + Mod Data Mod Data - + Error Opening %1 Folder Feil Under Åpning av %1 Mappen - - + + Folder does not exist! Mappen eksisterer ikke! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - + Contents Innhold - + Update Oppdatering - + DLC DLC - + Remove Entry Fjern oppføring - + Remove Installed Game %1? Fjern Installert Spill %1? - - - - - - + + + + + + Successfully Removed Fjerning lykkes - + Successfully removed the installed base game. - - - + + + Error Removing %1 Feil Under Fjerning av %1 - + The base game is not installed in the NAND and cannot be removed. Grunnspillet er ikke installert i NAND og kan ikke bli fjernet. - + Successfully removed the installed update. Fjernet vellykket den installerte oppdateringen. - + There is no update installed for this title. Det er ingen oppdatering installert for denne tittelen. - + There are no DLC installed for this title. Det er ingen DLC installert for denne tittelen. - + Successfully removed %1 installed DLC. Fjernet vellykket %1 installerte DLC-er. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? Fjern Tilpasset Spillkonfigurasjon? - + Remove File Fjern Fil - - + + Error Removing Transferable Shader Cache Feil under fjerning av overførbar shader cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. Lykkes i å fjerne den overførbare shader cachen. - + Failed to remove the transferable shader cache. Feil under fjerning av den overførbare shader cachen. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Feil Under Fjerning Av Tilpasset Konfigurasjon - + A custom configuration for this title does not exist. En tilpasset konfigurasjon for denne tittelen finnes ikke. - + Successfully removed the custom game configuration. Fjernet vellykket den tilpassede spillkonfigurasjonen. - + Failed to remove the custom game configuration. Feil under fjerning av den tilpassede spillkonfigurasjonen. - - + + RomFS Extraction Failed! Utvinning av RomFS Feilet! - + There was an error copying the RomFS files or the user cancelled the operation. Det oppstod en feil under kopiering av RomFS filene eller så kansellerte brukeren operasjonen. - + Full Fullstendig - + Skeleton Skjelett - + Select RomFS Dump Mode Velg RomFS Dump Modus - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Velg hvordan du vil dumpe RomFS.<br>Fullstendig vil kopiere alle filene til en ny mappe mens <br>skjelett vil bare skape mappestrukturen. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Utvinner RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! RomFS Utpakking lyktes! - + The operation completed successfully. Operasjonen fullført vellykket. - + Error Opening %1 Feil ved åpning av %1 - + Select Directory Velg Mappe - + Properties Egenskaper - + The game properties could not be loaded. Spillets egenskaper kunne ikke bli lastet inn. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Kjørbar Fil (%1);;Alle Filer (*.*) - + Load File Last inn Fil - + Open Extracted ROM Directory Åpne Utpakket ROM Mappe - + Invalid Directory Selected Ugyldig Mappe Valgt - + The directory you have selected does not contain a 'main' file. Mappen du valgte inneholder ikke en 'main' fil. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installerbar Switch-Fil (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xcI) - + Install Files Installer Filer - + %n file(s) remaining %n fil gjenstår%n filer gjenstår - + Installing file "%1"... Installerer fil "%1"... - - + + Install Results Insallasjonsresultater - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed %n fil ble nylig installert @@ -4635,7 +4704,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n fil ble overskrevet @@ -4643,7 +4712,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n fil ble ikke installert @@ -4651,411 +4720,391 @@ Please, only use this feature to install updates and DLC. - + System Application Systemapplikasjon - + System Archive Systemarkiv - + System Application Update Systemapplikasjonsoppdatering - + Firmware Package (Type A) Firmware Pakke (Type A) - + Firmware Package (Type B) Firmware-Pakke (Type B) - + Game Spill - + Game Update Spilloppdatering - + Game DLC Spill tilleggspakke - + Delta Title Delta Tittel - + Select NCA Install Type... Velg NCA Installasjonstype... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vennligst velg typen tittel du vil installere denne NCA-en som: (I de fleste tilfellene, standarden 'Spill' fungerer.) - + Failed to Install Feil under Installasjon - + The title type you selected for the NCA is invalid. Titteltypen du valgte for NCA-en er ugyldig. - + File not found Fil ikke funnet - + File "%1" not found Filen "%1" ikke funnet - + OK OK - + Missing yuzu Account Mangler yuzu Bruker - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. For å sende inn et testtilfelle for spillkompatibilitet, må du linke yuzu-brukeren din.<br><br/>For å linke yuzu-brukeren din, gå til Emulasjon &gt; Konfigurasjon &gt; Nett. - + Error opening URL Feil under åpning av URL - + Unable to open the URL "%1". Kunne ikke åpne URL "%1". - + TAS Recording TAS-innspilling - + Overwrite file of player 1? Overskriv filen til spiller 1? - + Invalid config detected Ugyldig konfigurasjon oppdaget - + Handheld controller can't be used on docked mode. Pro controller will be selected. Håndholdt kontroller kan ikke brukes i dokket modus. Pro-kontroller vil bli valgt. - - + + Error Feil - - + + The current game is not looking for amiibos Det kjørende spillet sjekker ikke for amiibo-er - - + + Amiibo Amiibo - - + + The current amiibo has been removed Den valgte amiibo-en har blitt fjernet - + Amiibo File (%1);; All Files (*.*) Amiibo-Fil (%1);; Alle Filer (*.*) - + Load Amiibo Last inn Amiibo - - Error opening Amiibo data file - Feil ved Åpning av Amiibo data fil - - - - Unable to open Amiibo file "%1" for reading. - Kunne ikke åpne Amiibo-fil "%1" for lesing. - - - - Error reading Amiibo data file - Feil under lesing av Amiibo datafil. - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Kunne ikke lese all Amiibo-data. Forventet å lese minst %1 bytes, men kunne bare lese %2 bytes. - - - + Error loading Amiibo data Feil ved lasting av Amiibo data - + Unable to load Amiibo data. Kunne ikke laste Amiibo-data. - + Capture Screenshot Ta Skjermbilde - + PNG Image (*.png) PNG Bilde (*.png) - + TAS state: Running %1/%2 TAS-tilstand: Kjører %1/%2 - + TAS state: Recording %1 TAS-tilstand: Spiller inn %1 - + TAS state: Idle %1/%2 TAS-tilstand: Venter %1%2 - + TAS State: Invalid TAS-tilstand: Ugyldig - + &Stop Running &Stopp kjøring - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) Bygger: %n shaderBygger: %n shader-e - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS (Unlocked) Spill: %1 FPS (ubegrenset) - + Game: %1 FPS Spill: %1 FPS - + Frame: %1 ms Ramme: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HØY - + GPU EXTREME GPU EKSTREM - + GPU ERROR GPU FEIL - + DOCKED - + HANDHELD - + NEAREST NÆRMESTE - - + + BILINEAR BILINEÆR - + BICUBIC BIKUBISK - + GAUSSIAN GAUSSISK - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA INGEN AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Spillet du prøver å laste krever at ekstra filer fra din Switch blir dumpet før du spiller.<br/><br/>For mer informasjon om dumping av disse filene, vennligst se den følgende wiki-siden: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping av System-Arkiv og Shared Fonts fra en Switch-Konsoll</a>.<br/><br/>Vil du gå tilbake til spillisten? Fortsetting av emulasjon kan føre til krasjing, ødelagt lagringsdata, eller andre feil. - + yuzu was unable to locate a Switch system archive. %1 yuzu kunne ikke finne et Switch system-arkiv. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu kunne ikke finne et Switch system-arkiv: %1. %2 - + System Archive Not Found System Arkiv Ikke Funnet - + System Archive Missing System Arkiv Mangler - + yuzu was unable to locate the Switch shared fonts. %1 yuzu kunne ikke finne Switch shared fonts. %1 - + Shared Fonts Not Found Shared Fonts Ikke Funnet - + Shared Font Missing Shared Font Mangler - + Fatal Error Fatal Feil - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu har oppdaget en fatal feil, vennligst se loggen for flere detaljer. For mer informasjon om å finne loggen, vennligst se den følgende siden: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Hvordan å Laste Opp Log-Filen</a>.<br/><br/>Vil du gå tilbake til spillisten? Fortsetting av emulasjon kan føre til krasjing, ødelagt lagringsdata, eller andre feil. - + Fatal Error encountered Fatal Feil oppstått - + Confirm Key Rederivation Bekreft Nøkkel-Redirevasjon - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5072,37 +5121,37 @@ og eventuelt lag backups. Dette vil slette dine autogenererte nøkkel-filer og kjøre nøkkel-derivasjonsmodulen på nytt. - + Missing fuses Mangler fuses - + - Missing BOOT0 - Mangler BOOT0 - + - Missing BCPKG2-1-Normal-Main - Mangler BCPKG2-1-Normal-Main - + - Missing PRODINFO - Mangler PRODINFO - + Derivation Components Missing Derivasjonskomponenter Mangler - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Krypteringsnøkler mangler. <br>Vennligst følg <a href='https://yuzu-emu.org/help/quickstart/'>yuzus oppstartsguide</a> for å få alle nøklene, fastvaren og spillene dine.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5111,39 +5160,39 @@ Dette kan ta opp til et minutt avhengig av systemytelsen din. - + Deriving Keys Deriverer Nøkler - + Select RomFS Dump Target Velg RomFS Dump-Mål - + Please select which RomFS you would like to dump. Vennligst velg hvilken RomFS du vil dumpe. - + Are you sure you want to close yuzu? Er du sikker på at du vil lukke yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Er du sikker på at du vil stoppe emulasjonen? All ulagret fremgang vil bli tapt. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5520,12 +5569,12 @@ Screen. HostRoomWindow - + Error Feil - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5534,11 +5583,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5560,112 +5610,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Ta Skjermbilde - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Fullskjerm - + Load File Last inn Fil - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5780,42 +5829,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Spillere - + Refreshing - + Refresh List @@ -6142,7 +6191,7 @@ Debug Message: - + Connected Tilkoblet @@ -6164,7 +6213,7 @@ Debug Message: - + New Messages Received @@ -6268,22 +6317,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6291,7 +6357,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Feil @@ -6350,7 +6416,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6405,7 +6471,7 @@ p, li { white-space: pre-wrap; } - + [not set] [ikke satt] @@ -6420,10 +6486,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Akse %1%2 @@ -6437,9 +6503,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [ukjent] @@ -6604,15 +6670,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [ugyldig] - - + + %1%2Hat %3 @@ -6620,35 +6686,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Akse %3 - + %1%2Axis %3,%4,%5 %1%2Akse %3,%4,%5 - + %1%2Motion %3 %1%2Bevegelse %3 - - + + %1%2Button %3 %1%2Knapp %3 - + [unused] [ubrukt] @@ -6689,7 +6755,7 @@ p, li { white-space: pre-wrap; } Ekstra - + %1%2%3 %1%2%3 diff --git a/dist/languages/nl.ts b/dist/languages/nl.ts index e42dbb519f..a6fe4274d7 100644 --- a/dist/languages/nl.ts +++ b/dist/languages/nl.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -743,200 +743,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub GDB Stub Aanzetten - + Port: Poort: - + Logging Loggen - + Global Log Filter Globale Log Filter - + Show Log in Console Laat Log Venster Zien - + Open Log Location Open Log Locatie - + When checked, the max size of the log increases from 100 MB to 1 GB Indien aangevinkt, neemt de maximale grootte van de log toe van 100 MB tot 1 GB - + Enable Extended Logging** Activeer Uitgebreid Loggen** - + Homebrew Homebrew - + Arguments String Argumenten Rij - + Graphics Graphics - + When checked, the graphics API enters a slower debugging mode Indien aangevinkt, gaat de grafische API naar een langzamere foutopsporingsmodus - + Enable Graphics Debugging Grafische foutopsporing inschakelen - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Indien aangevinkt, wordt de macro Just In Time-compiler uitgeschakeld. Als u dit inschakelt, worden games langzamer - + Disable Macro JIT Schakel Macro JIT uit - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Debugging - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Geavanceerd - + Kiosk (Quest) Mode Kiosk (Quest) Modus - + Enable CPU Debugging - + Enable Debug Asserts Schakel Debug asserties in - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Deze optie wordt automatisch gereset wanneer yuzu is gesloten. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1556,37 +1591,47 @@ This would ban both their forum username and their IP address. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotrope Filtering: - + Automatic - + Default Standaard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2078,7 +2123,7 @@ This would ban both their forum username and their IP address. - + Left Stick Linker Stick @@ -2172,14 +2217,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2198,7 +2243,7 @@ This would ban both their forum username and their IP address. - + Plus Plus: @@ -2211,15 +2256,15 @@ This would ban both their forum username and their IP address. - + R R: - + ZR ZR @@ -2276,231 +2321,236 @@ This would ban both their forum username and their IP address. - + Right Stick Rechter Stick - - - - + + + + Clear Verwijder - - - - - + + + + + [not set] [niet ingesteld] - - - Toggle button - Shakel Knop - - - - + + Invert button - - + + + Toggle button + Shakel Knop + + + + Invert axis Spiegel As - - - + + + Set threshold - - + + Choose a value between 0% and 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Zet Analoge Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Na OK in te drukken, beweeg je joystick eerst horizontaal en dan verticaal. Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. - + Center axis - - + + Deadzone: %1% Deadzone: %1% - - + + Modifier Range: %1% Bewerk Range: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Twee Joycons - + Left Joycon Linker Joycon - + Right Joycon Rechter Joycon - + Handheld Mobiel - + GameCube Controller GameCube Controller - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Start / Pauze - + Z Z - + Control Stick Control Stick - + C-Stick C-Stick - + Shake! Shudden! - + [waiting] [aan het wachten] - + New Profile Nieuw Profiel - + Enter a profile name: Voer nieuwe gebruikersnaam in: - - + + Create Input Profile Creëer een nieuw Invoer Profiel - + The given profile name is not valid! De ingevoerde Profiel naam is niet geldig - + Failed to create the input profile "%1" Het is mislukt om Invoer Profiel "%1 te Creëer - + Delete Input Profile Verwijder invoer profiel - + Failed to delete the input profile "%1" Het is mislukt om Invoer Profiel "%1 te Verwijderen - + Load Input Profile Laad invoer profiel - + Failed to load the input profile "%1" Het is mislukt om Invoer Profiel "%1 te Laden - + Save Input Profile Sla Invoer profiel op - + Failed to save the input profile "%1" Het is mislukt om Invoer Profiel "%1 Op te slaan @@ -2755,42 +2805,42 @@ Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. Ontwikkelaar - + Add-Ons Add-Ons - + General Algemeen - + System Systeem - + CPU CPU - + Graphics Grafisch - + Adv. Graphics Adv. Grafisch - + Audio Geluid - + Properties Eigenschappen @@ -3502,47 +3552,47 @@ Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Pad - + ... ... @@ -3952,7 +4002,7 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen - + Verify Verifieer @@ -4039,7 +4089,7 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen - + Unspecified Niet gespecificeerd @@ -4054,17 +4104,36 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen Token is niet geverifieerd. De verandering aan uw token zijn niet opgeslagen. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifiëren... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verificatie mislukt + + + Verification failed Verificatie mislukt - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verificatie mislukt. Check dat uw token correct is en dat uw internet werkt. @@ -4133,12 +4202,12 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen DirectConnectWindow - + Connecting - + Connect @@ -4146,908 +4215,888 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Annonieme gegevens worden verzameld</a> om yuzu te helpen verbeteren. <br/><br/> Zou je jouw gebruiksgegevens met ons willen delen? - + Telemetry Telemetrie - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Web Applet Laden... - + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Huidige emulatie snelheid. Waardes hoger of lager dan 100% betekent dat de emulatie sneller of langzamer loopt dan de Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hoeveel frames per seconde de game op dit moment weergeeft. Dit zal veranderen van game naar game en van scène naar scène. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tijd gebruikt om een frame van de Switch te emuleren, waarbij framelimiteren of v-sync niet wordt meegerekend. Voor emulatie op volledige snelheid zou dit maximaal 16.67 ms zijn. - + VULKAN - + OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Pauzeren - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Waarschuwing Verouderd Spel Formaat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Je gebruikt gedeconstrueerd ROM map formaat voor dit Spel, dit is een verouderd formaat en is vervangen door formaten zoals NCA, NAX, XCI of NSP. Gedeconstrueerd ROM map heeft geen iconen, metadata en update understeuning.<br><br>Voor een uitleg over welke Switch formaten yuzu ondersteund, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>kijk op onze wiki</a>. Dit bericht word niet nog een keer weergegeven. - - + + Error while loading ROM! Fout tijdens het laden van een ROM! - + The ROM format is not supported. Het formaat van de ROM is niet ondersteunt. - + An error occurred initializing the video core. Er is een fout opgetreden tijdens het initialiseren van de videokern. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Een onbekende fout heeft plaatsgevonden. Kijk in de log voor meer details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data Save Data - + Mod Data Mod Data - + Error Opening %1 Folder Fout tijdens het openen van %1 folder - - + + Folder does not exist! Folder bestaat niet! - + Error Opening Transferable Shader Cache Fout Bij Het Openen Van Overdraagbare Shader Cache - + Failed to create the shader cache directory for this title. - + Contents - + Update - + DLC - + Remove Entry - + Remove Installed Game %1? - - - - - - + + + + + + Successfully Removed - + Successfully removed the installed base game. - - - + + + Error Removing %1 - + The base game is not installed in the NAND and cannot be removed. - + Successfully removed the installed update. - + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. Er bestaat geen shader cache voor deze game - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! RomFS Extractie Mislukt! - + There was an error copying the RomFS files or the user cancelled the operation. Er was een fout tijdens het kopiëren van de RomFS bestanden of de gebruiker heeft de operatie geannuleerd. - + Full Vol - + Skeleton Skelet - + Select RomFS Dump Mode Selecteer RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Selecteer alstublieft hoe je de RomFS wilt dumpen.<br>Volledig kopieërd alle bestanden in een map terwijl <br> skelet maakt alleen het map structuur. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... RomFS uitpakken... - - + + Cancel Annuleren - + RomFS Extraction Succeeded! RomFS Extractie Geslaagd! - + The operation completed successfully. De operatie is succesvol voltooid. - + Error Opening %1 Fout bij openen %1 - + Select Directory Selecteer Map - + Properties Eigenschappen - + The game properties could not be loaded. De eigenschappen van de game kunnen niet geladen worden. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Executable (%1);;Alle bestanden (*.*) - + Load File Laad Bestand - + Open Extracted ROM Directory Open Gedecomprimeerd ROM Map - + Invalid Directory Selected Ongeldige Map Geselecteerd - + The directory you have selected does not contain a 'main' file. De map die je hebt geselecteerd bevat geen 'main' bestand. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Bestand "%1" Installeren... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systeem Applicatie - + System Archive Systeem Archief - + System Application Update Systeem Applicatie Update - + Firmware Package (Type A) Filmware Pakket (Type A) - + Firmware Package (Type B) Filmware Pakket (Type B) - + Game Game - + Game Update Game Update - + Game DLC Game DLC - + Delta Title Delta Titel - + Select NCA Install Type... Selecteer NCA Installatie Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Selecteer het type titel hoe je wilt dat deze NCA installeerd: (In de meeste gevallen is de standaard 'Game' juist.) - + Failed to Install Installatie Mislukt - + The title type you selected for the NCA is invalid. Het type title dat je hebt geselecteerd voor de NCA is ongeldig. - + File not found Bestand niet gevonden - + File "%1" not found Bestand "%1" niet gevonden - + OK OK - + Missing yuzu Account Je yuzu account mist - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Om game campatibiliteit te raporteren, moet je je yuzu account koppelen.<br><br/> Om je yuzu account te koppelen, ga naar Emulatie &gt; Configuratie &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Amiibo Bestand (%1);; Alle Bestanden (*.*) - + Load Amiibo Laad Amiibo - - Error opening Amiibo data file - Fout tijdens het openen van het Amiibo gegevens bestand - - - - Unable to open Amiibo file "%1" for reading. - Kan Amiibo bestand "%1" niet lezen. - - - - Error reading Amiibo data file - Fout tijdens het lezen van het Amiibo gegevens bestand - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Kan de volledige Amiibo gegevens niet lezen. Verwacht om %1 bytes te lezen, maar het is alleen mogelijk om %2 bytes te lezen. - - - + Error loading Amiibo data Fout tijdens het laden van de Amiibo data - + Unable to load Amiibo data. Kan de Amiibo gegevens niet laden. - + Capture Screenshot Screenshot Vastleggen - + PNG Image (*.png) PNG afbeelding (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Snelheid: %1% / %2% - + Speed: %1% Snelheid: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Game: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. De game die je probeert te laden heeft extra bestanden nodig van je Switch voordat je het kan spelen. <br/><br/>Voor meer informatie over het dumpen van deze bestanden, volg alsjeblieft onze wiki pagina: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Het dumpen van Systeem Archieven en de Gedeelde Lettertypen van een Switch console </a>. <br/><br/>Wil je terug gaan naar de game lijst? Verdergaan met de emulatie zal misschien gevolgen hebben als vastlopen, beschadigde opslag data, of andere problemen. - + yuzu was unable to locate a Switch system archive. %1 yuzu was niet in staat om de Switch systeem archieven te vinden. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu was niet in staat om de Switch systeem archieven te vinden. %1. %2 - + System Archive Not Found Systeem Archief Niet Gevonden - + System Archive Missing Systeem Archief Mist - + yuzu was unable to locate the Switch shared fonts. %1 yuzu was niet in staat om de Switch shared fonts te vinden. %1 - + Shared Fonts Not Found Shared Fonts Niet Gevonden - + Shared Font Missing Gedeelde Lettertypes Niet Gevonden - + Fatal Error Fatale Fout - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu is een fatale fout tegengekomen, zie de log voor meer details. Voor meer informatie over toegang krijgen tot de log, zie de volgende pagina: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Hoe upload je een log bestand</a>.<br/><br/>Zou je terug willen naar de game lijst? Doorgaan met emulatie kan resulteren in vastlapen, corrupte save gegevens, of andere problemen. - + Fatal Error encountered Fatale Fout opgetreden - + Confirm Key Rederivation Bevestig Sleutel Herafleiding - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5064,37 +5113,37 @@ en optioneel maak backups. Dit zal je automatisch gegenereerde sleutel bestanden verwijderen en de sleutel verkrijger module opnieuw starten - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5102,39 +5151,39 @@ on your system's performance. op je systeem's performatie. - + Deriving Keys Sleutels afleiden - + Select RomFS Dump Target Selecteer RomFS Dump Doel - + Please select which RomFS you would like to dump. Selecteer welke RomFS je zou willen dumpen. - + Are you sure you want to close yuzu? Weet je zeker dat je yuzu wilt sluiten? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Weet je zeker dat je de emulatie wilt stoppen? Alle onopgeslagen voortgang will verloren gaan. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5511,12 +5560,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5525,11 +5574,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5551,112 +5601,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Screenshot Vastleggen - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Volledig Scherm - + Load File Laad Bestand - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5770,42 +5819,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Spelers - + Refreshing - + Refresh List @@ -6132,7 +6181,7 @@ Debug Message: - + Connected Verbonden @@ -6154,7 +6203,7 @@ Debug Message: - + New Messages Received @@ -6258,22 +6307,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6281,7 +6347,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6336,7 +6402,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6391,7 +6457,7 @@ p, li { white-space: pre-wrap; } - + [not set] [niet aangegeven] @@ -6406,10 +6472,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axis %1%2 @@ -6423,9 +6489,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [onbekend] @@ -6590,15 +6656,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6606,35 +6672,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [ongebruikt] @@ -6675,7 +6741,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/pl.ts b/dist/languages/pl.ts index 83b4d2b958..c4f75063a2 100644 --- a/dist/languages/pl.ts +++ b/dist/languages/pl.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } Wyślij Wiadomość - + Members Członkowie - + %1 has joined %1 dołączył/a - + %1 has left %1 wyszedł/ła - + %1 has been kicked %1 został/a wyrzucony/a - + %1 has been banned %1 został/a zbanowany/a - + %1 has been unbanned %1 został/a odbanowany/a - + View Profile Wyświetl Profil - - + + Block Player Zablokuj Gracza - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Po zablokowaniu gracza nie będziesz otrzymywał od niego/jej wiadomości. <br><br>Czy na pewno chcesz zablokować %1? - + Kick Wyrzuć - + Ban Zbanuj - + Kick Player Wyrzuć Gracza - + Are you sure you would like to <b>kick</b> %1? Na pewno chcesz <b>wyrzucić</b> %1? - + Ban Player Zbanuj Gracza - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -758,200 +758,235 @@ Gdy ta opcja jest włączona, niedopasowanie jest uruchamiane tylko wtedy, gdy d ConfigureDebug - + Debugger - + Enable GDB Stub Włącz namiastkę GDB - + Port: Port - + Logging Logowanie - + Global Log Filter Globalny filtr rejestrów - + Show Log in Console Pokaż Log w konsoli - + Open Log Location Otwórz miejsce rejestrów - + When checked, the max size of the log increases from 100 MB to 1 GB Kiedy zaznaczony, maksymalny rozmiar logu wzrasta ze 100 MB do 1 GB - + Enable Extended Logging** Włącz Przedłużony Logging** - + Homebrew Homebrew - + Arguments String Linijka argumentu - + Graphics Grafika - + When checked, the graphics API enters a slower debugging mode Gdy zaznaczone, API grafiki przechodzi w wolniejszy tryb debugowania - + Enable Graphics Debugging Włącz debugowanie grafiki - + When checked, it enables Nsight Aftermath crash dumps Gdy zaznaczone, włącza zrzucanie awarii Nsight Aftermath - + Enable Nsight Aftermath Włącz Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Po zaznaczeniu, zrzuci wszystkie oryginalne shadery asemblera z pamięci podręcznej dysku shaderów albo gry, jeśli zostaną znalezione - + Dump Game Shaders Zrzuć Shadery Gry - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Gdy zaznaczone, wyłącza kompilator makr Just In Time. Włączenie tej opcji spowalnia działanie gier - + Disable Macro JIT Wyłącz Makro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Po zaznaczeniu, yuzu będzie rejestrować statystyki dotyczące skompilowanej pamięci podręcznej. - + Enable Shader Feedback Włącz funkcję Feedbacku Shaderów - + When checked, it executes shaders without loop logic changes Gdy zaznaczone, używa shaderów bez zmian logicznych pętli - + Disable Loop safety checks Wyłącz Zapętlanie sprawdzania bezpieczeństwa - + Debugging Debugowanie - - Enable FS Access Log - Włącz dziennik Dostępu FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Włącz Pełne Usługi Raportowania** - + + Enable FS Access Log + Włącz dziennik Dostępu FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Zaawansowane - + Kiosk (Quest) Mode Tryb Kiosk (Quest) - + Enable CPU Debugging Włącz Debugowanie CPU - + Enable Debug Asserts Włącz potwierdzenia debugowania - + Enable Auto-Stub** Włącz Auto-Stub** - + Enable All Controller Types - + Disable Web Applet Wyłącz Aplet internetowy - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **To zresetuje się automatycznie po wyłączeniu yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1571,37 +1606,47 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności.Użyj Szybszego Czasu GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtrowanie anizotropowe: - + Automatic Automatyczne - + Default Domyślne - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2093,7 +2138,7 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + Left Stick Lewa gałka @@ -2187,14 +2232,14 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + L L - + ZL ZL @@ -2213,7 +2258,7 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + Plus Plus @@ -2226,15 +2271,15 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + R R - + ZR ZR @@ -2291,231 +2336,236 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + Right Stick Prawa gałka - - - - + + + + Clear Wyczyść - - - - - + + + + + [not set] [nie ustawione] - - - Toggle button - Przycisk Toggle - - - - + + Invert button Odwróć przycisk - - + + + Toggle button + Przycisk Toggle + + + + Invert axis Odwróć oś - - - + + + Set threshold Ustaw próg - - + + Choose a value between 0% and 100% Wybierz wartość od 0% do 100% - + + Toggle axis + + + + Set gyro threshold Ustaw próg gyro - + Map Analog Stick Przypisz Drążek Analogowy - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Po naciśnięciu OK, najpierw przesuń joystick w poziomie, a następnie w pionie. Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo. - + Center axis - - + + Deadzone: %1% Martwa strefa: %1% - - + + Modifier Range: %1% Zasięg Modyfikatora: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Para Joyconów - + Left Joycon Lewy Joycon - + Right Joycon Prawy Joycon - + Handheld Handheld - + GameCube Controller Kontroler GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Kontroler NES/Pegasus - + SNES Controller Kontroler SNES - + N64 Controller Kontroler N64 - + Sega Genesis Sega Mega Drive - + Start / Pause Start / Pauza - + Z Z - + Control Stick Lewa gałka - + C-Stick C-gałka - + Shake! Potrząśnij! - + [waiting] [oczekiwanie] - + New Profile Nowy profil - + Enter a profile name: Wpisz nazwę profilu: - - + + Create Input Profile Utwórz profil wejściowy - + The given profile name is not valid! Podana nazwa profilu jest nieprawidłowa! - + Failed to create the input profile "%1" Nie udało się utworzyć profilu wejściowego "%1" - + Delete Input Profile Usuń profil wejściowy - + Failed to delete the input profile "%1" Nie udało się usunąć profilu wejściowego "%1" - + Load Input Profile Załaduj profil wejściowy - + Failed to load the input profile "%1" Nie udało się wczytać profilu wejściowego "%1" - + Save Input Profile Zapisz profil wejściowy - + Failed to save the input profile "%1" Nie udało się zapisać profilu wejściowego "%1" @@ -2770,42 +2820,42 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo.Deweloper - + Add-Ons Dodatki - + General Ogólne - + System System - + CPU CPU - + Graphics Grafika - + Adv. Graphics Zaaw. Grafika - + Audio Dźwięk - + Properties Właściwości @@ -3517,47 +3567,47 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo.<html><head/><body><p>Odczytuje dane wejściowe kontrolera ze skryptów takiego samego formatu jak skrypty TAS-nx.<br/> Jeśli chcesz otrzymać bardziej szczegółowe wyjaśnienie, wejdź na <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">stronę pomocy</span></a> na stronie internetowej yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Aby sprawdzić jakie skróty sterują odtwarzaniem/nagrywaniem, odnieś się do ustawień Skrótów (Konfiguruj -> Ogólne -> Skróty Klawiszowe). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. UWAGA: To jest funkcja eksperymentalna. Nie będzie odtwarzała skryptów co do klatki z teraźniejszą, nieperfekcyjną metodą synchronizacji. - + Settings Ustawienia - + Enable TAS features Włącz funkcje TAS - + Loop script Zapętlij skrypt - + Pause execution during loads Zatrzymuj wykonanie w trakcie ładowania - + Script Directory Ścieżka Skryptu - + Path Ścieżka - + ... ... @@ -3967,7 +4017,7 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe - + Verify Zweryfikuj @@ -4054,7 +4104,7 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe - + Unspecified Nieokreślona @@ -4069,17 +4119,36 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe Token nie został zweryfikowany. Zmiana w Twoim tokenie nie została zapisana. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Weryfikowanie... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Weryfikowanie nieudane + + + Verification failed Weryfikowanie nieudane - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Weryfikacja nie powiodła się. Sprawdź, czy poprawnie podałeś swój token oraz czy działa twoje połączenie internetowe. @@ -4148,12 +4217,12 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe DirectConnectWindow - + Connecting Łączenie - + Connect Połącz @@ -4161,489 +4230,489 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dane anonimowe są gromadzone</a> aby ulepszyć yuzu. <br/><br/>Czy chcesz udostępnić nam swoje dane o użytkowaniu? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Ładowanie apletu internetowego... - + Disable Web Applet Wyłącz Aplet internetowy - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Wyłączanie web appletu może doprowadzić do nieokreślonych zachowań - wyłączyć applet należy jedynie grając w Super Mario 3D All-Stars. Na pewno chcesz wyłączyć web applet? (Można go ponownie włączyć w ustawieniach debug.) - + The amount of shaders currently being built Ilość budowanych shaderów - + The current selected resolution scaling multiplier. Obecnie wybrany mnożnik rozdzielczości. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktualna prędkość emulacji. Wartości większe lub niższe niż 100% wskazują, że emulacja działa szybciej lub wolniej niż Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Ile klatek na sekundę gra aktualnie wyświetla. To będzie się różnić w zależności od gry, od sceny do sceny. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Czas potrzebny do emulacji klatki na sekundę Switcha, nie licząc ograniczania klatek ani v-sync. Dla emulacji pełnej szybkości powinno to wynosić co najwyżej 16,67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Usuń Ostatnie pliki - + &Continue &Kontynuuj - + &Pause &Pauza - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu jest w trakcie gry - + Warning Outdated Game Format OSTRZEŻENIE! Nieaktualny format gry - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Używasz zdekonstruowanego formatu katalogu ROM dla tej gry, który jest przestarzałym formatem, który został zastąpiony przez inne, takie jak NCA, NAX, XCI lub NSP. W zdekonstruowanych katalogach ROM brakuje ikon, metadanych i obsługi aktualizacji.<br><br> Aby znaleźć wyjaśnienie różnych formatów Switch obsługiwanych przez yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'> sprawdź nasze wiki</a>. Ta wiadomość nie pojawi się ponownie. - - + + Error while loading ROM! Błąd podczas wczytywania ROMu! - + The ROM format is not supported. Ten format ROMu nie jest wspierany. - + An error occurred initializing the video core. Wystąpił błąd podczas inicjowania rdzenia wideo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu napotkał błąd podczas uruchamiania rdzenia wideo. Jest to zwykle spowodowane przestarzałymi sterownikami GPU, w tym zintegrowanymi. Więcej szczegółów znajdziesz w pliku log. Więcej informacji na temat dostępu do log-u można znaleźć na następującej stronie: <a href='https://yuzu-emu.org/help/reference/log-files/'>Jak przesłać plik log</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Błąd podczas wczytywania ROMu! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Postępuj zgodnie z<a href='https://yuzu-emu.org/help/quickstart/'>yuzu quickstart guide</a> aby zrzucić ponownie swoje pliki.<br>Możesz odwołać się do wiki yuzu</a>lub discord yuzu </a> po pomoc. - + An unknown error occurred. Please see the log for more details. Wystąpił nieznany błąd. Więcej informacji można znaleźć w pliku log. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Zapis danych - + Mod Data Dane modów - + Error Opening %1 Folder Błąd podczas otwarcia folderu %1 - - + + Folder does not exist! Folder nie istnieje! - + Error Opening Transferable Shader Cache Błąd podczas otwierania przenośnej pamięci podręcznej Shaderów. - + Failed to create the shader cache directory for this title. Nie udało się stworzyć ścieżki shaderów dla tego tytułu. - + Contents Zawartość - + Update Łatka - + DLC DLC - + Remove Entry Usuń wpis - + Remove Installed Game %1? Usunąć zainstalowaną grę %1? - - - - - - + + + + + + Successfully Removed Pomyślnie usunięto - + Successfully removed the installed base game. Pomyślnie usunięto zainstalowaną grę. - - - + + + Error Removing %1 Błąd podczas usuwania %1 - + The base game is not installed in the NAND and cannot be removed. Gra nie jest zainstalowana w NAND i nie może zostać usunięta. - + Successfully removed the installed update. Pomyślnie usunięto zainstalowaną łatkę. - + There is no update installed for this title. Brak zainstalowanych łatek dla tego tytułu. - + There are no DLC installed for this title. Brak zainstalowanych DLC dla tego tytułu. - + Successfully removed %1 installed DLC. Pomyślnie usunięto %1 zainstalowane DLC. - + Delete OpenGL Transferable Shader Cache? Usunąć Transferowalne Shadery OpenGL? - + Delete Vulkan Transferable Shader Cache? Usunąć Transferowalne Shadery Vulkan? - + Delete All Transferable Shader Caches? Usunąć Wszystkie Transferowalne Shadery? - + Remove Custom Game Configuration? Usunąć niestandardową konfigurację gry? - + Remove File Usuń plik - - + + Error Removing Transferable Shader Cache Błąd podczas usuwania przenośnej pamięci podręcznej Shaderów. - - + + A shader cache for this title does not exist. Pamięć podręczna Shaderów dla tego tytułu nie istnieje. - + Successfully removed the transferable shader cache. Pomyślnie usunięto przenośną pamięć podręczną Shaderów. - + Failed to remove the transferable shader cache. Nie udało się usunąć przenośnej pamięci Shaderów. - - + + Error Removing Transferable Shader Caches Błąd podczas usuwania Transferowalnych Shaderów - + Successfully removed the transferable shader caches. Pomyślnie usunięto transferowalne shadery. - + Failed to remove the transferable shader cache directory. Nie udało się usunąć ścieżki transferowalnych shaderów. - - + + Error Removing Custom Configuration Błąd podczas usuwania niestandardowej konfiguracji - + A custom configuration for this title does not exist. Niestandardowa konfiguracja nie istnieje dla tego tytułu. - + Successfully removed the custom game configuration. Pomyślnie usunięto niestandardową konfiguracje gry. - + Failed to remove the custom game configuration. Nie udało się usunąć niestandardowej konfiguracji gry. - - + + RomFS Extraction Failed! Wypakowanie RomFS nieudane! - + There was an error copying the RomFS files or the user cancelled the operation. Wystąpił błąd podczas kopiowania plików RomFS lub użytkownik anulował operację. - + Full Pełny - + Skeleton Szkielet - + Select RomFS Dump Mode Wybierz tryb zrzutu RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Proszę wybrać w jaki sposób chcesz, aby zrzut pliku RomFS został wykonany. <br>Pełna kopia ze wszystkimi plikami do nowego folderu, gdy <br>skielet utworzy tylko strukturę folderu. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Nie ma wystarczająco miejsca w %1 aby wyodrębnić RomFS. Zwolnij trochę miejsca, albo zmień ścieżkę zrzutu RomFs w Emulacja> Konfiguruj> System> System Plików> Źródło Zrzutu - + Extracting RomFS... Wypakowywanie RomFS... - - + + Cancel Anuluj - + RomFS Extraction Succeeded! Wypakowanie RomFS zakończone pomyślnie! - + The operation completed successfully. Operacja zakończona sukcesem. - + Error Opening %1 Błąd podczas otwierania %1 - + Select Directory Wybierz folder... - + Properties Właściwości - + The game properties could not be loaded. Właściwości tej gry nie mogły zostać załadowane. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Plik wykonywalny Switcha (%1);;Wszystkie pliki (*.*) - + Load File Załaduj plik... - + Open Extracted ROM Directory Otwórz folder wypakowanego ROMu - + Invalid Directory Selected Wybrano niewłaściwy folder - + The directory you have selected does not contain a 'main' file. Folder wybrany przez ciebie nie zawiera 'głownego' pliku. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Instalacyjne pliki Switch'a (*.nca *.nsp *.xci);;Archiwum zawartości Nintendo (*.nca);;Pakiet poddany Nintendo (*.nsp);;Obraz z kartridża NX (*.xci) - + Install Files Zainstaluj pliki - + %n file(s) remaining 1 plik został%n plików zostało%n plików zostało%n plików zostało - + Installing file "%1"... Instalowanie pliku "%1"... - - + + Install Results Wynik instalacji - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Aby uniknąć ewentualnych konfliktów, odradzamy użytkownikom instalowanie gier na NAND. Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were newly installed 1 nowy plik został zainstalowany @@ -4653,424 +4722,404 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were overwritten 1 plik został nadpisany%n plików zostało nadpisane%n plików zostało nadpisane%n plików zostało nadpisane - + %n file(s) failed to install 1 pliku nie udało się zainstalować%n plików nie udało się zainstalować%n plików nie udało się zainstalować%n plików nie udało się zainstalować - + System Application Aplikacja systemowa - + System Archive Archiwum systemu - + System Application Update Aktualizacja aplikacji systemowej - + Firmware Package (Type A) Paczka systemowa (Typ A) - + Firmware Package (Type B) Paczka systemowa (Typ B) - + Game Gra - + Game Update Aktualizacja gry - + Game DLC Dodatek do gry - + Delta Title Tytuł Delta - + Select NCA Install Type... Wybierz typ instalacji NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Wybierz typ tytułu, do którego chcesz zainstalować ten NCA, jako: (W większości przypadków domyślna "gra" jest w porządku.) - + Failed to Install Instalacja nieudana - + The title type you selected for the NCA is invalid. Typ tytułu wybrany dla NCA jest nieprawidłowy. - + File not found Nie znaleziono pliku - + File "%1" not found Nie znaleziono pliku "%1" - + OK OK - + Missing yuzu Account Brakuje konta Yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Aby przesłać test zgodności gry, musisz połączyć swoje konto yuzu.<br><br/> Aby połączyć swoje konto yuzu, przejdź do opcji Emulacja &gt; Konfiguracja &gt; Sieć. - + Error opening URL Błąd otwierania adresu URL - + Unable to open the URL "%1". Nie można otworzyć adresu URL "%1". - + TAS Recording Nagrywanie TAS - + Overwrite file of player 1? Nadpisać plik gracza 1? - + Invalid config detected Wykryto nieprawidłową konfigurację - + Handheld controller can't be used on docked mode. Pro controller will be selected. Nie można używać kontrolera handheld w trybie zadokowanym. Zostanie wybrany kontroler Pro. - - + + Error Błąd - - + + The current game is not looking for amiibos Ta gra nie szuka amiibo - - + + Amiibo Amiibo - - + + The current amiibo has been removed Amiibo zostało "zdjęte" - + Amiibo File (%1);; All Files (*.*) Plik Amiibo (%1);;Wszyskie pliki (*.*) - + Load Amiibo Załaduj Amiibo - - Error opening Amiibo data file - Błąd otwarcia pliku danych Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Nie można otworzyć pliku Amiibo "%1" do odczytu. - - - - Error reading Amiibo data file - Błąd podczas odczytu pliku danych Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Nie można w pełni odczytać danych Amiibo. Oczekiwano odczytu %1 bajtów, ale był on w stanie odczytać tylko %2 bajty. - - - + Error loading Amiibo data Błąd podczas ładowania pliku danych Amiibo - + Unable to load Amiibo data. Nie można załadować danych Amiibo. - + Capture Screenshot Zrób zrzut ekranu - + PNG Image (*.png) Obrazek PNG (*.png) - + TAS state: Running %1/%2 Status TAS: Działa %1%2 - + TAS state: Recording %1 Status TAS: Nagrywa %1 - + TAS state: Idle %1/%2 Status TAS: Bezczynny %1%2 - + TAS State: Invalid Status TAS: Niepoprawny - + &Stop Running &Wyłącz - + &Start &Start - + Stop R&ecording Przestań N&agrywać - + R&ecord N&agraj - + Building: %n shader(s) Budowanie shaderaBudowanie: %n shaderówBudowanie: %n shaderówBudowanie: %n shaderów - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Prędkość: %1% / %2% - + Speed: %1% Prędkość: %1% - + Game: %1 FPS (Unlocked) Gra: %1 FPS (Odblokowane) - + Game: %1 FPS Gra: %1 FPS - + Frame: %1 ms Klatka: %1 ms - + GPU NORMAL GPU NORMALNE - + GPU HIGH GPU WYSOKIE - + GPU EXTREME GPU EKSTREMALNE - + GPU ERROR BŁĄD GPU - + DOCKED TRYB ZADOKOWANY - + HANDHELD TRYB PRZENOŚNY - + NEAREST NAJBLIŻSZY - - + + BILINEAR BILINEARNY - + BICUBIC BIKUBICZNY - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA BEZ AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Gra, którą próbujesz wczytać, wymaga dodatkowych plików z Switch'a, które zostaną zrzucone przed graniem.<br/><br/> Aby uzyskać więcej informacji na temat wyrzucania tych plików, odwiedź następującą stronę wiki:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'> Zrzut archiw systemu i udostępnionych czcionek z konsoli Nintendo Switch</a>. <br/><br/>Czy chcesz wrócić do listy gier? Kontynuacja emulacji może spowodować awarie, uszkodzone dane zapisu lub inne błędy. - + yuzu was unable to locate a Switch system archive. %1 yuzu nie był w stanie znaleźć archiwum systemu Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu nie był w stanie znaleźć archiwum systemu Switch. %1. %2 - + System Archive Not Found Archiwum systemu nie znalezione. - + System Archive Missing Brak archiwum systemowego - + yuzu was unable to locate the Switch shared fonts. %1 yuzu nie był w stanie zlokalizować czcionek Switch'a. %1 - + Shared Fonts Not Found Czcionki nie zostały znalezione - + Shared Font Missing Brak wspólnej czcionki - + Fatal Error Fatalny błąd - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu napotkał błąd, proszę zobaczyć log po więcej szczegółów. Aby uzyskać więcej informacji na temat uzyskiwania dostępu do pliku log, zobacz następującą stronę: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Jak przesłać plik log</a>?<br/><br/> Czy chcesz wrócić do listy gier? Kontynuacja emulacji może spowodować awarie, uszkodzone dane zapisu lub inne błędy. - + Fatal Error encountered Wystąpił błąd krytyczny - + Confirm Key Rederivation Potwierdź ponowną aktywacje klucza - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5087,37 +5136,37 @@ i opcjonalnie tworzyć kopie zapasowe. Spowoduje to usunięcie wygenerowanych automatycznie plików kluczy i ponowne uruchomienie modułu pochodnego klucza. - + Missing fuses Brakujące bezpieczniki - + - Missing BOOT0 - Brak BOOT0 - + - Missing BCPKG2-1-Normal-Main - Brak BCPKG2-1-Normal-Main - + - Missing PRODINFO - Brak PRODINFO - + Derivation Components Missing Brak komponentów wyprowadzania - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Brakuje elementów, które mogą uniemożliwić zakończenie wyprowadzania kluczy. <br>Postępuj zgodnie z <a href='https://yuzu-emu.org/help/quickstart/'>yuzu quickstart guide</a> aby zdobyć wszystkie swoje klucze i gry.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5126,39 +5175,39 @@ Zależnie od tego może potrwać do minuty na wydajność twojego systemu. - + Deriving Keys Wyprowadzanie kluczy... - + Select RomFS Dump Target Wybierz cel zrzutu RomFS - + Please select which RomFS you would like to dump. Proszę wybrać RomFS, jakie chcesz zrzucić. - + Are you sure you want to close yuzu? Czy na pewno chcesz zamknąć yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Czy na pewno chcesz zatrzymać emulację? Wszystkie niezapisane postępy zostaną utracone. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5540,12 +5589,12 @@ startowy. HostRoomWindow - + Error Błąd - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5554,11 +5603,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute Wycisz/Odcisz Audio + @@ -5580,112 +5630,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Zrób zrzut ekranu - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation Kontynuuj/Zatrzymaj Emulację - + Exit Fullscreen Wyłącz Pełny Ekran - + Exit yuzu Wyjdź z yuzu - + Fullscreen Pełny ekran - + Load File Załaduj plik... - + Load/Remove Amiibo Załaduj/Usuń Amiibo - + Restart Emulation Zrestartuj Emulację - + Stop Emulation Zatrzymaj Emulację - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5800,42 +5849,42 @@ Debug Message: Odśwież Lobby - + Password Required to Join Aby dołączyć, potrzebne jest hasło - + Password: Hasło: - + Room Name Nazwa Pokoju - + Preferred Game Preferowana Gra - + Host Host - + Players Gracze - + Refreshing Odświeżam - + Refresh List Odśwież listę @@ -6162,7 +6211,7 @@ Debug Message: - + Connected Połączony @@ -6184,7 +6233,7 @@ Debug Message: - + New Messages Received Otrzymano nowe wiadomości @@ -6289,22 +6338,39 @@ They may have left the room. Możliwe, że opuścił/a pokój. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Wyjdź z Pokoju Multiplayer - + You are about to close the room. Any network connections will be closed. Zamierzasz zamknąć pokój multiplayer. Wszystkie połączenia zostaną zamknięte. - + Disconnect Rozłącz - + You are about to leave the room. Any network connections will be closed. Wychodzisz z pokoju multiplayer. Wszystkie połączenia zostaną zamknięte. @@ -6312,7 +6378,7 @@ Możliwe, że opuścił/a pokój. NetworkMessage::ErrorManager - + Error Błąd @@ -6371,7 +6437,7 @@ p, li { white-space: pre-wrap; } %1 gra w %2 - + Not playing a game Nie gra w żadną grę @@ -6426,7 +6492,7 @@ p, li { white-space: pre-wrap; } - + [not set] [nie ustawione] @@ -6441,10 +6507,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Oś %1%2 @@ -6458,9 +6524,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [nieznane] @@ -6625,15 +6691,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [niepoprawne] - - + + %1%2Hat %3 %1%2Drążek %3 @@ -6641,35 +6707,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Oś %3 - + %1%2Axis %3,%4,%5 %1%2Oś %3,%4,%5 - + %1%2Motion %3 %1%2Ruch %3 - - + + %1%2Button %3 %1%2Przycisk %3 - + [unused] [nieużywane] @@ -6710,7 +6776,7 @@ p, li { white-space: pre-wrap; } Dodatkowe - + %1%2%3 %1%2%3 diff --git a/dist/languages/pt_BR.ts b/dist/languages/pt_BR.ts index 40a8a4cf64..bee6aa5b92 100644 --- a/dist/languages/pt_BR.ts +++ b/dist/languages/pt_BR.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Site</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Código fonte</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contribuidores</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licença</span></a></p></body></html> @@ -82,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Janela da sala Send Chat Message - + Enviar mensagem no bate-papo Send Message - + Enviar mensagem - + Members - + Membros - + %1 has joined - + %1 entrou - + %1 has left - + %1 saiu - + %1 has been kicked - + %1 foi expulso(a) - + %1 has been banned - + %1 foi banido(a) - + %1 has been unbanned - - - - - View Profile - + %1 foi desbanido(a) - - Block Player - + View Profile + Ver perfil - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + + + Block Player + Bloquear jogador + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Quando bloqueia um jogador, você não receberá mais mensagens dele.<br><br>Você deseja mesmo bloquear %1? + + + Kick - - - - - Ban - - - - - Kick Player - - - - - Are you sure you would like to <b>kick</b> %1? - + Expulsar - Ban Player - + Ban + Banir - + + Kick Player + Expulsar jogador + + + + Are you sure you would like to <b>kick</b> %1? + Você deseja mesmo <b>expulsar</b> %1? + + + + Ban Player + Banir jogador + + + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Você deseja mesmo <b>expulsar e banir</b> %1? + +Isto banirá tanto o nome de usuário do fórum como o endereço IP. @@ -178,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Janela da sala Room Description - + Descrição da sala Moderation... - + Moderação... Leave Room - + Sair da sala @@ -206,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + Desconectado %1 (%2/%3 members) - connected - + %1 (%2/%3 membros) - conectado @@ -339,7 +341,7 @@ This would ban both their forum username and their IP address. Output Device - + Dispositivo de saída @@ -378,37 +380,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Configurar câmera infravermelha Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Selecione de onde vem a imagem da câmera emulada. Pode ser uma câmera virtual ou uma câmera real. Camera Image Source: - + Origem da imagem da câmera: Input device: - + Dispositivo de entrada: Preview - + Prévia Resolution: 320*240 - + Resolução: 320*240 Click to preview - + Clique para pré-visualizar @@ -769,200 +771,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Depurador - + Enable GDB Stub Ativar GDB stub - + Port: Porta: - + Logging Registros de depuração - + Global Log Filter Filtro global de registros - + Show Log in Console Mostrar registro no console - + Open Log Location Abrir local dos registros - + When checked, the max size of the log increases from 100 MB to 1 GB Quando ativado, o tamanho máximo do arquivo de registro aumenta de 100 MB para 1 GB - + Enable Extended Logging** Ativar registros avançados** - + Homebrew Homebrew - + Arguments String Linha de argumentos - + Graphics Gráficos - + When checked, the graphics API enters a slower debugging mode Quando ativado, a API gráfica entra em um modo de depuração mais lento. - + Enable Graphics Debugging Ativar depuração de gráficos - + When checked, it enables Nsight Aftermath crash dumps Quando ativado, ativa a extração de registros de travamento do Nsight Aftermath - + Enable Nsight Aftermath Ativar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Se selecionado, extrai todos os shaders originários do cache do disco ou do jogo conforme encontrá-los. - + Dump Game Shaders Descarregar shaders do jogo - + When checked, it will dump all the macro programs of the GPU Quando marcada, essa opção irá extrair todos os macro programas da GPU - + Dump Maxwell Macros Extrair macros Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Quando ativado, desativa o macro compilador Just In Time. Ativar isto faz os jogos rodarem mais lentamente. - + Disable Macro JIT Desativar macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Quando ativado, o yuzu registrará estatísticas sobre o cache de pipeline compilado - + Enable Shader Feedback Ativar Feedback de Shaders - + When checked, it executes shaders without loop logic changes Quando ativado, executa shaders sem mudanças de lógica de loop - + Disable Loop safety checks Desativar verificação de segurança de loops - + Debugging Depuração - - Enable FS Access Log - Ativar acesso de registro FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Ativar serviços de relatório detalhado** - + + Enable FS Access Log + Ativar acesso de registro FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avançado - + Kiosk (Quest) Mode Modo quiosque (Quest) - + Enable CPU Debugging Ativar depuração de CPU - + Enable Debug Asserts Ativar asserções de depuração - + Enable Auto-Stub** Ativar auto-esboço** - + Enable All Controller Types Ativar todos os tipos de controles - + Disable Web Applet Desativar o applet da web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Isto será restaurado automaticamente assim que o yuzu for fechado. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1581,37 +1618,47 @@ This would ban both their forum username and their IP address. Usar tempo de resposta rápido da GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtragem anisotrópica: - + Automatic Automático - + Default Padrão - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2103,7 +2150,7 @@ This would ban both their forum username and their IP address. - + Left Stick Analógico esquerdo @@ -2197,14 +2244,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2223,7 +2270,7 @@ This would ban both their forum username and their IP address. - + Plus Mais @@ -2236,15 +2283,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2301,231 +2348,236 @@ This would ban both their forum username and their IP address. - + Right Stick Analógico direito - - - - + + + + Clear Limpar - - - - - + + + + + [not set] [não definido] - - - Toggle button - Alternar pressionamento do botão - - - - + + Invert button Inverter botão - - + + + Toggle button + Alternar pressionamento do botão + + + + Invert axis Inverter eixo - - - + + + Set threshold Definir limite - - + + Choose a value between 0% and 100% Escolha um valor entre 0% e 100% - + + Toggle axis + + + + Set gyro threshold Definir limite do giroscópio - + Map Analog Stick Mapear analógico - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Após pressionar OK, mova o seu direcional analógico primeiro horizontalmente e depois verticalmente. Para inverter os eixos, mova seu analógico primeiro verticalmente e depois horizontalmente. - + Center axis Eixo central - - + + Deadzone: %1% Zona morta: %1% - - + + Modifier Range: %1% Alcance de modificador: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Par de Joycons - + Left Joycon Joycon Esquerdo - + Right Joycon Joycon Direito - + Handheld Portátil - + GameCube Controller Controle de GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Controle NES - + SNES Controller Controle SNES - + N64 Controller Controle N64 - + Sega Genesis Mega Drive - + Start / Pause Iniciar / Pausar - + Z Z - + Control Stick Direcional de controle - + C-Stick C-Stick - + Shake! Balance! - + [waiting] [esperando] - + New Profile Novo perfil - + Enter a profile name: Insira um nome para o perfil: - - + + Create Input Profile Criar perfil de controle - + The given profile name is not valid! O nome de perfil inserido não é válido! - + Failed to create the input profile "%1" Falha ao criar o perfil de controle "%1" - + Delete Input Profile Excluir perfil de controle - + Failed to delete the input profile "%1" Falha ao excluir o perfil de controle "%1" - + Load Input Profile Carregar perfil de controle - + Failed to load the input profile "%1" Falha ao carregar o perfil de controle "%1" - + Save Input Profile Salvar perfil de controle - + Failed to save the input profile "%1" Falha ao salvar o perfil de controle "%1" @@ -2780,42 +2832,42 @@ Para inverter os eixos, mova seu analógico primeiro verticalmente e depois hori Desenvolvedor - + Add-Ons Adicionais - + General Geral - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráf. avançados - + Audio Áudio - + Properties Propriedades @@ -3527,47 +3579,47 @@ Para inverter os eixos, mova seu analógico primeiro verticalmente e depois hori <html><head/><body><p>Lê entradas de controle a partir de scripts no mesmo formato que TAS-nx. <br/>Para uma explicação mais detalhada, por favor consulte a <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">página de ajuda</span></a> no website do yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Para checar que atalhos controlam rodar/gravar, por favor refira-se às Teclas de atalhos (Configurar -> Geral -> Teclas de atalhos) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ATENÇÃO: Este é um recurso experimental. <br/>Não irá rodar os scrips em quadros perfeitos com o atual, imperfeito método de sincronização. - + Settings Configurações - + Enable TAS features Ativar funcionalidades TAS - + Loop script Repetir script em loop - + Pause execution during loads Pausar execução durante carregamentos - + Script Directory Diretório do script - + Path Caminho - + ... ... @@ -3977,7 +4029,7 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe - + Verify Verificar @@ -4064,7 +4116,7 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe - + Unspecified Não especificado @@ -4079,17 +4131,36 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe O token não foi verificado. A alteração no seu token não foi salva. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verificando... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Falha na verificação + + + Verification failed Falha na verificação - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Falha na verificação. Verifique se o seu token foi inserido corretamente e se a sua conexão à internet está funcionando. @@ -4158,12 +4229,12 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe DirectConnectWindow - + Connecting - + Connect @@ -4171,488 +4242,488 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dados anônimos são recolhidos</a> para ajudar a melhorar o yuzu. <br/><br/>Gostaria de compartilhar os seus dados de uso conosco? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Carregando applet web... - + Disable Web Applet Desativar o applet da web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) A desativação do applet da web pode causar comportamento inesperado e deve apenas ser usada com Super Mario 3D All-Stars. Você deseja mesmo desativar o applet da web? (Ele pode ser reativado nas configurações de depuração.) - + The amount of shaders currently being built A quantidade de shaders sendo construídos - + The current selected resolution scaling multiplier. O atualmente multiplicador de escala de resolução selecionado. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidade atual de emulação. Valores maiores ou menores que 100% indicam que a emulação está rodando mais rápida ou lentamente que em um Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quantos quadros por segundo o jogo está exibindo atualmente. Isto irá variar de jogo para jogo e cena para cena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo que leva para emular um quadro do Switch, sem considerar o limitador de taxa de quadros ou a sincronização vertical. Um valor menor ou igual a 16.67 ms indica que a emulação está em velocidade plena. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Limpar arquivos recentes - + &Continue &Continuar - + &Pause &Pausar - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu está rodando um jogo - + Warning Outdated Game Format Aviso - formato de jogo desatualizado - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Você está usando neste jogo o formato de ROM desconstruída e extraída em uma pasta, que é um formato desatualizado que foi substituído por outros, como NCA, NAX, XCI ou NSP. Pastas desconstruídas de ROMs não possuem ícones, metadados e suporte a atualizações.<br><br>Para saber mais sobre os vários formatos de ROMs de Switch compatíveis com o yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>confira a nossa wiki</a>. Esta mensagem não será exibida novamente. - - + + Error while loading ROM! Erro ao carregar a ROM! - + The ROM format is not supported. O formato da ROM não é suportado. - + An error occurred initializing the video core. Ocorreu um erro ao inicializar o núcleo de vídeo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu encontrou um erro enquanto rodando o núcleo de vídeo. Normalmente isto é causado por drivers de GPU desatualizados, incluindo integrados. Por favor veja o registro para mais detalhes. Para mais informações em acesso ao registro por favor veja a seguinte página: <a href='https://yuzu-emu.org/help/reference/log-files/'>Como fazer envio de arquivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Erro ao carregar a ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>o guia de início rápido</a> para reextrair os seus arquivos.<br>Você pode consultar a wiki do yuzu</a> ou o Discord do yuzu</a> para obter ajuda. - + An unknown error occurred. Please see the log for more details. Ocorreu um erro desconhecido. Consulte o registro para mais detalhes. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Dados de jogos salvos - + Mod Data Dados de mods - + Error Opening %1 Folder Erro ao abrir a pasta %1 - - + + Folder does not exist! A pasta não existe! - + Error Opening Transferable Shader Cache Erro ao abrir o cache de shaders transferível - + Failed to create the shader cache directory for this title. Falha ao criar o diretório de cache de shaders para este título. - + Contents Conteúdo - + Update Atualização - + DLC DLC - + Remove Entry Remover item - + Remove Installed Game %1? Remover o jogo instalado %1? - - - - - - + + + + + + Successfully Removed Removido com sucesso - + Successfully removed the installed base game. O jogo base foi removido com sucesso. - - - + + + Error Removing %1 Erro ao remover %1 - + The base game is not installed in the NAND and cannot be removed. O jogo base não está instalado na NAND e não pode ser removido. - + Successfully removed the installed update. A atualização instalada foi removida com sucesso. - + There is no update installed for this title. Não há nenhuma atualização instalada para este título. - + There are no DLC installed for this title. Não há nenhum DLC instalado para este título. - + Successfully removed %1 installed DLC. %1 DLC(s) instalados foram removidos com sucesso. - + Delete OpenGL Transferable Shader Cache? Apagar o cache de shaders transferível do OpenGL? - + Delete Vulkan Transferable Shader Cache? Apagar o cache de shaders transferível do Vulkan? - + Delete All Transferable Shader Caches? Apagar todos os caches de shaders transferíveis? - + Remove Custom Game Configuration? Remover configurações customizadas do jogo? - + Remove File Remover arquivo - - + + Error Removing Transferable Shader Cache Erro ao remover cache de shaders transferível - - + + A shader cache for this title does not exist. Não existe um cache de shaders para este título. - + Successfully removed the transferable shader cache. O cache de shaders transferível foi removido com sucesso. - + Failed to remove the transferable shader cache. Falha ao remover o cache de shaders transferível. - - + + Error Removing Transferable Shader Caches Erro ao remover os caches de shaders transferíveis - + Successfully removed the transferable shader caches. Os caches de shaders transferíveis foram removidos com sucesso. - + Failed to remove the transferable shader cache directory. Falha ao remover o diretório do cache de shaders transferível. - - + + Error Removing Custom Configuration Erro ao remover as configurações customizadas do jogo. - + A custom configuration for this title does not exist. Não há uma configuração customizada para este título. - + Successfully removed the custom game configuration. As configurações customizadas do jogo foram removidas com sucesso. - + Failed to remove the custom game configuration. Falha ao remover as configurações customizadas do jogo. - - + + RomFS Extraction Failed! Falha ao extrair RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Houve um erro ao copiar os arquivos RomFS ou o usuário cancelou a operação. - + Full Extração completa - + Skeleton Apenas estrutura - + Select RomFS Dump Mode Selecione o modo de extração do RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Selecione a forma como você gostaria que o RomFS seja extraído.<br>"Extração completa" copiará todos os arquivos para a nova pasta, enquanto que <br>"Apenas estrutura" criará apenas a estrutura de pastas. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Não há espaço suficiente em %1 para extrair o RomFS. Por favor abra espaço ou selecione um diretório diferente em Emulação > Configurar > Sistema > Sistema de arquivos > Extrair raiz - + Extracting RomFS... Extraindo RomFS... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! Extração do RomFS concluida! - + The operation completed successfully. A operação foi concluída com sucesso. - + Error Opening %1 Erro ao abrir %1 - + Select Directory Selecionar pasta - + Properties Propriedades - + The game properties could not be loaded. As propriedades do jogo não puderam ser carregadas. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Executável do Switch (%1);;Todos os arquivos (*.*) - + Load File Carregar arquivo - + Open Extracted ROM Directory Abrir pasta da ROM extraída - + Invalid Directory Selected Pasta inválida selecionada - + The directory you have selected does not contain a 'main' file. A pasta que você selecionou não contém um arquivo 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Arquivo de Switch instalável (*.nca *.nsp *.xci);; Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Instalar arquivos - + %n file(s) remaining %n arquivo restante%n arquivo(s) restante(s)%n arquivo(s) restante(s) - + Installing file "%1"... Instalando arquivo "%1"... - - + + Install Results Resultados da instalação - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar possíveis conflitos, desencorajamos que os usuários instalem os jogos base na NAND. Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + %n file(s) were newly installed %n arquivo(s) instalado(s) @@ -4661,7 +4732,7 @@ Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + %n file(s) were overwritten %n arquivo(s) sobrescrito(s) @@ -4670,7 +4741,7 @@ Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + %n file(s) failed to install %n arquivo(s) não instalado(s) @@ -4679,411 +4750,391 @@ Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + System Application Aplicativo do sistema - + System Archive Arquivo do sistema - + System Application Update Atualização de aplicativo do sistema - + Firmware Package (Type A) Pacote de firmware (tipo A) - + Firmware Package (Type B) Pacote de firmware (tipo B) - + Game Jogo - + Game Update Atualização de jogo - + Game DLC DLC de jogo - + Delta Title Título delta - + Select NCA Install Type... Selecione o tipo de instalação do NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Selecione o tipo de título como o qual você gostaria de instalar este NCA: (Na maioria dos casos, o padrão 'Jogo' serve bem.) - + Failed to Install Falha ao instalar - + The title type you selected for the NCA is invalid. O tipo de título que você selecionou para o NCA é inválido. - + File not found Arquivo não encontrado - + File "%1" not found Arquivo "%1" não encontrado - + OK OK - + Missing yuzu Account Conta do yuzu faltando - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Para enviar um caso de teste de compatibilidade de jogo, você precisa entrar com a sua conta do yuzu.<br><br/>Para isso, vá para Emulação &gt; Configurar... &gt; Rede. - + Error opening URL Erro ao abrir URL - + Unable to open the URL "%1". Não foi possível abrir o URL "%1". - + TAS Recording Gravando TAS - + Overwrite file of player 1? Sobrescrever arquivo do jogador 1? - + Invalid config detected Configuração inválida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. O controle portátil não pode ser usado no modo encaixado na base. O Pro Controller será selecionado. - - + + Error Erro - - + + The current game is not looking for amiibos O jogo atual não está procurando amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed O amiibo atual foi removido - + Amiibo File (%1);; All Files (*.*) Arquivo Amiibo (%1);; Todos os arquivos (*.*) - + Load Amiibo Carregar Amiibo - - Error opening Amiibo data file - Erro ao abrir arquivo de dados do Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Não foi possível abrir o arquivo de Amiibo "%1" para leitura. - - - - Error reading Amiibo data file - Erro ao ler arquivo de dados de Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Não foi possível ler completamente os dados do Amiibo. O yuzu esperava ler %1 bytes, mas foi capaz de ler apenas %2 bytes. - - - + Error loading Amiibo data Erro ao carregar dados do Amiibo - + Unable to load Amiibo data. Não foi possível carregar os dados do Amiibo. - + Capture Screenshot Capturar tela - + PNG Image (*.png) Imagem PNG (*.png) - + TAS state: Running %1/%2 Situação TAS: Rodando %1%2 - + TAS state: Recording %1 Situação TAS: Gravando %1 - + TAS state: Idle %1/%2 Situação TAS: Repouso %1%2 - + TAS State: Invalid Situação TAS: Inválido - + &Stop Running &Parar de rodar - + &Start &Iniciar - + Stop R&ecording Parar G&ravação - + R&ecord G&ravação - + Building: %n shader(s) Compilando: %n shader(s)Compilando: %n shader(s)Compilando: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocidade: %1% / %2% - + Speed: %1% Velocidade: %1% - + Game: %1 FPS (Unlocked) Jogo: %1 FPS (Desbloqueado) - + Game: %1 FPS Jogo: %1 FPS - + Frame: %1 ms Quadro: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR ERRO DE GPU - + DOCKED - + HANDHELD - + NEAREST VIZINHO - - + + BILINEAR BILINEAR - + BICUBIC BICÚBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA Sem AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. O jogo que você está tentando carregar precisa que arquivos adicionais do seu Switch sejam extraídos antes de jogá-lo.<br/><br/>Para saber mais sobre como extrair esses arquivos, visite a seguinte página da wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Extraindo arquivos de sistema e fontes compartilhadas de um Switch</a>.<br/><br/> Gostaria de voltar para a lista de jogos? Continuar com a emulação pode resultar em travamentos, dados salvos corrompidos ou outros problemas. - + yuzu was unable to locate a Switch system archive. %1 O yuzu não foi capaz de encontrar um arquivo de sistema do Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 O yuzu não foi capaz de encontrar um arquivo de sistema do Switch. %1. %2 - + System Archive Not Found Arquivo do sistema não encontrado - + System Archive Missing Arquivo de sistema faltando - + yuzu was unable to locate the Switch shared fonts. %1 O yuzu não foi capaz de encontrar as fontes compartilhadas do Switch. %1 - + Shared Fonts Not Found Fontes compartilhadas não encontradas - + Shared Font Missing Fonte compartilhada faltando - + Fatal Error Erro fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. O yuzu encontrou um erro fatal. Consulte o registro para mais detalhes. Para mais informações sobre como acessar o registro, consulte a seguinte página: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Como enviar o arquivo de registro</a>.<br/><br/>Gostaria de voltar para a lista de jogos? Continuar com a emulação pode resultar em travamentos, dados salvos corrompidos ou outros problemas. - + Fatal Error encountered Erro fatal encontrado - + Confirm Key Rederivation Confirmar rederivação de chave - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5100,37 +5151,37 @@ e opcionalmente faça cópias de segurança. Isto excluirá o seus arquivos de chaves geradas automaticamente, e reexecutar o módulo de derivação de chaves. - + Missing fuses Faltando fusíveis - + - Missing BOOT0 - Faltando BOOT0 - + - Missing BCPKG2-1-Normal-Main - Faltando BCPKG2-1-Normal-Main - + - Missing PRODINFO - Faltando PRODINFO - + Derivation Components Missing Faltando componentes de derivação - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Chaves de encriptação faltando. <br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>o guia de início rápido</a> para extrair suas chaves, firmware e jogos. <br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5139,39 +5190,39 @@ Isto pode demorar até um minuto, dependendo do desempenho do seu sistema. - + Deriving Keys Derivando chaves - + Select RomFS Dump Target Selecionar alvo de extração do RomFS - + Please select which RomFS you would like to dump. Selecione qual RomFS você quer extrair. - + Are you sure you want to close yuzu? Você deseja mesmo fechar o yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Deseja mesmo parar a emulação? Qualquer progresso não salvo será perdido. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5527,7 +5578,7 @@ tela inicial do jogo. Room Description - + Descrição da sala @@ -5553,12 +5604,12 @@ tela inicial do jogo. HostRoomWindow - + Error Erro - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5567,11 +5618,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5593,112 +5645,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Capturar tela - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Tela cheia - + Load File Carregar arquivo - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5813,42 +5864,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Jogadores - + Refreshing - + Refresh List @@ -6018,7 +6069,7 @@ Debug Message: Leave Room - + Sair da sala @@ -6175,7 +6226,7 @@ Debug Message: - + Connected Conectado @@ -6197,7 +6248,7 @@ Debug Message: - + New Messages Received @@ -6301,22 +6352,39 @@ They may have left the room. - - Leave Room + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + + Leave Room + Sair da sala + + + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6324,7 +6392,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Erro @@ -6383,7 +6451,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6438,7 +6506,7 @@ p, li { white-space: pre-wrap; } - + [not set] [não definido] @@ -6453,10 +6521,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eixo %1%2 @@ -6470,9 +6538,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [desconhecido] @@ -6637,15 +6705,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [inválido] - - + + %1%2Hat %3 %1%2Direcional %3 @@ -6653,35 +6721,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eixo %3 - + %1%2Axis %3,%4,%5 %1%2Eixo %3,%4,%5 - + %1%2Motion %3 %1%2Movimentação %3 - - + + %1%2Button %3 %1%2Botão %3 - + [unused] [não utilizado] @@ -6722,7 +6790,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/pt_PT.ts b/dist/languages/pt_PT.ts index 74d7656bdb..525f0e9843 100644 --- a/dist/languages/pt_PT.ts +++ b/dist/languages/pt_PT.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + Site | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Código fonte | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contribuidores</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licença</span></a></p></body></html> @@ -82,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Janela da sala Send Chat Message - + Enviar mensagem Send Message - + Enviar mensagem - + Members - + Membros - + %1 has joined - + %1 entrou - + %1 has left - + %1 saiu - + %1 has been kicked - + %1 foi expulso(a) - + %1 has been banned - + %1 foi banido(a) - + %1 has been unbanned - - - - - View Profile - + %1 foi desbanido(a) - - Block Player - + View Profile + Ver perfil - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + + + Block Player + Bloquear jogador + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Quando bloqueia um jogador, você não receberá mais mensagens dele.<br><br>Você deseja mesmo bloquear %1? + + + Kick - - - - - Ban - - - - - Kick Player - - - - - Are you sure you would like to <b>kick</b> %1? - + Expulsar - Ban Player - + Ban + Banir - + + Kick Player + Expulsar jogador + + + + Are you sure you would like to <b>kick</b> %1? + Você deseja mesmo <b>expulsar</b> %1? + + + + Ban Player + Banir jogador + + + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Você deseja mesmo <b>expulsar e banir</b> %1? + +Isto banirá tanto o nome de usuário do fórum como o endereço IP. @@ -178,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Janela da sala Room Description - + Descrição da sala Moderation... - + Moderação... Leave Room - + Sair da sala @@ -206,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + Desconectado %1 (%2/%3 members) - connected - + %1 (%2/%3 membros) - conectado @@ -339,7 +341,7 @@ This would ban both their forum username and their IP address. Output Device - + Dispositivo de saída @@ -378,37 +380,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Configurar câmera infravermelha Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Selecione de onde vem a imagem da câmera emulada. Pode ser uma câmera virtual ou uma câmera real. Camera Image Source: - + Origem da imagem da câmera: Input device: - + Dispositivo de entrada: Preview - + Prévia Resolution: 320*240 - + Resolução: 320*240 Click to preview - + Clique para pré-visualizar @@ -759,200 +761,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Depurador - + Enable GDB Stub Activar GDB Stub - + Port: Porta: - + Logging Entrando - + Global Log Filter Filtro de registro global - + Show Log in Console Mostrar Relatório na Consola - + Open Log Location Abrir a localização do registro - + When checked, the max size of the log increases from 100 MB to 1 GB Quando ativado, o tamanho máximo do registo aumenta de 100 MB para 1 GB - + Enable Extended Logging** Ativar registros avançados** - + Homebrew Homebrew - + Arguments String Argumentos String - + Graphics Gráficos - + When checked, the graphics API enters a slower debugging mode Quando ativado, a API gráfica entra em um modo de depuração mais lento. - + Enable Graphics Debugging Activar Depuração Gráfica - + When checked, it enables Nsight Aftermath crash dumps Quando ativado, ativa a extração de registros de travamento do Nsight Aftermath - + Enable Nsight Aftermath Ativar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Se selecionado, descarrega todos os shaders originários do cache do disco ou do jogo conforme encontrá-los. - + Dump Game Shaders Descarregar shaders do jogo - + When checked, it will dump all the macro programs of the GPU Quando marcada, essa opção irá despejar todos os macro programas da GPU - + Dump Maxwell Macros Despejar macros Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Quando ativado, desativa o macro compilador Just In Time. Ativar isto faz os jogos rodarem mais lentamente. - + Disable Macro JIT Desactivar Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Quando ativado, o yuzu registrará estatísticas sobre o cache de pipeline compilado - + Enable Shader Feedback Ativar Feedback de Shaders - + When checked, it executes shaders without loop logic changes Quando ativado, executa shaders sem mudanças de lógica de loop - + Disable Loop safety checks Desativar verificação de segurança de loops - + Debugging Depuração - - Enable FS Access Log - Ativar acesso de registro FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Ativar serviços de relatório detalhado** - + + Enable FS Access Log + Ativar acesso de registro FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avançado - + Kiosk (Quest) Mode Modo Quiosque (Quest) - + Enable CPU Debugging Ativar depuração de CPU - + Enable Debug Asserts Ativar asserções de depuração - + Enable Auto-Stub** Ativar auto-esboço** - + Enable All Controller Types Ativar todos os tipos de controles - + Disable Web Applet Desativar Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Isto será restaurado automaticamente assim que o yuzu for fechado. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1571,37 +1608,47 @@ This would ban both their forum username and their IP address. Usar tempo de resposta rápido da GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtro Anisotrópico: - + Automatic Automático - + Default Padrão - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2093,7 +2140,7 @@ This would ban both their forum username and their IP address. - + Left Stick Analógico Esquerdo @@ -2187,14 +2234,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2213,7 +2260,7 @@ This would ban both their forum username and their IP address. - + Plus Mais @@ -2226,15 +2273,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2291,231 +2338,236 @@ This would ban both their forum username and their IP address. - + Right Stick Analógico Direito - - - - + + + + Clear Limpar - - - - - + + + + + [not set] [não definido] - - - Toggle button - Alternar pressionamento do botão - - - - + + Invert button Inverter botão - - + + + Toggle button + Alternar pressionamento do botão + + + + Invert axis Inverter eixo - - - + + + Set threshold Definir limite - - + + Choose a value between 0% and 100% Escolha um valor entre 0% e 100% - + + Toggle axis + + + + Set gyro threshold Definir limite do giroscópio - + Map Analog Stick Mapear analógicos - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Após pressionar OK, mova o seu analógico primeiro horizontalmente e depois verticalmente. Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois horizontalmente. - + Center axis Eixo central - - + + Deadzone: %1% Ponto Morto: %1% - - + + Modifier Range: %1% Modificador de Alcance: %1% - - + + Pro Controller Comando Pro - + Dual Joycons Joycons Duplos - + Left Joycon Joycon Esquerdo - + Right Joycon Joycon Direito - + Handheld Portátil - + GameCube Controller Controlador de depuração - + Poke Ball Plus Poke Ball Plus - + NES Controller Controle NES - + SNES Controller Controle SNES - + N64 Controller Controle N64 - + Sega Genesis Mega Drive - + Start / Pause Iniciar / Pausar - + Z Z - + Control Stick Direcional de controle - + C-Stick C-Stick - + Shake! Abane! - + [waiting] [em espera] - + New Profile Novo Perfil - + Enter a profile name: Introduza um novo nome de perfil: - - + + Create Input Profile Criar perfil de controlo - + The given profile name is not valid! O nome de perfil dado não é válido! - + Failed to create the input profile "%1" Falha ao criar o perfil de controlo "%1" - + Delete Input Profile Apagar Perfil de Controlo - + Failed to delete the input profile "%1" Falha ao apagar o perfil de controlo "%1" - + Load Input Profile Carregar perfil de controlo - + Failed to load the input profile "%1" Falha ao carregar o perfil de controlo "%1" - + Save Input Profile Guardar perfil de controlo - + Failed to save the input profile "%1" Falha ao guardar o perfil de controlo "%1" @@ -2770,42 +2822,42 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho Desenvolvedor - + Add-Ons Add-Ons - + General Geral - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos Avç. - + Audio Audio - + Properties Propriedades @@ -3517,47 +3569,47 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho <html><head/><body><p>Lê entradas de controle a partir de scripts no mesmo formato que TAS-nx. <br/>Para uma explicação mais detalhada, por favor consulte a <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">página de ajuda</span></a> no website do yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Para checar que atalhos controlam rodar/gravar, por favor refira-se às Teclas de atalhos (Configurar -> Geral -> Teclas de atalhos) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ATENÇÃO: Este é um recurso experimental. <br/>Não irá rodar os scrips em quadros perfeitos com o atual, imperfeito método de sincronização. - + Settings Configurações - + Enable TAS features Ativar funcionalidades TAS - + Loop script Repetir script em loop - + Pause execution during loads Pausar execução durante carregamentos - + Script Directory Diretório do script - + Path Caminho - + ... ... @@ -3967,7 +4019,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta - + Verify Verificar @@ -4054,7 +4106,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta - + Unspecified Não especificado @@ -4069,17 +4121,36 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta O token não foi verificado. A alteração do token não foi gravada. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... A verificar... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verificação Falhada + + + Verification failed Verificação Falhada - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verificação Falhada. Verifique se introduziu seu nome de utilizador e o token correctamente e se a conexão com a Internet está operacional. @@ -4148,12 +4219,12 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta DirectConnectWindow - + Connecting - + Connect @@ -4161,910 +4232,890 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dados anônimos são coletados</a>para ajudar a melhorar o yuzu.<br/><br/>Gostaria de compartilhar seus dados de uso conosco? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... A Carregar o Web Applet ... - + Disable Web Applet Desativar Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) A desativação do applet da web pode causar comportamento inesperado e deve apenas ser usada com Super Mario 3D All-Stars. Você deseja mesmo desativar o applet da web? (Ele pode ser reativado nas configurações de depuração.) - + The amount of shaders currently being built Quantidade de shaders a serem construídos - + The current selected resolution scaling multiplier. O atualmente multiplicador de escala de resolução selecionado. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidade da emulação actual. Valores acima ou abaixo de 100% indicam que a emulação está sendo executada mais depressa ou mais devagar do que a Switch - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quantos quadros por segundo o jogo está exibindo de momento. Isto irá variar de jogo para jogo e de cena para cena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo gasto para emular um frame da Switch, sem contar o a limitação de quadros ou o v-sync. Para emulação de velocidade máxima, esta deve ser no máximo 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Limpar arquivos recentes - + &Continue &Continuar - + &Pause &Pausa - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu está rodando um jogo - + Warning Outdated Game Format Aviso de Formato de Jogo Desactualizado - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Você está usando o formato de directório ROM desconstruído para este jogo, que é um formato desactualizado que foi substituído por outros, como NCA, NAX, XCI ou NSP. Os directórios de ROM não construídos não possuem ícones, metadados e suporte de actualização.<br><br>Para uma explicação dos vários formatos de Switch que o yuzu suporta,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Verifique a nossa Wiki</a>. Esta mensagem não será mostrada novamente. - - + + Error while loading ROM! Erro ao carregar o ROM! - + The ROM format is not supported. O formato do ROM não é suportado. - + An error occurred initializing the video core. Ocorreu um erro ao inicializar o núcleo do vídeo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu encontrou um erro enquanto rodando o núcleo de vídeo. Normalmente isto é causado por drivers de GPU desatualizados, incluindo integrados. Por favor veja o registro para mais detalhes. Para mais informações em acesso ao registro por favor veja a seguinte página: <a href='https://yuzu-emu.org/help/reference/log-files/'>Como fazer envio de arquivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Erro ao carregar a ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>a guia de início rápido do yuzu</a> para fazer o redespejo dos seus arquivos.<br>Você pode consultar a wiki do yuzu</a> ou o Discord do yuzu</a> para obter ajuda. - + An unknown error occurred. Please see the log for more details. Ocorreu um erro desconhecido. Por favor, veja o log para mais detalhes. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Save Data - + Mod Data Mod Data - + Error Opening %1 Folder Erro ao abrir a pasta %1 - - + + Folder does not exist! A Pasta não existe! - + Error Opening Transferable Shader Cache Erro ao abrir os Shader Cache transferíveis - + Failed to create the shader cache directory for this title. Falha ao criar o diretório de cache de shaders para este título. - + Contents Conteúdos - + Update Actualização - + DLC DLC - + Remove Entry Remover Entrada - + Remove Installed Game %1? Remover Jogo Instalado %1? - - - - - - + + + + + + Successfully Removed Removido com Sucesso - + Successfully removed the installed base game. Removida a instalação do jogo base com sucesso. - - - + + + Error Removing %1 Erro ao Remover %1 - + The base game is not installed in the NAND and cannot be removed. O jogo base não está instalado no NAND e não pode ser removido. - + Successfully removed the installed update. Removida a actualização instalada com sucesso. - + There is no update installed for this title. Não há actualização instalada neste título. - + There are no DLC installed for this title. Não há DLC instalado neste título. - + Successfully removed %1 installed DLC. Removido DLC instalado %1 com sucesso. - + Delete OpenGL Transferable Shader Cache? Apagar o cache de shaders transferível do OpenGL? - + Delete Vulkan Transferable Shader Cache? Apagar o cache de shaders transferível do Vulkan? - + Delete All Transferable Shader Caches? Apagar todos os caches de shaders transferíveis? - + Remove Custom Game Configuration? Remover Configuração Personalizada do Jogo? - + Remove File Remover Ficheiro - - + + Error Removing Transferable Shader Cache Error ao Remover Cache de Shader Transferível - - + + A shader cache for this title does not exist. O Shader Cache para este titulo não existe. - + Successfully removed the transferable shader cache. Removido a Cache de Shader Transferível com Sucesso. - + Failed to remove the transferable shader cache. Falha ao remover a cache de shader transferível. - - + + Error Removing Transferable Shader Caches Erro ao remover os caches de shaders transferíveis - + Successfully removed the transferable shader caches. Os caches de shaders transferíveis foram removidos com sucesso. - + Failed to remove the transferable shader cache directory. Falha ao remover o diretório do cache de shaders transferível. - - + + Error Removing Custom Configuration Erro ao Remover Configuração Personalizada - + A custom configuration for this title does not exist. Não existe uma configuração personalizada para este titúlo. - + Successfully removed the custom game configuration. Removida a configuração personalizada do jogo com sucesso. - + Failed to remove the custom game configuration. Falha ao remover a configuração personalizada do jogo. - - + + RomFS Extraction Failed! A Extração de RomFS falhou! - + There was an error copying the RomFS files or the user cancelled the operation. Houve um erro ao copiar os arquivos RomFS ou o usuário cancelou a operação. - + Full Cheio - + Skeleton Esqueleto - + Select RomFS Dump Mode Selecione o modo de despejo do RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Por favor, selecione a forma como você gostaria que o RomFS fosse despejado<br>Full irá copiar todos os arquivos para o novo diretório enquanto<br>skeleton criará apenas a estrutura de diretórios. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Não há espaço suficiente em %1 para extrair o RomFS. Por favor abra espaço ou selecione um diretório diferente em Emulação > Configurar > Sistema > Sistema de arquivos > Extrair raiz - + Extracting RomFS... Extraindo o RomFS ... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! Extração de RomFS Bem-Sucedida! - + The operation completed successfully. A operação foi completa com sucesso. - + Error Opening %1 Erro ao abrir %1 - + Select Directory Selecione o Diretório - + Properties Propriedades - + The game properties could not be loaded. As propriedades do jogo não puderam ser carregadas. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Executáveis Switch (%1);;Todos os Ficheiros (*.*) - + Load File Carregar Ficheiro - + Open Extracted ROM Directory Abrir o directório ROM extraído - + Invalid Directory Selected Diretório inválido selecionado - + The directory you have selected does not contain a 'main' file. O diretório que você selecionou não contém um arquivo 'Main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Ficheiro Switch Instalável (*.nca *.nsp *.xci);;Arquivo de Conteúdo Nintendo (*.nca);;Pacote de Envio Nintendo (*.nsp);;Imagem de Cartucho NX (*.xci) - + Install Files Instalar Ficheiros - + %n file(s) remaining - + Installing file "%1"... Instalando arquivo "%1"... - - + + Install Results Instalar Resultados - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar possíveis conflitos, desencorajamos que os utilizadores instalem os jogos base na NAND. Por favor, use esse recurso apenas para instalar atualizações e DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Aplicação do sistema - + System Archive Arquivo do sistema - + System Application Update Atualização do aplicativo do sistema - + Firmware Package (Type A) Pacote de Firmware (Tipo A) - + Firmware Package (Type B) Pacote de Firmware (Tipo B) - + Game Jogo - + Game Update Actualização do Jogo - + Game DLC DLC do Jogo - + Delta Title Título Delta - + Select NCA Install Type... Selecione o tipo de instalação do NCA ... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Por favor, selecione o tipo de título que você gostaria de instalar este NCA como: (Na maioria dos casos, o padrão 'Jogo' é suficiente). - + Failed to Install Falha na instalação - + The title type you selected for the NCA is invalid. O tipo de título que você selecionou para o NCA é inválido. - + File not found Arquivo não encontrado - + File "%1" not found Arquivo "%1" não encontrado - + OK OK - + Missing yuzu Account Conta Yuzu Ausente - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Para enviar um caso de teste de compatibilidade de jogos, você deve vincular sua conta yuzu.<br><br/>Para vincular sua conta yuzu, vá para Emulação &gt; Configuração &gt; Rede. - + Error opening URL Erro ao abrir URL - + Unable to open the URL "%1". Não foi possível abrir o URL "%1". - + TAS Recording Gravando TAS - + Overwrite file of player 1? Sobrescrever arquivo do jogador 1? - + Invalid config detected Configação inválida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. O comando portátil não pode ser usado no modo encaixado na base. O Pro controller será selecionado. - - + + Error Erro - - + + The current game is not looking for amiibos O jogo atual não está procurando amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed O amiibo atual foi removido - + Amiibo File (%1);; All Files (*.*) Arquivo Amiibo (%1);; Todos os Arquivos (*.*) - + Load Amiibo Carregar Amiibo - - Error opening Amiibo data file - Erro ao abrir o arquivo de dados do Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Não é possível abrir o arquivo Amiibo "%1" para leitura. - - - - Error reading Amiibo data file - Erro ao ler o arquivo de dados do Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Não é possível ler completamente os dados do Amiibo. Espera-se que leia %1 bytes, mas só conseguiu ler %2 bytes. - - - + Error loading Amiibo data Erro ao carregar dados do Amiibo - + Unable to load Amiibo data. Não foi possível carregar os dados do Amiibo. - + Capture Screenshot Captura de Tela - + PNG Image (*.png) Imagem PNG (*.png) - + TAS state: Running %1/%2 Situação TAS: Rodando %1%2 - + TAS state: Recording %1 Situação TAS: Gravando %1 - + TAS state: Idle %1/%2 Situação TAS: Repouso %1%2 - + TAS State: Invalid Situação TAS: Inválido - + &Stop Running &Parar de rodar - + &Start &Começar - + Stop R&ecording Parar G&ravação - + R&ecord G&ravação - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocidade: %1% / %2% - + Speed: %1% Velocidade: %1% - + Game: %1 FPS (Unlocked) Jogo: %1 FPS (Desbloqueado) - + Game: %1 FPS Jogo: %1 FPS - + Frame: %1 ms Quadro: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR ERRO DE GPU - + DOCKED - + HANDHELD - + NEAREST VIZINHO - - + + BILINEAR BILINEAR - + BICUBIC BICÚBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA Sem AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. O jogo que você está tentando carregar requer arquivos adicionais do seu Switch para serem despejados antes de jogar.<br/><br/>Para obter mais informações sobre como despejar esses arquivos, consulte a seguinte página da wiki:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Despejando arquivos do sistema e as fontes compartilhadas de uma consola Switch</a>.<br/><br/>Você gostaria de regressar para a lista de jogos? Continuar a emulação pode resultar em falhas, dados de salvamento corrompidos ou outros erros. - + yuzu was unable to locate a Switch system archive. %1 O yuzu não conseguiu localizar um arquivo de sistema do Switch. % 1 - + yuzu was unable to locate a Switch system archive: %1. %2 O yuzu não conseguiu localizar um arquivo de sistema do Switch: %1. %2 - + System Archive Not Found Arquivo do Sistema Não Encontrado - + System Archive Missing Arquivo de Sistema em falta - + yuzu was unable to locate the Switch shared fonts. %1 yuzu não conseguiu localizar as fontes compartilhadas do Switch. %1 - + Shared Fonts Not Found Fontes compartilhadas não encontradas - + Shared Font Missing Fontes compartilhadas em falta - + Fatal Error Erro fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu encontrou um erro fatal, por favor veja o registro para mais detalhes. Para mais informações sobre como acessar o registro, por favor, veja a seguinte página:<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Como carregar o arquivo de registro</a>.<br/><br/>Você gostaria de regressar para a lista de jogos? Continuar a emulação pode resultar em falhas, dados de salvamento corrompidos ou outros erros. - + Fatal Error encountered Ocorreu um Erro fatal - + Confirm Key Rederivation Confirme a rederivação da chave - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5081,37 +5132,37 @@ e opcionalmente faça backups. Isso irá excluir os seus arquivos de chave gerados automaticamente e executará novamente o módulo de derivação de chave. - + Missing fuses Fusíveis em Falta - + - Missing BOOT0 - BOOT0 em Falta - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main em Falta - + - Missing PRODINFO - PRODINFO em Falta - + Derivation Components Missing Componentes de Derivação em Falta - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Chaves de encriptação faltando. <br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>o guia de início rápido</a> para extrair suas chaves, firmware e jogos. <br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5120,39 +5171,39 @@ Isto pode demorar até um minuto, dependendo do desempenho do seu sistema. - + Deriving Keys Derivando Chaves - + Select RomFS Dump Target Selecione o destino de despejo do RomFS - + Please select which RomFS you would like to dump. Por favor, selecione qual o RomFS que você gostaria de despejar. - + Are you sure you want to close yuzu? Tem a certeza que quer fechar o yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Tem a certeza de que quer parar a emulação? Qualquer progresso não salvo será perdido. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5508,7 +5559,7 @@ Inicial Room Description - + Descrição da sala @@ -5534,12 +5585,12 @@ Inicial HostRoomWindow - + Error Erro - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5548,11 +5599,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5574,112 +5626,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Captura de Tela - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Tela Cheia - + Load File Carregar Ficheiro - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5794,42 +5845,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Jogadores - + Refreshing - + Refresh List @@ -5999,7 +6050,7 @@ Debug Message: Leave Room - + Sair da sala @@ -6156,7 +6207,7 @@ Debug Message: - + Connected Conectado @@ -6178,7 +6229,7 @@ Debug Message: - + New Messages Received @@ -6282,22 +6333,39 @@ They may have left the room. - - Leave Room + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + + Leave Room + Sair da sala + + + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6305,7 +6373,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Erro @@ -6364,7 +6432,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6419,7 +6487,7 @@ p, li { white-space: pre-wrap; } - + [not set] [não configurado] @@ -6434,10 +6502,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eixo %1%2 @@ -6451,9 +6519,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [Desconhecido] @@ -6618,15 +6686,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [inválido] - - + + %1%2Hat %3 %1%2Direcional %3 @@ -6634,35 +6702,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eixo %3 - + %1%2Axis %3,%4,%5 %1%2Eixo %3,%4,%5 - + %1%2Motion %3 %1%2Movimentação %3 - - + + %1%2Button %3 %1%2Botão %3 - + [unused] [sem uso] @@ -6703,7 +6771,7 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts index 8ba92a5b74..5bbf4e6b06 100644 --- a/dist/languages/ru_RU.ts +++ b/dist/languages/ru_RU.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } Отправить сообщение - + Members Участники - + %1 has joined %1 присоединился - + %1 has left %1 вышел - + %1 has been kicked %1 был выгнан - + %1 has been banned %1 был забанен - + %1 has been unbanned %1 был разбанен - + View Profile Посмотреть профиль - - + + Block Player Заблокировать игрока - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Когда вы блокируете игрока, вы больше не будете получать от него сообщения в чате.<br><br>Вы уверены, что хотите заблокировать %1? - + Kick Выгнать - + Ban Забанить - + Kick Player Выгнать игрока - + Are you sure you would like to <b>kick</b> %1? Вы уверены, что хотите <b>выгнать</b> %1? - + Ban Player Забанить игрока - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -771,200 +771,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Отладчик - + Enable GDB Stub Включить GDB Stub - + Port: Порт: - + Logging Журналирование - + Global Log Filter Глобальный фильтр журналов - + Show Log in Console Показывать журнал в консоли - + Open Log Location Открыть папку для журналов - + When checked, the max size of the log increases from 100 MB to 1 GB Если включено, максимальный размер журнала увеличивается со 100 МБ до 1 ГБ - + Enable Extended Logging** Включить расширенное ведение журнала** - + Homebrew Homebrew - + Arguments String Строка аргументов - + Graphics Графика - + When checked, the graphics API enters a slower debugging mode Если включено, графический API переходит в более медленный режим отладки - + Enable Graphics Debugging Включить отладку графики - + When checked, it enables Nsight Aftermath crash dumps Если включено, включает дампы крашей Nsight Aftermath - + Enable Nsight Aftermath Включить Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Если включено, будет дампить все оригинальные шейдеры ассемблера из кэша шейдеров на диске или игры как найденные - + Dump Game Shaders Дамп игровых шейдеров - + When checked, it will dump all the macro programs of the GPU Если включено, будет дампить все макропрограммы ГП - + Dump Maxwell Macros Дамп макросов Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Если включено, отключает компилятор макроса Just In Time. Включение опции делает игры медленнее - + Disable Macro JIT Отключить Макрос JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Если включено, yuzu будет записывать статистику о скомпилированном кэше конвейера - + Enable Shader Feedback Включить обратную связь о шейдерах - + When checked, it executes shaders without loop logic changes Если включено, производит выполнение шейдеров без изменения логики цикла - + Disable Loop safety checks Отключить проверку безопасности цикла - + Debugging Отладка - - Enable FS Access Log - Включить журнал доступа к FS - - - - Dump Audio Commands To Console** - Дамп аудиокоманд в консоль** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - Включите эту опцию, чтобы выводить на консоль последний сгенерированный список аудиокоманд. Влияет только на игры, использующие аудио рендерер. - - - + Enable Verbose Reporting Services** Включить службу отчётов в развернутом виде** - + + Enable FS Access Log + Включить журнал доступа к FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + Включите эту опцию, чтобы выводить на консоль последний сгенерированный список аудиокоманд. Влияет только на игры, использующие аудио рендерер. + + + + Dump Audio Commands To Console** + Дамп аудиокоманд в консоль** + + + + Create Minidump After Crash + + + + Advanced Дополнительно - + Kiosk (Quest) Mode Режим киоска (Квест) - + Enable CPU Debugging Включить отладку ЦП - + Enable Debug Asserts Включить отладочные сигналы - + Enable Auto-Stub** Включить Автоподставку** - + Enable All Controller Types Включить все типы контроллеров - + Disable Web Applet Отключить веб-апплет - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Это будет автоматически сброшено после закрытия yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1560,7 +1595,7 @@ This would ban both their forum username and their IP address. Use VSync - + Использовать вертикальную синхронизацию @@ -1583,37 +1618,47 @@ This would ban both their forum username and their IP address. Включить Fast GPU Time (Хак) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Включает пессимистическую очистку буферов. Эта опция заставляет промывать немодифицированные буферы, что может снизить производительность. + + + + Use pessimistic buffer flushes (Hack) + Использовать пессимистическую очистку буферов (Хак) + + + Anisotropic Filtering: Анизотропная Фильтрация: - + Automatic Автоматически - + Default Стандартная - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2105,7 +2150,7 @@ This would ban both their forum username and their IP address. - + Left Stick Левый стик @@ -2199,14 +2244,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2225,7 +2270,7 @@ This would ban both their forum username and their IP address. - + Plus Плюс @@ -2238,15 +2283,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2303,231 +2348,236 @@ This would ban both their forum username and their IP address. - + Right Stick Правый стик - - - - + + + + Clear Очистить - - - - - + + + + + [not set] [не задано] - - - Toggle button - Переключить кнопку - - - - + + Invert button Инвертировать кнопку - - + + + Toggle button + Переключить кнопку + + + + Invert axis Инвертировать оси - - - + + + Set threshold Установить порог - - + + Choose a value between 0% and 100% Выберите значение между 0% и 100% - + + Toggle axis + Переключить оси + + + Set gyro threshold Установить порог гироскопа - + Map Analog Stick Задать аналоговый стик - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. После нажатия на ОК, двигайте ваш джойстик горизонтально, а затем вертикально. Чтобы инвертировать оси, сначала двигайте ваш джойстик вертикально, а затем горизонтально. - + Center axis Центрировать оси - - + + Deadzone: %1% Мёртвая зона: %1% - - + + Modifier Range: %1% Диапазон модификатора: %1% - - + + Pro Controller Контроллер Pro - + Dual Joycons Двойные Joycon'ы - + Left Joycon Левый Joycon - + Right Joycon Правый Joycon - + Handheld Портативный - + GameCube Controller Контроллер GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Контроллер NES - + SNES Controller Контроллер SNES - + N64 Controller Контроллер N64 - + Sega Genesis Sega Genesis - + Start / Pause Старт / Пауза - + Z Z - + Control Stick Стик управления - + C-Stick C-Стик - + Shake! Встряхните! - + [waiting] [ожидание] - + New Profile Новый профиль - + Enter a profile name: Введите имя профиля: - - + + Create Input Profile Создать профиль управления - + The given profile name is not valid! Заданное имя профиля недействительно! - + Failed to create the input profile "%1" Не удалось создать профиль управления "%1" - + Delete Input Profile Удалить профиль управления - + Failed to delete the input profile "%1" Не удалось удалить профиль управления "%1" - + Load Input Profile Загрузить профиль управления - + Failed to load the input profile "%1" Не удалось загрузить профиль управления "%1" - + Save Input Profile Сохранить профиль управления - + Failed to save the input profile "%1" Не удалось сохранить профиль управления "%1" @@ -2782,42 +2832,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Разработчик - + Add-Ons Дополнения - + General Общие - + System Система - + CPU ЦП - + Graphics Графика - + Adv. Graphics Расш. Графика - + Audio Звук - + Properties Свойства @@ -3529,47 +3579,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>Считывает входные данные контроллера из скриптов в том же формате, что и скрипты TAS-nx.<br/>Для более подробного объяснения обратитесь к <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">странице помощи</span></a> на сайте yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Чтобы проверить, какие горячие клавиши управляют воспроизведением/записью, обратитесь к настройкам горячих клавиш (Настройки - Общие -> Горячие клавиши). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ПРЕДУПРЕЖДЕНИЕ: Это экспериментальная функция.<br/>Она не будет идеально воспроизводить кадры сценариев при текущем несовершенном методе синхронизации. - + Settings Настройки - + Enable TAS features Включить функции TAS - + Loop script Зациклить скрипт - + Pause execution during loads Приостановить выполнение во время загрузки - + Script Directory Папка для скриптов - + Path Путь - + ... ... @@ -3979,7 +4029,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Подтвердить @@ -4066,7 +4116,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified Отсутствует @@ -4081,17 +4131,36 @@ Drag points to change position, or double-click table cells to edit values.Токен не был подтвержден. Изменение вашего токена не было сохранено. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Проверка... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Ошибка подтверждения + + + Verification failed Ошибка подтверждения - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Ошибка подтверждения. Убедитесь, что вы правильно ввели свой токен и что ваше подключение к Интернету работает. @@ -4160,12 +4229,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting Подключение - + Connect Подключиться @@ -4173,488 +4242,488 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Анонимные данные собираются для того,</a> чтобы помочь улучшить работу yuzu. <br/><br/>Хотели бы вы делиться данными об использовании с нами? - + Telemetry Телеметрия - + Broken Vulkan Installation Detected Обнаружена поврежденная установка Vulkan - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. Не удалось выполнить инициализацию Vulkan во время загрузки.<br><br>Нажмите <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>здесь для получения инструкций по устранению проблемы</a>. - + Loading Web Applet... Загрузка веб-апплета... - + Disable Web Applet Отключить веб-апплет - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Отключение веб-апплета может привести к неожиданному поведению и должно использоваться только с Super Mario 3D All-Stars. Вы уверены, что хотите отключить веб-апплет? (Его можно снова включить в настройках отладки.) - + The amount of shaders currently being built Количество создаваемых шейдеров на данный момент - + The current selected resolution scaling multiplier. Текущий выбранный множитель масштабирования разрешения. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Текущая скорость эмуляции. Значения выше или ниже 100% указывают на то, что эмуляция идет быстрее или медленнее, чем на Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Количество кадров в секунду в данный момент. Значение будет меняться между играми и сценами. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Время, которое нужно для эмуляции 1 кадра Switch, не принимая во внимание ограничение FPS или вертикальную синхронизацию. Для эмуляции в полной скорости значение должно быть не больше 16,67 мс. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files [&C] Очистить недавние файлы - + &Continue [&C] Продолжить - + &Pause [&P] Пауза - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping В yuzu запущена игра - + Warning Outdated Game Format Предупреждение устаревший формат игры - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Для этой игры вы используете разархивированный формат ROM'а, который является устаревшим и был заменен другими, такими как NCA, NAX, XCI или NSP. В разархивированных каталогах ROM'а отсутствуют иконки, метаданные и поддержка обновлений. <br><br>Для получения информации о различных форматах Switch, поддерживаемых yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>просмотрите нашу вики</a>. Это сообщение больше не будет отображаться. - - + + Error while loading ROM! Ошибка при загрузке ROM'а! - + The ROM format is not supported. Формат ROM'а не поддерживается. - + An error occurred initializing the video core. Произошла ошибка при инициализации видеоядра. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu столкнулся с ошибкой при запуске видеоядра. Обычно это вызвано устаревшими драйверами GPU, включая интегрированные. Проверьте журнал для получения более подробной информации. Дополнительную информацию о доступе к журналу смотрите на следующей странице: <a href='https://yuzu-emu.org/help/reference/log-files/'>Как загрузить файл журнала</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Ошибка при загрузке ROM'а! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Пожалуйста, следуйте <a href='https://yuzu-emu.org/help/quickstart/'>краткому руководству пользователя yuzu</a> чтобы пере-дампить ваши файлы<br>Вы можете обратиться к вики yuzu</a> или Discord yuzu</a> для помощи. - + An unknown error occurred. Please see the log for more details. Произошла неизвестная ошибка. Пожалуйста, проверьте журнал для подробностей. - + (64-bit) (64-х битный) - + (32-bit) (32-х битный) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Сохранения - + Mod Data Данные модов - + Error Opening %1 Folder Ошибка при открытии папки %1 - - + + Folder does not exist! Папка не существует! - + Error Opening Transferable Shader Cache Ошибка при открытии переносного кэша шейдеров - + Failed to create the shader cache directory for this title. Не удалось создать папку кэша шейдеров для этой игры. - + Contents Содержание - + Update Обновление - + DLC Загружаемый контент - + Remove Entry Удалить запись - + Remove Installed Game %1? Удалить установленную игру %1? - - - - - - + + + + + + Successfully Removed Успешно удалено - + Successfully removed the installed base game. Установленная игра успешно удалена. - - - + + + Error Removing %1 Ошибка при удалении %1 - + The base game is not installed in the NAND and cannot be removed. Игра не установлена в NAND и не может быть удалена. - + Successfully removed the installed update. Установленное обновление успешно удалено. - + There is no update installed for this title. Для этой игры не было установлено обновление. - + There are no DLC installed for this title. Для этой игры не был установлен загружаемый контент. - + Successfully removed %1 installed DLC. Установленный загружаемый контент %1 был успешно удалён - + Delete OpenGL Transferable Shader Cache? Удалить переносной кэш шейдеров OpenGL? - + Delete Vulkan Transferable Shader Cache? Удалить переносной кэш шейдеров Vulkan? - + Delete All Transferable Shader Caches? Удалить весь переносной кэш шейдеров? - + Remove Custom Game Configuration? Удалить пользовательскую настройку игры? - + Remove File Удалить файл - - + + Error Removing Transferable Shader Cache Ошибка при удалении переносного кэша шейдеров - - + + A shader cache for this title does not exist. Кэш шейдеров для этой игры не существует. - + Successfully removed the transferable shader cache. Переносной кэш шейдеров успешно удалён. - + Failed to remove the transferable shader cache. Не удалось удалить переносной кэш шейдеров. - - + + Error Removing Transferable Shader Caches Ошибка при удалении переносного кэша шейдеров - + Successfully removed the transferable shader caches. Переносной кэш шейдеров успешно удален. - + Failed to remove the transferable shader cache directory. Ошибка при удалении папки переносного кэша шейдеров. - - + + Error Removing Custom Configuration Ошибка при удалении пользовательской настройки - + A custom configuration for this title does not exist. Пользовательская настройка для этой игры не существует. - + Successfully removed the custom game configuration. Пользовательская настройка игры успешно удалена. - + Failed to remove the custom game configuration. Не удалось удалить пользовательскую настройку игры. - - + + RomFS Extraction Failed! Не удалось извлечь RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Произошла ошибка при копировании файлов RomFS или пользователь отменил операцию. - + Full Полный - + Skeleton Скелет - + Select RomFS Dump Mode Выберите режим дампа RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Пожалуйста, выберите, как вы хотите выполнить дамп RomFS. <br>Полный скопирует все файлы в новую папку, в то время как <br>скелет создаст только структуру папок. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root В %1 недостаточно свободного места для извлечения RomFS. Пожалуйста, освободите место или выберите другую папку для дампа в Эмуляция > Настройка > Система > Файловая система > Корень дампа - + Extracting RomFS... Извлечение RomFS... - - + + Cancel Отмена - + RomFS Extraction Succeeded! Извлечение RomFS прошло успешно! - + The operation completed successfully. Операция выполнена. - + Error Opening %1 Ошибка открытия %1 - + Select Directory Выбрать папку - + Properties Свойства - + The game properties could not be loaded. Не удалось загрузить свойства игры. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Исполняемый файл Switch (%1);;Все файлы (*.*) - + Load File Загрузить файл - + Open Extracted ROM Directory Открыть папку извлечённого ROM'а - + Invalid Directory Selected Выбрана недопустимая папка - + The directory you have selected does not contain a 'main' file. Папка, которую вы выбрали, не содержит файла 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Устанавливаемый файл Switch (*.nca, *.nsp, *.xci);;Архив контента Nintendo (*.nca);;Пакет подачи Nintendo (*.nsp);;Образ картриджа NX (*.xci) - + Install Files Установить файлы - + %n file(s) remaining Остался %n файлОсталось %n файл(ов)Осталось %n файл(ов)Осталось %n файл(ов) - + Installing file "%1"... Установка файла "%1"... - - + + Install Results Установить результаты - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Чтобы избежать возможных конфликтов, мы не рекомендуем пользователям устанавливать игры в NAND. Пожалуйста, используйте эту функцию только для установки обновлений и загружаемого контента. - + %n file(s) were newly installed %n файл был недавно установлен @@ -4664,7 +4733,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n файл был перезаписан @@ -4674,7 +4743,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n файл не удалось установить @@ -4684,411 +4753,391 @@ Please, only use this feature to install updates and DLC. - + System Application Системное приложение - + System Archive Системный архив - + System Application Update Обновление системного приложения - + Firmware Package (Type A) Пакет прошивки (Тип А) - + Firmware Package (Type B) Пакет прошивки (Тип Б) - + Game Игра - + Game Update Обновление игры - + Game DLC Загружаемый контент игры - + Delta Title Дельта-титул - + Select NCA Install Type... Выберите тип установки NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Пожалуйста, выберите тип приложения, который вы хотите установить для этого NCA: (В большинстве случаев, подходит стандартный выбор «Игра».) - + Failed to Install Ошибка установки - + The title type you selected for the NCA is invalid. Тип приложения, который вы выбрали для NCA, недействителен. - + File not found Файл не найден - + File "%1" not found Файл "%1" не найден - + OK ОК - + Missing yuzu Account Отсутствует аккаунт yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Чтобы отправить отчет о совместимости игры, необходимо привязать свою учетную запись yuzu.<br><br/>Чтобы привязать свою учетную запись yuzu, перейдите в раздел Эмуляция &gt; Параметры &gt; Сеть. - + Error opening URL Ошибка при открытии URL - + Unable to open the URL "%1". Не удалось открыть URL: "%1". - + TAS Recording Запись TAS - + Overwrite file of player 1? Перезаписать файл игрока 1? - + Invalid config detected Обнаружена недопустимая конфигурация - + Handheld controller can't be used on docked mode. Pro controller will be selected. Портативный контроллер не может быть использован в режиме док-станции. Будет выбран контроллер Pro. - - + + Error Ошибка - - + + The current game is not looking for amiibos Текущая игра не ищет amiibo - - + + Amiibo Amiibo - - + + The current amiibo has been removed Текущий amiibo был убран - + Amiibo File (%1);; All Files (*.*) Файл Amiibo (%1);; Все Файлы (*.*) - + Load Amiibo Загрузить Amiibo - - Error opening Amiibo data file - Ошибка открытия файла данных Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Невозможно открыть файл Amiibo "%1" для чтения. - - - - Error reading Amiibo data file - Ошибка чтения файла данных Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Невозможно полностью прочитать данные Amiibo. Ожидалось прочитать %1 байт, но удалось прочитать только %2 байт. - - - + Error loading Amiibo data Ошибка загрузки данных Amiibo - + Unable to load Amiibo data. Невозможно загрузить данные Amiibo. - + Capture Screenshot Сделать скриншот - + PNG Image (*.png) Изображение PNG (*.png) - + TAS state: Running %1/%2 Состояние TAS: Выполняется %1/%2 - + TAS state: Recording %1 Состояние TAS: Записывается %1 - + TAS state: Idle %1/%2 Состояние TAS: Простой %1/%2 - + TAS State: Invalid Состояние TAS: Неверное - + &Stop Running [&S] Остановка - + &Start [&S] Начать - + Stop R&ecording [&E] Закончить запись - + R&ecord [&E] Запись - + Building: %n shader(s) Постройка: %n шейдерПостройка: %n шейдер(ов)Постройка: %n шейдер(ов)Постройка: %n шейдер(ов) - + Scale: %1x %1 is the resolution scaling factor Масштаб: %1x - + Speed: %1% / %2% Скорость: %1% / %2% - + Speed: %1% Скорость: %1% - + Game: %1 FPS (Unlocked) Игра: %1 FPS (Неограниченно) - + Game: %1 FPS Игра: %1 FPS - + Frame: %1 ms Кадр: %1 мс - + GPU NORMAL ГП НОРМАЛЬНО - + GPU HIGH ГП ВЫСОКО - + GPU EXTREME ГП ЭКСТРИМ - + GPU ERROR ГП ОШИБКА - + DOCKED В ДОК-СТАНЦИИ - + HANDHELD ПОРТАТИВНЫЙ - + NEAREST БЛИЖАЙШИЙ - - + + BILINEAR БИЛИНЕЙНЫЙ - + BICUBIC БИКУБИЧЕСКИЙ - + GAUSSIAN ГАУСС - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA БЕЗ СГЛАЖИВАНИЯ - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Игра, которую вы пытаетесь загрузить, требует, чтобы дополнительные файлы были сдамплены с вашего Switch перед началом игры. <br/><br/>Для получения дополнительной информации о дампе этих файлов см. следующую вики: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Дамп системных архивов и общих шрифтов с консоли</a>. <br/><br/>Хотите вернуться к списку игр? Продолжение эмуляции может привести к сбоям, повреждению сохраненных данных или другим ошибкам. - + yuzu was unable to locate a Switch system archive. %1 yuzu не удалось найти системный архив Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu не удалось найти системный архив Switch: %1. %2 - + System Archive Not Found Системный архив не найден - + System Archive Missing Отсутствует системный архив - + yuzu was unable to locate the Switch shared fonts. %1 yuzu не удалось найти общие шрифты Switch. %1 - + Shared Fonts Not Found Общие шрифты не найдены - + Shared Font Missing Общие шрифты отсутствуют - + Fatal Error Фатальная ошибка - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu столкнулся с фатальной ошибкой, проверьте журнал для получения более подробной информации. Для получения дополнительной информации о доступе к журналу откройте следующую страницу: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Как загрузить файл журнала</a>.<br/><br/>Вы хотите вернуться к списку игр? Продолжение эмуляции может привести к сбоям, повреждению сохранений или другим ошибкам. - + Fatal Error encountered Произошла фатальная ошибка - + Confirm Key Rederivation Подтвердите перерасчет ключа - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5105,37 +5154,37 @@ This will delete your autogenerated key files and re-run the key derivation modu Это удалит ваши автоматически сгенерированные файлы ключей и повторно запустит модуль расчета ключей. - + Missing fuses Отсутствуют предохранители - + - Missing BOOT0 - Отсутствует BOOT0 - + - Missing BCPKG2-1-Normal-Main - Отсутствует BCPKG2-1-Normal-Main - + - Missing PRODINFO - Отсутствует PRODINFO - + Derivation Components Missing Компоненты расчета отсутствуют - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Ключи шифрования отсутствуют. <br>Пожалуйста, следуйте <a href='https://yuzu-emu.org/help/quickstart/'>краткому руководству пользователя yuzu</a>, чтобы получить все ваши ключи, прошивку и игры.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5144,39 +5193,39 @@ on your system's performance. от производительности вашей системы. - + Deriving Keys Получение ключей - + Select RomFS Dump Target Выберите цель для дампа RomFS - + Please select which RomFS you would like to dump. Пожалуйста, выберите, какой RomFS вы хотите сдампить. - + Are you sure you want to close yuzu? Вы уверены, что хотите закрыть yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Вы уверены, что хотите остановить эмуляцию? Любой несохраненный прогресс будет потерян. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5558,12 +5607,12 @@ Screen. HostRoomWindow - + Error Ошибка - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Не удалось объявить комнату в публичном лобби. Чтобы хостить публичную комнату, у вас должна быть действующая учетная запись yuzu, настроенная в Эмуляция -> Параметры -> Сеть. Если вы не хотите объявлять комнату в публичном лобби, выберите вместо этого скрытый тип. @@ -5573,11 +5622,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute Включение/отключение звука + @@ -5599,112 +5649,111 @@ Debug Message: - Main Window Основное окно - + Audio Volume Down Уменьшить громкость звука - + Audio Volume Up Повысить громкость звука - + Capture Screenshot Сделать скриншот - + Change Adapting Filter Изменить адаптирующий фильтр - + Change Docked Mode Изменить режим консоли - + Change GPU Accuracy Изменить точность ГП - + Continue/Pause Emulation Продолжение/Пауза эмуляции - + Exit Fullscreen Выйти из полноэкранного режима - + Exit yuzu Выйти из yuzu - + Fullscreen Полный экран - + Load File Загрузить файл - + Load/Remove Amiibo Загрузить/удалить Amiibo - + Restart Emulation Перезапустить эмуляцию - + Stop Emulation Остановить эмуляцию - + TAS Record Запись TAS - + TAS Reset Сброс TAS - + TAS Start/Stop Старт/Стоп TAS - + Toggle Filter Bar Переключить панель поиска - + Toggle Framerate Limit Переключить ограничение частоты кадров - + Toggle Mouse Panning Переключить панорамирование мыши - + Toggle Status Bar Переключить панель состояния @@ -5819,42 +5868,42 @@ Debug Message: Обновить лобби - + Password Required to Join Для входа необходим пароль - + Password: Пароль: - + Room Name Название комнаты - + Preferred Game Предпочтительная игра - + Host Хост - + Players Игроки - + Refreshing Обновление - + Refresh List Обновить список @@ -6181,7 +6230,7 @@ Debug Message: - + Connected Подключено @@ -6204,7 +6253,7 @@ Debug Message: Сообщение отладки: - + New Messages Received Получены новые сообщения @@ -6294,7 +6343,7 @@ Debug Message: IP address is already in use. Please choose another. - + IP-адрес уже используется. Пожалуйста, выберите другой. @@ -6309,22 +6358,41 @@ They may have left the room. Возможно, они покинули комнату. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Не выбран интерфейс сети. +Пожалуйста, перейдите в Параметры -> Система -> Сеть и сделайте выбор. + + + + Game already running + Игра уже запущена + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Присоединяться к комнате, когда игра уже запущена, не рекомендуется, это может привести к неправильной работе функции комнаты. +Все равно продолжить? + + + Leave Room Покинуть комнату - + You are about to close the room. Any network connections will be closed. Вы собираетесь закрыть комнату. Все сетевые подключения будут закрыты. - + Disconnect Отключиться - + You are about to leave the room. Any network connections will be closed. Вы собираетесь покинуть комнату. Все сетевые подключения будут закрыты. @@ -6332,7 +6400,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Ошибка @@ -6391,7 +6459,7 @@ p, li { white-space: pre-wrap; } %1 играет в %2 - + Not playing a game Не играет в игру @@ -6446,7 +6514,7 @@ p, li { white-space: pre-wrap; } - + [not set] [не задано] @@ -6461,10 +6529,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Ось %1%2 @@ -6478,9 +6546,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [неизвестно] @@ -6645,15 +6713,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [недопустимо] - - + + %1%2Hat %3 %1%2Крест. %3 @@ -6661,35 +6729,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Ось %3 - + %1%2Axis %3,%4,%5 %1%2Ось %3,%4,%5 - + %1%2Motion %3 %1%2Движение %3 - - + + %1%2Button %3 %1%2Кнопка %3 - + [unused] [не используется] @@ -6730,7 +6798,7 @@ p, li { white-space: pre-wrap; } Дополнительная - + %1%2%3 %1%2%3 diff --git a/dist/languages/sv.ts b/dist/languages/sv.ts index 00d08a5ef2..eda5562907 100644 --- a/dist/languages/sv.ts +++ b/dist/languages/sv.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -740,200 +740,235 @@ avgjord kod.</div> ConfigureDebug - + Debugger - + Enable GDB Stub Aktivera GDB Stub - + Port: Port: - + Logging Loggning - + Global Log Filter Globalt Loggfilter - + Show Log in Console - + Open Log Location Öppna Logg-Destination - + When checked, the max size of the log increases from 100 MB to 1 GB - + Enable Extended Logging** - + Homebrew Homebrew - + Arguments String Argumentsträng - + Graphics Grafik - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Sätt på grafikdebugging - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT Stäng av Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Felsökning - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avancerat - + Kiosk (Quest) Mode Kiosk(Quest)-läge - + Enable CPU Debugging - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1552,37 +1587,47 @@ avgjord kod.</div> - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropisk filtrering: - + Automatic - + Default Standard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2074,7 +2119,7 @@ avgjord kod.</div> - + Left Stick Vänster Spak @@ -2168,14 +2213,14 @@ avgjord kod.</div> - + L L - + ZL ZL @@ -2194,7 +2239,7 @@ avgjord kod.</div> - + Plus Pluss @@ -2207,15 +2252,15 @@ avgjord kod.</div> - + R R - + ZR ZR @@ -2272,230 +2317,235 @@ avgjord kod.</div> - + Right Stick Höger Spak - - - - + + + + Clear Rensa - - - - - + + + + + [not set] [ej angett] - - - Toggle button - - - - - + + Invert button - - + + + Toggle button + + + + + Invert axis - - - + + + Set threshold - - + + Choose a value between 0% and 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. - + Center axis - - + + Deadzone: %1% Dödzon: %1% - - + + Modifier Range: %1% Modifieringsräckvidd: %1% - - + + Pro Controller Prokontroller - + Dual Joycons Dubbla Joycons - + Left Joycon Vänster Joycon - + Right Joycon Höger Joycon - + Handheld Handhållen - + GameCube Controller - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause - + Z - + Control Stick - + C-Stick - + Shake! - + [waiting] [väntar] - + New Profile Ny profil - + Enter a profile name: - - + + Create Input Profile - + The given profile name is not valid! - + Failed to create the input profile "%1" - + Delete Input Profile - + Failed to delete the input profile "%1" - + Load Input Profile - + Failed to load the input profile "%1" - + Save Input Profile - + Failed to save the input profile "%1" @@ -2750,42 +2800,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Utvecklare - + Add-Ons Tillägg - + General Allmänt - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics Avancerade Grafikinställningar - + Audio Ljud - + Properties egenskaper @@ -3497,47 +3547,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Sökväg - + ... ... @@ -3947,7 +3997,7 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r - + Verify Verifiera @@ -4034,7 +4084,7 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r - + Unspecified Ospecificerat @@ -4049,17 +4099,36 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r Polletten verifierades inte. Ändringen till din pollett har inte sparats. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifierar... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verifiering misslyckad + + + Verification failed Verifiering misslyckad - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verifiering misslyckad. Kontrollera att du har skrivit in din pollett korrekt, och att din internetuppkoppling fungerar. @@ -4128,12 +4197,12 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r DirectConnectWindow - + Connecting - + Connect @@ -4141,908 +4210,888 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonym data skickas </a>För att förbättra yuzu. <br/><br/>Vill du dela med dig av din användarstatistik med oss? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Laddar WebApplet... - + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Mängden shaders som just nu byggs - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Nuvarande emuleringshastighet. Värden över eller under 100% indikerar på att emulationen körs snabbare eller långsammare än en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hur många bilder per sekund som spelet just nu visar. Detta varierar från spel till spel och scen till scen. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid det tar att emulera en Switch bild, utan att räkna med framelimiting eller v-sync. För emulering på full hastighet så ska det vara som mest 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Paus - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Varning Föråldrat Spelformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Du använder det dekonstruerade ROM-formatet för det här spelet. Det är ett föråldrat format som har överträffats av andra som NCA, NAX, XCI eller NSP. Dekonstruerade ROM-kataloger saknar ikoner, metadata och uppdatering.<br><br>För en förklaring av de olika format som yuzu stöder, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>kolla in vår wiki</a>. Det här meddelandet visas inte igen. - - + + Error while loading ROM! Fel vid laddning av ROM! - + The ROM format is not supported. ROM-formatet stöds inte. - + An error occurred initializing the video core. Ett fel inträffade vid initiering av videokärnan. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Ett okänt fel har uppstått. Se loggen för mer information. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data Spardata - + Mod Data Mod-data - + Error Opening %1 Folder Fel Öppnar %1 Mappen - - + + Folder does not exist! Mappen finns inte! - + Error Opening Transferable Shader Cache Fel Under Öppning Av Överförbar Shadercache - + Failed to create the shader cache directory for this title. - + Contents Innehåll - + Update Uppdatera - + DLC DLC - + Remove Entry Ta bort katalog - + Remove Installed Game %1? Ta Bort Installerat Spel %1? - - - - - - + + + + + + Successfully Removed Framgångsrikt borttagen - + Successfully removed the installed base game. Tog bort det installerade basspelet framgångsrikt. - - - + + + Error Removing %1 Fel Under Borttagning Av %1 - + The base game is not installed in the NAND and cannot be removed. Basspelet är inte installerat i NAND och kan inte tas bort. - + Successfully removed the installed update. Tog bort den installerade uppdateringen framgångsrikt. - + There is no update installed for this title. Det finns ingen uppdatering installerad för denna titel. - + There are no DLC installed for this title. Det finns inga DLC installerade för denna titel. - + Successfully removed %1 installed DLC. Tog framgångsrikt bort den %1 installerade DLCn. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? Ta Bort Anpassad Spelkonfiguration? - + Remove File Radera fil - - + + Error Removing Transferable Shader Cache Fel När Överförbar Shader Cache Raderades - - + + A shader cache for this title does not exist. En shader cache för denna titel existerar inte. - + Successfully removed the transferable shader cache. Raderade den överförbara shadercachen framgångsrikt. - + Failed to remove the transferable shader cache. Misslyckades att ta bort den överförbara shadercache - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Fel När Anpassad Konfiguration Raderades - + A custom configuration for this title does not exist. En anpassad konfiguration för denna titel existerar inte. - + Successfully removed the custom game configuration. Tog bort den anpassade spelkonfigurationen framgångsrikt. - + Failed to remove the custom game configuration. Misslyckades att ta bort den anpassade spelkonfigurationen. - - + + RomFS Extraction Failed! RomFS Extraktion Misslyckades! - + There was an error copying the RomFS files or the user cancelled the operation. Det uppstod ett fel vid kopiering av RomFS filer eller användaren avbröt operationen. - + Full Full - + Skeleton Skelett - + Select RomFS Dump Mode Välj RomFS Dump-Läge - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Välj hur du vill att RomFS ska dumpas. <br>Full kommer att kopiera alla filer i den nya katalogen medan <br>skelett bara skapar katalogstrukturen. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Extraherar RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! RomFS Extraktion Lyckades! - + The operation completed successfully. Operationen var lyckad. - + Error Opening %1 Fel under öppning av %1 - + Select Directory Välj Katalog - + Properties Egenskaper - + The game properties could not be loaded. Spelegenskaperna kunde inte laddas. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Körbar (%1);;Alla Filer (*.*) - + Load File Ladda Fil - + Open Extracted ROM Directory Öppna Extraherad ROM-Katalog - + Invalid Directory Selected Ogiltig Katalog Vald - + The directory you have selected does not contain a 'main' file. Katalogen du har valt innehåller inte en 'main'-fil. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installerbar Switch-fil (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installera filer - + %n file(s) remaining - + Installing file "%1"... Installerar Fil "%1"... - - + + Install Results Installera resultat - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemapplikation - + System Archive Systemarkiv - + System Application Update Systemapplikationsuppdatering - + Firmware Package (Type A) Firmwarepaket (Typ A) - + Firmware Package (Type B) Firmwarepaket (Typ B) - + Game Spel - + Game Update Speluppdatering - + Game DLC Spel DLC - + Delta Title Delta Titel - + Select NCA Install Type... Välj NCA-Installationsläge... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Välj vilken typ av titel du vill installera som: (I de flesta fallen, standard 'Spel' är bra.) - + Failed to Install Misslyckades med Installationen - + The title type you selected for the NCA is invalid. Den titeltyp du valt för NCA är ogiltig. - + File not found Filen hittades inte - + File "%1" not found Filen "%1" hittades inte - + OK OK - + Missing yuzu Account yuzu Konto hittades inte - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. För att skicka ett spelkompatibilitetstest, du måste länka ditt yuzu-konto.<br><br/>För att länka ditt yuzu-konto, gå till Emulering &gt, Konfigurering &gt, Web. - + Error opening URL Fel när URL öppnades - + Unable to open the URL "%1". Oförmögen att öppna URL:en "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Amiibo Fil (%1);; Alla Filer (*.*) - + Load Amiibo Ladda Amiibo - - Error opening Amiibo data file - Fel öppnar Amiibo-datafilen - - - - Unable to open Amiibo file "%1" for reading. - Kunde inte öppna Amiibo filen "%1" för läsning. - - - - Error reading Amiibo data file - Fel vid läsning av Amiibo-datafil - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Kan inte läsa Amiibo-data helt. Förväntas läsa %1 byte, men kunde bara läsa %2 byte. - - - + Error loading Amiibo data Fel vid laddning av Amiibodata - + Unable to load Amiibo data. Kan inte ladda Amiibodata. - + Capture Screenshot Skärmdump - + PNG Image (*.png) PNG Bild (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Spel: %1 FPS - + Frame: %1 ms Ruta: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Spelet du försöker ladda kräver att ytterligare filer dumpas från din Switch innan du spelar.<br/><br/>För mer information om dumpning av dessa filer, se följande wiki sida: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumpning System Arkiv och Delade Teckensnitt från en Switchkonsol</a>.<br/><br/>Vill du avsluta till spellistan? Fortsatt emulering kan leda till kraschar, skadad spara data och andra buggar. - + yuzu was unable to locate a Switch system archive. %1 yuzu kunde inte lokalisera ett Switchsystemarkiv. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu kunde inte lokalisera ett Switchsystemarkiv: %1. %2 - + System Archive Not Found Systemarkivet Hittades Inte - + System Archive Missing Systemarkiv Saknas - + yuzu was unable to locate the Switch shared fonts. %1 yuzu kunde inte lokalisera Switchens delade fonter. %1 - + Shared Fonts Not Found Delade Teckensnitt Hittades Inte - + Shared Font Missing Delad Font Saknas - + Fatal Error Dödligt Fel - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu stötte på ett dödligt fel, se loggen för mer information. För mer information om åtkomst till loggen, se följande sida: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Hur man Laddar upp Loggfilen</a>.<br/><br/>Vill du avsluta till spellistan? Fortsatt emulering kan leda till kraschar, skadad spara data och andra buggar. - + Fatal Error encountered Allvarligt fel påträffat - + Confirm Key Rederivation Bekräfta Nyckel Rederivering - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5059,37 +5108,37 @@ och eventuellt göra säkerhetskopior. Detta raderar dina autogenererade nyckelfiler och kör nyckelderivationsmodulen. - + Missing fuses Saknade säkringar - + - Missing BOOT0 - Saknar BOOT0 - + - Missing BCPKG2-1-Normal-Main - Saknar BCPKG2-1-Normal-Main - + - Missing PRODINFO - Saknar PRODINFO - + Derivation Components Missing Deriveringsdelar saknas - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5098,39 +5147,39 @@ Detta kan ta upp till en minut beroende på systemets prestanda. - + Deriving Keys Härleda Nycklar - + Select RomFS Dump Target Välj RomFS Dumpa Mål - + Please select which RomFS you would like to dump. Välj vilken RomFS du vill dumpa. - + Are you sure you want to close yuzu? Är du säker på att du vill stänga yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Är du säker på att du vill stoppa emuleringen? Du kommer att förlora osparade framsteg. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5512,12 +5561,12 @@ startskärmen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5526,11 +5575,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5552,112 +5602,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Skärmdump - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Fullskärm - + Load File Ladda Fil - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5771,42 +5820,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Spelare - + Refreshing - + Refresh List @@ -6133,7 +6182,7 @@ Debug Message: - + Connected Kopplad @@ -6155,7 +6204,7 @@ Debug Message: - + New Messages Received @@ -6259,22 +6308,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6282,7 +6348,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6337,7 +6403,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6392,7 +6458,7 @@ p, li { white-space: pre-wrap; } - + [not set] [inte inställd] @@ -6407,10 +6473,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axel %1%2 @@ -6424,9 +6490,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [okänd] @@ -6591,15 +6657,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6607,35 +6673,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [oanvänd] @@ -6676,7 +6742,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/tr_TR.ts b/dist/languages/tr_TR.ts index 1e5c846bf5..89e1f26925 100644 --- a/dist/languages/tr_TR.ts +++ b/dist/languages/tr_TR.ts @@ -25,12 +25,18 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Yuzu GPLv3.0+ ile lisanslanmış Nintendo Switch için açık kaynak bir deneysel emülatördür.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Bu yazılım yasal yollarla edinilmemiş oyunları çalıştırmak için kullanılmamalı.</span></p></body></html> <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a>|<a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Kaynak Kodu</span></a>|<a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Katkıda Bulunanlar</span></a>|<a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Lisans</span></a></p></body></html> @@ -76,91 +82,91 @@ p, li { white-space: pre-wrap; } Room Window - + Oda Penceresi Send Chat Message - + Sohbet Mesajı At Send Message - + Mesaj Gönder - + Members - + Üyeler - + %1 has joined - + %1 katıldı - + %1 has left - + %1 ayrıldı - + %1 has been kicked - + %1 atıldı - + %1 has been banned - + %1 yasaklandı - + %1 has been unbanned - - - - - View Profile - + %1'in yasağı kaldırıldı - - Block Player - + View Profile + Profili Görüntüle - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + + + Block Player + Kullanıcıyı Engelle + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Bir kullanıcıyı engellediğinizde, ondan mesaj alamayacaksınız.<br><br> %1'i engellemek istediğinizden emin misiniz? + + + Kick - - - - - Ban - - - - - Kick Player - - - - - Are you sure you would like to <b>kick</b> %1? - + At - Ban Player - + Ban + Yasakla - + + Kick Player + Kullanıcıyı At + + + + Are you sure you would like to <b>kick</b> %1? + %1'i <b>atmak</b> istediğinizden emin misiniz? + + + + Ban Player + Kullanıcıyı Yasakla + + + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -172,22 +178,22 @@ This would ban both their forum username and their IP address. Room Window - + Oda Penceresi Room Description - + Oda Açıklaması Moderation... - + Moderasyon... Leave Room - + Odadan Ayrıl @@ -200,7 +206,7 @@ This would ban both their forum username and their IP address. Disconnected - + Bağlantı kesildi @@ -392,17 +398,17 @@ This would ban both their forum username and their IP address. Preview - + Önizle Resolution: 320*240 - + Çözünürlük: 320*240 Click to preview - + Önizlemek için tıkla @@ -747,200 +753,235 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra ConfigureDebug - + Debugger - + Hata Ayıklayıcı - + Enable GDB Stub GDB Stub'ı Etkinleştir - + Port: Port: - + Logging Kütük Tutma - + Global Log Filter Evrensel Kütük Filtresi - + Show Log in Console Konsolda Log'u Göster - + Open Log Location Kütük Konumunu Aç - + When checked, the max size of the log increases from 100 MB to 1 GB Etkinleştirildiğinde log'un boyut sınırı 100 MB'tan 1 GB'a çıkar - + Enable Extended Logging** Uzatılmış Hata Kaydını Etkinleştir. - + Homebrew Homebrew - + Arguments String Arguments String - + Graphics Grafikler - + When checked, the graphics API enters a slower debugging mode Etkinleştirildiğinde, grafik API'ı daha yavaş bir hata ayıklama moduna girer. - + Enable Graphics Debugging Grafik Hata Ayıklama Modunu Etkinleştir - + When checked, it enables Nsight Aftermath crash dumps İşaretlendiğinde Nsight Aftermath çökme dökümlerini etkinleştirir. - + Enable Nsight Aftermath Nsight Aftermath'ı Etkinleştir - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower İşaretlendiğinde Makro JIT derleyicisini devre dışı bırakır. Bu seçeneği etkinleştirmek oyunların yavaş çalışmasına neden olur. - + Disable Macro JIT Macro JIT'i devre dışı bırak - + When checked, yuzu will log statistics about the compiled pipeline cache Etkinleştirildiğinde, yuzu derlenen pipeline cache istatistiklerini log'a kaydeder. - + Enable Shader Feedback Shader Geribildirimini Etkinleştir - + When checked, it executes shaders without loop logic changes İşaretlendiğinde shaderları döngü mantık değişimleri olmaksızın uygular - + Disable Loop safety checks Döngü güvenliği kontrolünü devre dışı bırak - + Debugging Hata ayıklama - - Enable FS Access Log - FS Erişim Kaydını Etkinleştir - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Detaylı Raporlama Hizmetini Etkinleştir - + + Enable FS Access Log + FS Erişim Kaydını Etkinleştir + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Gelişmiş - + Kiosk (Quest) Mode Kiosk (Quest) Modu - + Enable CPU Debugging CPU Hata Ayıklama Modu'nu Etkinleştir - + Enable Debug Asserts Hata Ayıklama Assert'lerini Etkinleştir - + Enable Auto-Stub** Auto-Stub'ı Etkinleştir - + Enable All Controller Types Bütün Kontrolcü Türlerini Etkinleştir - + Disable Web Applet Web Uygulamasını Devre Dışı Bırak - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Bu yuzu kapandığında otomatik olarak eski haline dönecektir. + + + Restart Required + Yeniden Başlatma Gerekli + + + + yuzu is required to restart in order to apply this setting. + yuzu'nun bu ayarı uygulayabilmesi için yeniden başlatılması gereklidir. + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1249,7 +1290,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Pause emulation when in background - Arka plana alındığında emülasyonu durdur + Arka plana alındığında emülasyonu duraklat @@ -1536,7 +1577,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Use VSync - + VSync Kullan @@ -1559,37 +1600,47 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Hızlı GPU Saati Kullan (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropic Filtering: - + Automatic Otomatik - + Default Varsayılan - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1652,7 +1703,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Home+%1 - + Ev+%1 @@ -1984,7 +2035,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Ring Controller - + Ring Kontrolcüsü @@ -2081,7 +2132,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + Left Stick Sol Analog @@ -2175,14 +2226,14 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + L L - + ZL ZL @@ -2201,7 +2252,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + Plus Artı @@ -2214,15 +2265,15 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + R R - + ZR ZR @@ -2279,231 +2330,236 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + Right Stick Sağ Analog - - - - + + + + Clear Temizle - - - - - + + + + + [not set] [belirlenmedi] - - - Toggle button - Tuş ayarla - - - - + + Invert button Tuşları ters çevir - - + + + Toggle button + Tuş ayarla + + + + Invert axis Ekseni ters çevir - - - + + + Set threshold Alt sınır ayarla - - + + Choose a value between 0% and 100% %0 ve %100 arasında bir değer seçin - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Analog Çubuğu Ayarla - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Tamama bastıktan sonra, joystikinizi önce yatay sonra dikey olarak hareket ettirin. Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak hareket ettirin. - + Center axis - - + + Deadzone: %1% Ölü Bölge: %1% - - + + Modifier Range: %1% Düzenleyici Aralığı: %1% - - + + Pro Controller Pro Controller - + Dual Joycons İkili Joyconlar - + Left Joycon Sol Joycon - + Right Joycon Sağ Joycon - + Handheld Handheld - + GameCube Controller GameCube Kontrolcüsü - + Poke Ball Plus Poke Ball Plus - + NES Controller NES Kontrolcüsü - + SNES Controller SNES Kontrolcüsü - + N64 Controller N64 Kontrolcüsü - + Sega Genesis Sega Genesis - + Start / Pause - Başlat / Durdur + Başlat / Duraklat - + Z Z - + Control Stick Kontrol Çubuğu - + C-Stick C-Çubuğu - + Shake! Salla! - + [waiting] [bekleniyor] - + New Profile Yeni Profil - + Enter a profile name: Bir profil ismi girin: - - + + Create Input Profile Kontrol Profili Oluştur - + The given profile name is not valid! Girilen profil ismi geçerli değil! - + Failed to create the input profile "%1" "%1" kontrol profili oluşturulamadı - + Delete Input Profile Kontrol Profilini Kaldır - + Failed to delete the input profile "%1" "%1" kontrol profili kaldırılamadı - + Load Input Profile Kontrol Profilini Yükle - + Failed to load the input profile "%1" "%1" kontrol profili yüklenemedi - + Save Input Profile Kontrol Profilini Kaydet - + Failed to save the input profile "%1" "%1" kontrol profili kaydedilemedi @@ -2758,42 +2814,42 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har Geliştirici - + Add-Ons Eklentiler - + General Genel - + System Sistem - + CPU CPU - + Graphics Grafikler - + Adv. Graphics Gelişmiş Grafikler - + Audio Ses - + Properties Özellikler @@ -2992,7 +3048,7 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har Ring Sensor Parameters - + Ring Sensör Parametreleri @@ -3505,47 +3561,47 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har <html><head/><body><p> Kontrolcü girdilerini TAS-nx scriptleri ile aynı formatta okur. <br/>Daha detaylı bilgi için lütfen yuzu web sitesindeki <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;"> yardım sayfasına</span></a>bakınız.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Hangi kısayolların Yeniden Oynatma/Kayıt fonksiyonunu kontrol ettiğini öğrenmek için Kısayol Ayarlarına bakın. (Yapılandır -> Genel -> Kısayollar) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. UYARI: Bu deneysel bir özelliktir.<br/> Halihazırdaki kusurlu eşzamanlama yöntemi sebebiyle, scriptleri mükemmel olarak oynatmayacaktır. - + Settings Ayarlar - + Enable TAS features TAS özelliklerini Etkinleştir - + Loop script Döngü komut dosyası - + Pause execution during loads Yüklemeler sırasında yürütmeyi duraklat - + Script Directory Script Konumu - + Path Konum - + ... ... @@ -3955,7 +4011,7 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne - + Verify Doğrula @@ -4042,7 +4098,7 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne - + Unspecified Belirlenmemiş @@ -4057,17 +4113,36 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Token doğrulanmadı. Tokeninize yapılan değişiklik kaydedilmedi. - + + Unverified, please click Verify before saving configuration + Tooltip + Doğrulanmadı, lütfen konfigürasyonu kaydetmeden önce Doğrula tuşuna basın + + + + Verifying... Doğrulanıyor... - + + Verified + Tooltip + Doğrulandı + + + + Verification failed + Tooltip + Doğrulanma başarısız oldu + + + Verification failed Doğrulanma başarısız oldu - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Doğrulanma başarısız oldu. Kullanıcı adı ve tokeninizi doğru girdiğinizden ve internete bağlı olduğunuzdan. @@ -4090,17 +4165,17 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Direct Connect - + Direkt Bağlan IP Address - + IP Adresi IP - + IP @@ -4110,7 +4185,7 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Port - + Port @@ -4120,516 +4195,516 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Nickname - + Lakap Password - + Şifre Connect - + Bağlan DirectConnectWindow - + Connecting - + Bağlanılıyor - + Connect - + Bağlan GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Yuzuyu geliştirmeye yardımcı olmak için </a> anonim veri toplandı. <br/><br/>Kullanım verinizi bizimle paylaşmak ister misiniz? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Bozuk Vulkan Kurulumu Algılandı - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Web Uygulaması Yükleniyor... - + Disable Web Applet Web Uygulamasını Devre Dışı Bırak - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Şu anda derlenen shader miktarı - + The current selected resolution scaling multiplier. Geçerli seçili çözünürlük ölçekleme çarpanı. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Geçerli emülasyon hızı. %100'den yüksek veya düşük değerler emülasyonun bir Switch'den daha hızlı veya daha yavaş çalıştığını gösterir. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Oyunun şuanda saniye başına kaç kare gösterdiği. Bu oyundan oyuna ve sahneden sahneye değişiklik gösterir. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Bir Switch karesini emüle etmekte geçen zaman, karelimitleme ve v-sync hariç. Tam hız emülasyon için bu en çok 16,67 ms olmalı. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Son Dosyaları Temizle - + &Continue &Devam Et - + &Pause - &Durdur + &Duraklat - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu şu anda bir oyun çalıştırıyor - + Warning Outdated Game Format Uyarı, Eski Oyun Formatı - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Bu oyun için dekonstrükte ROM formatı kullanıyorsunuz, bu fromatın yerine NCA, NAX, XCI ve NSP formatları kullanılmaktadır. Dekonstrükte ROM formatları ikon, üst veri ve güncelleme desteği içermemektedir.<br><br>Yuzu'nun desteklediği çeşitli Switch formatları için<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Wiki'yi ziyaret edin</a>. Bu mesaj yeniden gösterilmeyecektir. - - + + Error while loading ROM! ROM yüklenirken hata oluştu! - + The ROM format is not supported. Bu ROM biçimi desteklenmiyor. - + An error occurred initializing the video core. Video çekirdeğini başlatılırken bir hata oluştu. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu video çekirdeğini çalıştırırken bir hatayla karşılaştı. Bu sorun genellikle eski GPU sürücüleri sebebiyle ortaya çıkar. Daha fazla detay için lütfen log dosyasına bakın. Log dosyasını incelemeye dair daha fazla bilgi için lütfen bu sayfaya ulaşın: <a href='https://yuzu-emu.org/help/reference/log-files/'>Log dosyası nasıl yüklenir</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM yüklenirken hata oluştu! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Lütfen dosyalarınızı yeniden dump etmek için<a href='https://yuzu-emu.org/help/quickstart/'>yuzu hızlı başlangıç kılavuzu'nu</a> takip edin.<br> Yardım için yuzu wiki</a>veya yuzu Discord'una</a> bakabilirsiniz. - + An unknown error occurred. Please see the log for more details. Bilinmeyen bir hata oluştu. Lütfen daha fazla detay için kütüğe göz atınız. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Kayıt Verisi - + Mod Data Mod Verisi - + Error Opening %1 Folder %1 klasörü açılırken hata - - + + Folder does not exist! Klasör mevcut değil! - + Error Opening Transferable Shader Cache Transfer Edilebilir Shader Cache'ini Açarken Bir Hata Oluştu - + Failed to create the shader cache directory for this title. Bu oyun için shader cache konumu oluşturulamadı. - + Contents İçerikler - + Update Güncelleme - + DLC DLC - + Remove Entry Girdiyi Kaldır - + Remove Installed Game %1? %1 Adlı Oyunu Kaldırmak İstediğinize Emin Misiniz? - - - - - - + + + + + + Successfully Removed Başarıyla Kaldırıldı - + Successfully removed the installed base game. Yüklenmiş oyun başarıyla kaldırıldı. - - - + + + Error Removing %1 %1 Adlı Oyun Kaldırılırken Bir Hata Oluştu - + The base game is not installed in the NAND and cannot be removed. Asıl oyun NAND'de kurulu değil ve kaldırılamaz. - + Successfully removed the installed update. Yüklenmiş güncelleme başarıyla kaldırıldı. - + There is no update installed for this title. Bu oyun için yüklenmiş bir güncelleme yok. - + There are no DLC installed for this title. Bu oyun için yüklenmiş bir DLC yok. - + Successfully removed %1 installed DLC. %1 yüklenmiş DLC başarıyla kaldırıldı. - + Delete OpenGL Transferable Shader Cache? OpenGL Transfer Edilebilir Shader Cache'ini Kaldırmak İstediğinize Emin Misiniz? - + Delete Vulkan Transferable Shader Cache? Vulkan Transfer Edilebilir Shader Cache'ini Kaldırmak İstediğinize Emin Misiniz? - + Delete All Transferable Shader Caches? Tüm Transfer Edilebilir Shader Cache'leri Kaldırmak İstediğinize Emin Misiniz? - + Remove Custom Game Configuration? Oyuna Özel Yapılandırmayı Kaldırmak İstediğinize Emin Misiniz? - + Remove File Dosyayı Sil - - + + Error Removing Transferable Shader Cache Transfer Edilebilir Shader Cache Kaldırılırken Bir Hata Oluştu - - + + A shader cache for this title does not exist. Bu oyun için oluşturulmuş bir shader cache yok. - + Successfully removed the transferable shader cache. Transfer edilebilir shader cache başarıyla kaldırıldı. - + Failed to remove the transferable shader cache. Transfer edilebilir shader cache kaldırılamadı. - - + + Error Removing Transferable Shader Caches Transfer Edilebilir Shader Cache'ler Kaldırılırken Bir Hata Oluştu - + Successfully removed the transferable shader caches. Transfer edilebilir shader cacheler başarıyla kaldırıldı. - + Failed to remove the transferable shader cache directory. Transfer edilebilir shader cache konumu kaldırılamadı. - - + + Error Removing Custom Configuration Oyuna Özel Yapılandırma Kaldırılırken Bir Hata Oluştu. - + A custom configuration for this title does not exist. Bu oyun için bir özel yapılandırma yok. - + Successfully removed the custom game configuration. Oyuna özel yapılandırma başarıyla kaldırıldı. - + Failed to remove the custom game configuration. Oyuna özel yapılandırma kaldırılamadı. - - + + RomFS Extraction Failed! RomFS Çıkartımı Başarısız! - + There was an error copying the RomFS files or the user cancelled the operation. RomFS dosyaları kopyalanırken bir hata oluştu veya kullanıcı işlemi iptal etti. - + Full Full - + Skeleton Çerçeve - + Select RomFS Dump Mode RomFS Dump Modunu Seçiniz - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Lütfen RomFS'in nasıl dump edilmesini istediğinizi seçin.<br>"Full" tüm dosyaları yeni bir klasöre kopyalarken <br>"skeleton" sadece klasör yapısını oluşturur. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 konumunda RomFS çıkarmaya yetecek alan yok. Lütfen yer açın ya da Emülasyon > Yapılandırma > Sistem > Dosya Sistemi > Dump konumu kısmından farklı bir çıktı konumu belirleyin. - + Extracting RomFS... RomFS çıkartılıyor... - - + + Cancel İptal - + RomFS Extraction Succeeded! RomFS Çıkartımı Başarılı! - + The operation completed successfully. İşlem başarıyla tamamlandı. - + Error Opening %1 %1 Açılırken Bir Hata Oluştu - + Select Directory Klasör Seç - + Properties Özellikler - + The game properties could not be loaded. Oyun özellikleri yüklenemedi. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Çalıştırılabilir Dosyası (%1);;Tüm Dosyalar (*.*) - + Load File Dosya Aç - + Open Extracted ROM Directory Çıkartılmış ROM klasörünü aç - + Invalid Directory Selected Geçersiz Klasör Seçildi - + The directory you have selected does not contain a 'main' file. Seçtiğiniz klasör bir "main" dosyası içermiyor. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Yüklenilebilir Switch Dosyası (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Dosya Kur - + %n file(s) remaining %n dosya kaldı%n dosya kaldı - + Installing file "%1"... "%1" dosyası kuruluyor... - - + + Install Results Kurulum Sonuçları - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Olası çakışmaları önlemek için oyunları NAND'e yüklememenizi tavsiye ediyoruz. Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + %n file(s) were newly installed %n dosya güncel olarak yüklendi @@ -4637,7 +4712,7 @@ Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + %n file(s) were overwritten %n dosyanın üstüne yazıldı @@ -4645,7 +4720,7 @@ Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + %n file(s) failed to install %n dosya yüklenemedi @@ -4653,411 +4728,391 @@ Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + System Application Sistem Uygulaması - + System Archive Sistem Arşivi - + System Application Update Sistem Uygulama Güncellemesi - + Firmware Package (Type A) Yazılım Paketi (Tür A) - + Firmware Package (Type B) Yazılım Paketi (Tür B) - + Game Oyun - + Game Update Oyun Güncellemesi - + Game DLC Oyun DLC'si - + Delta Title Delta Başlık - + Select NCA Install Type... NCA Kurulum Tipi Seçin... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Lütfen bu NCA dosyası için belirlemek istediğiniz başlık türünü seçiniz: (Çoğu durumda, varsayılan olan 'Oyun' kullanılabilir.) - + Failed to Install Kurulum Başarısız Oldu - + The title type you selected for the NCA is invalid. NCA için seçtiğiniz başlık türü geçersiz - + File not found Dosya Bulunamadı - + File "%1" not found Dosya "%1" Bulunamadı - + OK Tamam - + Missing yuzu Account Kayıp yuzu Hesabı - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Oyun uyumluluk test çalışması göndermek için öncelikle yuzu hesabınla giriş yapmanız gerekiyor.<br><br/>Yuzu hesabınızla giriş yapmak için, Emülasyon &gt; Yapılandırma &gt; Web'e gidiniz. - + Error opening URL URL açılırken bir hata oluştu - + Unable to open the URL "%1". URL "%1" açılamıyor. - + TAS Recording TAS kayıtta - + Overwrite file of player 1? Oyuncu 1'in dosyasının üstüne yazılsın mı? - + Invalid config detected Geçersiz yapılandırma tespit edildi - + Handheld controller can't be used on docked mode. Pro controller will be selected. Handheld kontrolcü dock modunda kullanılamaz. Pro kontrolcü seçilecek. - - + + Error Hata - - + + The current game is not looking for amiibos Aktif oyun amiibo beklemiyor - - + + Amiibo Amiibo - - + + The current amiibo has been removed Amiibo kaldırıldı - + Amiibo File (%1);; All Files (*.*) Amiibo Dosyası (%1);; Tüm Dosyalar (*.*) - + Load Amiibo Amiibo Yükle - - Error opening Amiibo data file - Amiibo veri dosyasını açarken hata - - - - Unable to open Amiibo file "%1" for reading. - "%1" Amiibo dosyası okunamadı - - - - Error reading Amiibo data file - Amiibo veri dosyasını okurken hata - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Amiibo verisi tamamen okunamadı. %1 byte okunması bekleniyordu, fakat bunun sadece %2'si okunabildi. - - - + Error loading Amiibo data Amiibo verisi yüklenirken hata - + Unable to load Amiibo data. Amiibo verisi yüklenemedi - + Capture Screenshot Ekran Görüntüsü Al - + PNG Image (*.png) PNG görüntüsü (*.png) - + TAS state: Running %1/%2 TAS durumu: %1%2 çalışıyor - + TAS state: Recording %1 TAS durumu: %1 kaydediliyor - + TAS state: Idle %1/%2 TAS durumu: %1%2 boşta - + TAS State: Invalid TAS durumu: Geçersiz - + &Stop Running &Çalıştırmayı durdur - + &Start &Başlat - + Stop R&ecording K&aydetmeyi Durdur - + R&ecord K&aydet - + Building: %n shader(s) Oluşturuluyor: %n shaderOluşturuluyor: %n shader - + Scale: %1x %1 is the resolution scaling factor Ölçek: %1x - + Speed: %1% / %2% Hız %1% / %2% - + Speed: %1% Hız: %1% - + Game: %1 FPS (Unlocked) Oyun: %1 FPS (Sınırsız) - + Game: %1 FPS Oyun: %1 FPS - + Frame: %1 ms Kare: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU YÜKSEK - + GPU EXTREME GPU EKSTREM - + GPU ERROR GPU HATASI - + DOCKED - + HANDHELD - + NEAREST EN YAKIN - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSYEN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Yüklemeye çalıştığınız oyun oynanmadan önce Switch'inizden ek dosyaların alınmasını gerektiriyor.<br/><br/>Bu dosyaları nasıl alacağınız hakkında daha fazla bilgi için, lütfen bu wiki sayfasına göz atınız: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Konsolunuzdan Sistem Arşivleri ve Shared Fontları Almak</a>.<br/><br/>oyun listesine geri dönmek ister misiniz? Emülasyona devam etmek çökmelere, kayıt dosyalarının bozulmasına veya başka hatalara sebep verebilir. - + yuzu was unable to locate a Switch system archive. %1 Yuzu bir Switch sistem arşivi bulamadı. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Yuzu bir Switch sistem arşivi bulamadı: %1. %2 - + System Archive Not Found Sistem Arşivi Bulunamadı - + System Archive Missing Sistem Arşivi Kayıp - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu Switch shared fontlarını bulamadı. %1 - + Shared Fonts Not Found Shared Font'lar Bulunamadı - + Shared Font Missing Shared Font Kayıp - + Fatal Error Önemli Hata - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Yuzu önemli bir hatayla karşılaştı, lütfen daha fazla detay için kütüğe bakınız. Kütüğe erişmek hakkında daha fazla bilgi için, lütfen bu sayfaya göz atınız: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Log Dosyası Nasıl Yüklenir</a>.<br/><br/>Oyun listesine geri dönmek ister misiniz? Emülasyona devam etmek çökmelere, kayıt dosyalarının bozulmasına veya başka hatalara sebep olabilir. - + Fatal Error encountered Önemli Bir Hatayla Karşılaşıldı - + Confirm Key Rederivation Anahtar Yeniden Türetimini Onayla - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5074,37 +5129,37 @@ ve opsiyonel olarak yedekler alın. Bu sizin otomatik oluşturulmuş anahtar dosyalarınızı silecek ve anahtar türetme modülünü tekrar çalıştıracak. - + Missing fuses Anahtarlar Kayıp - + - Missing BOOT0 - BOOT0 Kayıp - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main Kayıp - + - Missing PRODINFO - PRODINFO Kayıp - + Derivation Components Missing Türeten Bileşenleri Kayıp - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Şifreleme anahtarları eksik. <br>Lütfen takip edin<a href='https://yuzu-emu.org/help/quickstart/'>yuzu hızlı başlangıç kılavuzunu</a>tüm anahtarlarınızı, aygıt yazılımınızı ve oyunlarınızı almada.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5113,39 +5168,39 @@ Bu sistem performansınıza bağlı olarak bir dakika kadar zaman alabilir. - + Deriving Keys Anahtarlar Türetiliyor - + Select RomFS Dump Target RomFS Dump Hedefini Seçiniz - + Please select which RomFS you would like to dump. Lütfen dump etmek istediğiniz RomFS'i seçiniz. - + Are you sure you want to close yuzu? yuzu'yu kapatmak istediğinizden emin misiniz? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Emülasyonu durdurmak istediğinizden emin misiniz? Kaydedilmemiş veriler kaybolur. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5464,22 +5519,22 @@ Screen. Create Room - + Oda Oluştur Room Name - + Oda Adı Preferred Game - + Tercih Edilen Oyun Max Players - + Maksimum Oyuncular @@ -5489,37 +5544,37 @@ Screen. (Leave blank for open game) - + (Açık oyun için boş bırakın) Password - + Şifre Port - + Port Room Description - + Oda Açıklaması Load Previous Ban List - + Önceki Yasak Listesini Yükle Public - + Herkese Açık Unlisted - + Gizli @@ -5530,12 +5585,12 @@ Screen. HostRoomWindow - + Error Hata - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5544,11 +5599,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute - + Sesi Sustur/Aç + @@ -5570,112 +5626,111 @@ Debug Message: - Main Window - + Ana Pencere - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Ekran Görüntüsü Al - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Sürdür/Emülasyonu duraklat + + + + Exit Fullscreen + Tam Ekrandan Çık - Exit Fullscreen - + Exit yuzu + Yuzu'dan çık - Exit yuzu - - - - Fullscreen Tam Ekran - + Load File Dosya Aç - + Load/Remove Amiibo - + Restart Emulation - + Emülasyonu Yeniden Başlat + + + + Stop Emulation + Emülasyonu Durdur - Stop Emulation - + TAS Record + TAS Kaydet - TAS Record - + TAS Reset + TAS Sıfırla - TAS Reset - + TAS Start/Stop + TAS Başlat/Durdur - TAS Start/Stop - - - - Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5762,7 +5817,7 @@ Debug Message: Nickname - + Lakap @@ -5772,12 +5827,12 @@ Debug Message: Search - + Ara Games I Own - + Sahip Olduğum Oyunlar @@ -5790,44 +5845,44 @@ Debug Message: - + Password Required to Join - + Password: - + Şifre: - + Room Name - + Oda Adı - + Preferred Game - + Tercih Edilen Oyun - + Host - + Players Oyuncular - + Refreshing - + Yenileniyor - + Refresh List - + Listeyi Yenile @@ -5990,22 +6045,22 @@ Debug Message: Create Room - + Oda Oluştur Leave Room - + Odadan Ayrıl Direct Connect to Room - + Odaya Direkt Bağlan Show Current Room - + Şu Anki Odayı Göster @@ -6091,23 +6146,23 @@ Debug Message: Moderation - + Moderasyon Ban List - + Ban Listesi Refreshing - + Yenileniyor Unban - + Yasağı kaldır @@ -6117,22 +6172,22 @@ Debug Message: Type - + Tip Forum Username - + Forum Kullanıcı Adı IP Address - + IP Adresi Refresh - + Yenile @@ -6141,18 +6196,18 @@ Debug Message: Current connection status - + Anlık bağlantı durumu Not Connected. Click here to find a room! - + Bağlantı Yok. Oda bulmak için buraya basın! - + Connected Bağlandı @@ -6160,7 +6215,7 @@ Debug Message: Not Connected - + Bağlantı Yok @@ -6174,9 +6229,9 @@ Debug Message: - + New Messages Received - + Yeni Mesaj Alındı @@ -6184,27 +6239,27 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + Kullanıcı adı geçersiz. 4 ile 20 alfasayısal karakter arasında olmalı. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Oda adı geçersiz. 4 ile 20 alfasayısal karakter arasında olmalı. Username is already in use or not valid. Please choose another. - + Kullanıcı adı halihazırda kullanılıyor. Lütfen başka bir kullanıcı adı seçin. IP is not a valid IPv4 address. - + IP geçerli bir IPv4 adresi değil. Port must be a number between 0 to 65535. - + Port 0 ile 65535 arasında bir numara olmalı. @@ -6214,7 +6269,7 @@ Debug Message: Unable to find an internet connection. Check your internet settings. - + İnternet bağlantısı bulunamadı. İnternet ayarlarınızı kontrol edin. @@ -6224,17 +6279,17 @@ Debug Message: Unable to connect to the room because it is already full. - + Oda halihazırda dolu olduğundan dolayı katılınamadı. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + Odayı oluşturma başarısız oldu. Lütfen tekrar deneyin. Yuzu'yu yeniden başlatmak gerekebilir. The host of the room has banned you. Speak with the host to unban you or try a different room. - + Oda yöneticisi sizi odadan yasakladı. Yasağı kaldırmak için yönetici ile konuşun ya da başka bir oda deneyin. @@ -6244,7 +6299,7 @@ Debug Message: Incorrect password. - + Yanlış şifre. @@ -6254,17 +6309,17 @@ Debug Message: Connection to room lost. Try to reconnect. - + Odaya bağlantı kesildi. Yeniden bağlanmayı dene. You have been kicked by the room host. - + Oda yöneticisi seni odadan çıkardı. IP address is already in use. Please choose another. - + IP adresi halihazırda kullanılıyor. Lütfen başka bir IP adresi seçin. @@ -6278,22 +6333,39 @@ They may have left the room. - - Leave Room + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + Oyun halihazırda çalışıyor + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + + Leave Room + Odadan Ayrıl + + + You are about to close the room. Any network connections will be closed. - + Disconnect - + Bağlantı kesildi - + You are about to leave the room. Any network connections will be closed. @@ -6301,7 +6373,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Hata @@ -6344,7 +6416,7 @@ p, li { white-space: pre-wrap; } START/PAUSE - BAŞLAT/DURDUR + BAŞLAT/DURAKLAT @@ -6352,17 +6424,17 @@ p, li { white-space: pre-wrap; } %1 is not playing a game - + %1 şu anda oyun oynamıyor %1 is playing %2 - + %1 %2'yi oynuyor - + Not playing a game - + Şu anda oyun oynamıyor @@ -6415,7 +6487,7 @@ p, li { white-space: pre-wrap; } - + [not set] [belirlenmedi] @@ -6430,10 +6502,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eksen %1%2 @@ -6447,9 +6519,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [bilinmeyen] @@ -6614,15 +6686,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [geçersiz] - - + + %1%2Hat %3 %1%2Hat %3 @@ -6630,35 +6702,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eksen %3 - + %1%2Axis %3,%4,%5 %1%2Eksen %3,%4,%5 - + %1%2Motion %3 %1%2Hareket %3 - - + + %1%2Button %3 %1%2Tuş %3 - + [unused] [kullanılmayan] @@ -6699,7 +6771,7 @@ p, li { white-space: pre-wrap; } Ekstra - + %1%2%3 %1%2%3 diff --git a/dist/languages/vi.ts b/dist/languages/vi.ts index 6570ab6837..181e320aa8 100644 --- a/dist/languages/vi.ts +++ b/dist/languages/vi.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -733,200 +733,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Cho phép bật GDB sơ khai - + Port: Cổng: - + Logging Báo cáo - + Global Log Filter Bộ lọc báo cáo chung - + Show Log in Console - + Open Log Location Mở vị trí sổ ghi chép - + When checked, the max size of the log increases from 100 MB to 1 GB Khi tích vào, dung lượng tối đa cho file log chuyển từ 100 MB lên 1 GB - + Enable Extended Logging** - + Homebrew Homebrew - + Arguments String Xâu lệnh - + Graphics Đồ hoạ - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Kích hoạt chế độ gỡ lỗi đồ hoạ - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT Không dùng Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Vá lỗi - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Nâng Cao - + Kiosk (Quest) Mode - + Enable CPU Debugging Bật Vá Lỗi CPU - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Sẽ tự động thiết lập lại khi tắt yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1545,37 +1580,47 @@ This would ban both their forum username and their IP address. Tăng Tốc Thời Gian GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Bộ lọc góc nghiêng: - + Automatic - + Default Mặc định - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2067,7 +2112,7 @@ This would ban both their forum username and their IP address. - + Left Stick Cần trái @@ -2161,14 +2206,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2187,7 +2232,7 @@ This would ban both their forum username and their IP address. - + Plus Cộng @@ -2200,15 +2245,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2265,231 +2310,236 @@ This would ban both their forum username and their IP address. - + Right Stick Cần phải - - - - + + + + Clear Bỏ trống - - - - - + + + + + [not set] [không đặt] - - - Toggle button - - - - - + + Invert button - - + + + Toggle button + + + + + Invert axis - - - + + + Set threshold - - + + Choose a value between 0% and 100% Chọn một giá trị giữa 0% và 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Thiết lập Cần Điều Khiển - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Sau khi bấm OK, di chuyển cần sang ngang, rồi sau đó sang dọc. Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần sang dọc trước, rồi sang ngang. - + Center axis - - + + Deadzone: %1% - - + + Modifier Range: %1% - - + + Pro Controller Tay cầm Pro Controller - + Dual Joycons Joycon đôi - + Left Joycon Joycon Trái - + Right Joycon Joycon Phải - + Handheld Cầm tay - + GameCube Controller Tay cầm GameCube - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Bắt đầu / Tạm ngưng - + Z Z - + Control Stick - + C-Stick C-Stick - + Shake! Lắc! - + [waiting] [Chờ] - + New Profile Hồ sơ mới - + Enter a profile name: Nhập tên hồ sơ: - - + + Create Input Profile Tạo Hồ Sơ Phím - + The given profile name is not valid! Tên hồ sơ không hợp lệ! - + Failed to create the input profile "%1" Quá trình tạo hồ sơ phím "%1" thất bại - + Delete Input Profile Xóa Hồ Sơ Phím - + Failed to delete the input profile "%1" Quá trình xóa hồ sơ phím "%1" thất bại - + Load Input Profile Nạp Hồ Sơ Phím - + Failed to load the input profile "%1" Quá trình nạp hồ sơ phím "%1" thất bại - + Save Input Profile Lưu Hồ Sơ Phím - + Failed to save the input profile "%1" Quá trình lưu hồ sơ phím "%1" thất bại @@ -2744,42 +2794,42 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Nhà Phát Hành - + Add-Ons Bổ Sung - + General Chung - + System Hệ thống - + CPU CPU - + Graphics Đồ hoạ - + Adv. Graphics Đồ Họa Nâng Cao - + Audio Âm thanh - + Properties Thuộc tính @@ -3491,47 +3541,47 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Đường dẫn - + ... ... @@ -3940,7 +3990,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Xác nhận @@ -4027,7 +4077,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified @@ -4042,17 +4092,36 @@ Drag points to change position, or double-click table cells to edit values. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Xác nhận không thành công + + + Verification failed Xác nhận không thành công - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4121,12 +4190,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4134,908 +4203,888 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dữ liệu ẩn danh được thu thập</a>để hỗ trợ cải thiện yuzu. <br/><br/>Bạn có muốn chia sẽ dữ liệu sử dụng cho chúng tôi? - + Telemetry Viễn trắc - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... - + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Tốc độ giả lập hiện tại. Giá trị cao hơn hoặc thấp hơn 100% chỉ ra giả lập sẽ chạy nhanh hơn hoặc chậm hơn trên máy Switch - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Có bao nhiêu khung hình trên mỗi giây mà trò chơi đang hiển thị. Điều này sẽ thay đổi từ giữa các trò chơi và các khung cảnh khác nhau. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Thời gian mà giả lập lấy từ khung hình Switch, sẽ không kể đến giới hạn khung hình hoặc v-sync. Đối với tốc độ tối đa mà giả lập nhận được nhiều nhất là ở độ khoảng 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Tạm dừng - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Chú ý định dạng trò chơi đã lỗi thời - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Bạn đang sử dụng định dạng danh mục ROM giải mã cho trò chơi này, và đó là một định dạng lỗi thời đã được thay thế bởi những thứ khác như NCA, NAX, XCI, hoặc NSP. Danh mục ROM giải mã có thể thiếu các icon, metadata, và hỗ trợ cập nhật.<br><br>Để hiểu thêm về các định dạng khác nhau của Switch mà yuzu hỗ trợ, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>vui lòng kiểm tra trên wiki của chúng tôi</a>. Thông báo này sẽ không hiển thị lại lần sau. - - + + Error while loading ROM! Lỗi xảy ra khi nạp ROM! - + The ROM format is not supported. Định dạng ROM này không hỗ trợ. - + An error occurred initializing the video core. Đã xảy ra lỗi khi khởi tạo lõi đồ hoạ. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Đã xảy ra lỗi không xác định. Hãy kiểm tra phần báo cáo để biết thêm chi tiết. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data - + Mod Data - + Error Opening %1 Folder Xảy ra lỗi khi mở %1 thư mục - - + + Folder does not exist! Thư mục này không tồn tại! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - + Contents - + Update Cập nhật - + DLC - + Remove Entry - + Remove Installed Game %1? - - - - - - + + + + + + Successfully Removed - + Successfully removed the installed base game. - - - + + + Error Removing %1 - + The base game is not installed in the NAND and cannot be removed. - + Successfully removed the installed update. - + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! Khai thác RomFS không thành công! - + There was an error copying the RomFS files or the user cancelled the operation. Đã xảy ra lỗi khi sao chép tệp tin RomFS hoặc người dùng đã hủy bỏ hoạt động này. - + Full - + Skeleton Sườn - + Select RomFS Dump Mode Chọn chế độ kết xuất RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Vui lòng chọn cách mà bạn muốn RomFS kết xuất.<br>Chế độ Đầy Đủ sẽ sao chép toàn bộ tệp tin vào một danh mục mới trong khi <br>chế độ Sườn chỉ tạo kết cấu danh mục. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Khai thác RomFS... - - + + Cancel Hủy bỏ - + RomFS Extraction Succeeded! Khai thác RomFS thành công! - + The operation completed successfully. Các hoạt động đã hoàn tất thành công. - + Error Opening %1 - + Select Directory Chọn danh mục - + Properties Thuộc tính - + The game properties could not be loaded. Không thể tải thuộc tính của trò chơi. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Thực thi Switch (%1);;Tất cả tệp tin (*.*) - + Load File Nạp tệp tin - + Open Extracted ROM Directory Mở danh mục ROM đã trích xuất - + Invalid Directory Selected Danh mục đã chọn không hợp lệ - + The directory you have selected does not contain a 'main' file. Danh mục mà bạn đã chọn không có chứa tệp tin 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Những tệp tin Switch cài được (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Đang cài đặt tệp tin "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Ứng dụng hệ thống - + System Archive Hệ thống lưu trữ - + System Application Update Cập nhật hệ thống ứng dụng - + Firmware Package (Type A) Gói phần mềm hệ thống (Loại A) - + Firmware Package (Type B) Gói phần mềm (Loại B) - + Game Trò chơi - + Game Update Cập nhật trò chơi - + Game DLC Nội dung trò chơi có thể tải xuống - + Delta Title Tiêu đề Delta - + Select NCA Install Type... Chọn cách cài đặt NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vui lòng chọn loại tiêu đề mà bạn muốn cài đặt NCA này: (Trong hầu hết trường hợp, chọn mặc định 'Game' là tốt nhất.) - + Failed to Install Cài đặt đã không thành công - + The title type you selected for the NCA is invalid. Loại tiêu đề NCA mà bạn chọn nó không hợp lệ. - + File not found Không tìm thấy tệp tin - + File "%1" not found Không tìm thấy tệp tin "%1" - + OK OK - + Missing yuzu Account Thiếu tài khoản yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Để gửi trường hợp thử nghiệm trò chơi tương thích, bạn phải liên kết tài khoản yuzu.<br><br/>Để liên kết tải khoản yuzu của bạn, hãy đến Giả lập &gt; Thiết lập &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Tệp tin Amiibo (%1);; Tất cả tệp tin (*.*) - + Load Amiibo Nạp dữ liệu Amiibo - - Error opening Amiibo data file - Xảy ra lỗi khi mở dữ liệu tệp tin Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Không thể mở tệp tin Amiibo "%1" để đọc. - - - - Error reading Amiibo data file - Xảy ra lỗi khi đọc dữ liệu tệp tin Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Hoàn toàn không thể đọc được dữ liệu Amiibo. Dự kiến byte sẽ đọc là %1, nhưng byte chỉ có thể đọc là %2. - - - + Error loading Amiibo data Xảy ra lỗi khi nạp dữ liệu Amiibo - + Unable to load Amiibo data. Không thể nạp dữ liệu Amiibo. - + Capture Screenshot Chụp ảnh màn hình - + PNG Image (*.png) Hình ảnh PNG (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Bắt đầu - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Tốc độ: %1% / %2% - + Speed: %1% Tốc độ: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Trò chơi: %1 FPS - + Frame: %1 ms Khung hình: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Trò chơi bạn muốn chạy yêu cầu một số tệp tin được sao chép từ thiết từ máy Switch của bạn trước khi bắt đầu chơi.<br/><br/>Để biết thêm thông tin về cách sao chép những tệp tin đó, vui lòng tham khảo những wiki sau: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Sao chép dữ liệu hệ thống và font dùng chung từ máy Switch</a>.<br/><br/>Bạn có muốn trở về danh sách trò chơi? Nếu bạn vẫn tiếp tục thì trò chơi có thể gặp sự cố, dữ liệu lưu tiến trình có thể bị lỗi, hoặc bạn sẽ gặp những lỗi khác. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found Không tìm thấy tệp tin hệ thống - + System Archive Missing Bị thiếu tệp tin hệ thống - + yuzu was unable to locate the Switch shared fonts. %1 yuzu không thể tìm thấy vị trí font dùng chung của Switch. %1 - + Shared Fonts Not Found Không tìm thấy font dùng chung - + Shared Font Missing Bị thiếu font dùng chung - + Fatal Error Lỗi nghiêm trọng - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu đã xảy ra lỗi nghiêm trọng, vui lòng kiểm tra sổ ghi chép để biết thêm chi tiết. Để biết thêm thông tin về cách truy cập sổ ghi chép, vui lòng xem trang sau: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Làm sao để tải tệp tin sổ ghi chép lên</a>.<br/><br/>Bạn có muốn trở về danh sách game? Tiếp tục có thể khiến giả lập gặp sự cố, gây hỏng dữ liệu đã lưu, hoặc gây các lỗi khác. - + Fatal Error encountered - + Confirm Key Rederivation Xác nhận mã khóa Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5052,37 +5101,37 @@ và phải tạo ra một bản sao lưu lại. Điều này sẽ xóa mã khóa tự động tạo trên tệp tin của bạn và chạy lại mô-đun chiết xuất mã khoá. - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5091,39 +5140,39 @@ on your system's performance. hệ thống của bạn. - + Deriving Keys Mã khóa xuất phát - + Select RomFS Dump Target Chọn thư mục để sao chép RomFS - + Please select which RomFS you would like to dump. Vui lòng chọn RomFS mà bạn muốn chiết xuất. - + Are you sure you want to close yuzu? Bạn có chắc chắn muốn đóng yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Bạn có chắc rằng muốn dừng giả lập? Bất kì tiến trình nào chưa được lưu sẽ bị mất. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5502,12 +5551,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5516,11 +5565,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5542,112 +5592,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Chụp ảnh màn hình - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Toàn màn hình - + Load File Nạp tệp tin - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5761,42 +5810,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Người chơi - + Refreshing - + Refresh List @@ -6123,7 +6172,7 @@ Debug Message: - + Connected Đã kết nối @@ -6145,7 +6194,7 @@ Debug Message: - + New Messages Received @@ -6249,22 +6298,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6272,7 +6338,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6327,7 +6393,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6382,7 +6448,7 @@ p, li { white-space: pre-wrap; } - + [not set] [chưa đặt nút] @@ -6397,10 +6463,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Trục %1%2 @@ -6414,9 +6480,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [không xác định] @@ -6581,15 +6647,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6597,35 +6663,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [không sử dụng] @@ -6666,7 +6732,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/vi_VN.ts b/dist/languages/vi_VN.ts index 88c97201fc..a35d600045 100644 --- a/dist/languages/vi_VN.ts +++ b/dist/languages/vi_VN.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -733,200 +733,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Cho phép bật GDB sơ khai - + Port: Cổng: - + Logging Sổ ghi chép - + Global Log Filter Bộ lọc sổ ghi chép - + Show Log in Console - + Open Log Location Mở vị trí sổ ghi chép - + When checked, the max size of the log increases from 100 MB to 1 GB Khi tích vào, dung lượng tối đa cho file log chuyển từ 100 MB lên 1 GB - + Enable Extended Logging** - + Homebrew Homebrew - + Arguments String Chuỗi đối số - + Graphics Đồ hoạ - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Kích hoạt chế độ gỡ lỗi đồ hoạ - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT Không dùng Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Vá lỗi - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Nâng Cao - + Kiosk (Quest) Mode - + Enable CPU Debugging Bật Vá Lỗi CPU - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Sẽ tự động thiết lập lại khi tắt yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1545,37 +1580,47 @@ This would ban both their forum username and their IP address. Tăng Tốc Thời Gian GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Bộ lọc góc nghiêng: - + Automatic - + Default Mặc định - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2067,7 +2112,7 @@ This would ban both their forum username and their IP address. - + Left Stick Cần trái @@ -2161,14 +2206,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2187,7 +2232,7 @@ This would ban both their forum username and their IP address. - + Plus Cộng @@ -2200,15 +2245,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2265,231 +2310,236 @@ This would ban both their forum username and their IP address. - + Right Stick Cần phải - - - - + + + + Clear Bỏ trống - - - - - + + + + + [not set] [không đặt] - - - Toggle button - - - - - + + Invert button - - + + + Toggle button + + + + + Invert axis - - - + + + Set threshold - - + + Choose a value between 0% and 100% Chọn một giá trị giữa 0% và 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Thiết lập Cần Điều Khiển - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Sau khi bấm OK, di chuyển cần sang ngang, rồi sau đó sang dọc. Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần sang dọc trước, rồi sang ngang. - + Center axis - - + + Deadzone: %1% - - + + Modifier Range: %1% - - + + Pro Controller Tay cầm Pro Controller - + Dual Joycons Joycon đôi - + Left Joycon Joycon Trái - + Right Joycon Joycon Phải - + Handheld Cầm tay - + GameCube Controller Tay cầm GameCube - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Bắt đầu / Tạm ngưng - + Z Z - + Control Stick - + C-Stick C-Stick - + Shake! Lắc! - + [waiting] [Chờ] - + New Profile Hồ sơ mới - + Enter a profile name: Nhập tên hồ sơ: - - + + Create Input Profile Tạo Hồ Sơ Phím - + The given profile name is not valid! Tên hồ sơ không hợp lệ! - + Failed to create the input profile "%1" Quá trình tạo hồ sơ phím "%1" thất bại - + Delete Input Profile Xóa Hồ Sơ Phím - + Failed to delete the input profile "%1" Quá trình xóa hồ sơ phím "%1" thất bại - + Load Input Profile Nạp Hồ Sơ Phím - + Failed to load the input profile "%1" Quá trình nạp hồ sơ phím "%1" thất bại - + Save Input Profile Lưu Hồ Sơ Phím - + Failed to save the input profile "%1" Quá trình lưu hồ sơ phím "%1" thất bại @@ -2744,42 +2794,42 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Nhà Phát Hành - + Add-Ons Bổ Sung - + General Tổng Quan - + System Hệ Thống - + CPU CPU - + Graphics Đồ Họa - + Adv. Graphics Đồ Họa Nâng Cao - + Audio Âm Thanh - + Properties Thuộc tính @@ -3491,47 +3541,47 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Đường dẫn - + ... ... @@ -3940,7 +3990,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Xác nhận @@ -4027,7 +4077,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified @@ -4042,17 +4092,36 @@ Drag points to change position, or double-click table cells to edit values. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Xác nhận không thành công + + + Verification failed Xác nhận không thành công - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4121,12 +4190,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4134,908 +4203,888 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dữ liệu ẩn danh được thu thập</a>để hỗ trợ cải thiện yuzu. <br/><br/>Bạn có muốn chia sẽ dữ liệu sử dụng cho chúng tôi? - + Telemetry Viễn trắc - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... - + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Tốc độ giả lập hiện tại. Giá trị cao hơn hoặc thấp hơn 100% chỉ ra giả lập sẽ chạy nhanh hơn hoặc chậm hơn trên máy Switch - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Có bao nhiêu khung hình trên mỗi giây mà trò chơi đang hiển thị. Điều này sẽ thay đổi từ trò chơi này đến trò chơi kia và khung cảnh này đến khung cảnh kia. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Thời gian mà giả lập lấy từ khung hình Switch, sẽ không kể đến giới hạn khung hình hoặc v-sync. Đối với tốc độ tối đa mà giả lập nhận được nhiều nhất là ở độ khoảng 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Tạm dừng - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Chú ý định dạng trò chơi đã lỗi thời - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Bạn đang sử dụng định dạng danh mục ROM giải mã cho trò chơi này, và đó là một định dạng lỗi thời đã được thay thế bởi những thứ khác như NCA, NAX, XCI, hoặc NSP. Danh mục ROM giải mã có thể thiếu biểu tượng, metadata, và hỗ trợ cập nhật.<br><br>Để giải thích về các định dạng khác nhau của Switch mà yuzu hỗ trợ, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>vui lòng kiểm tra trên wiki của chúng tôi</a>. Thông báo này sẽ không hiển thị lại lần sau. - - + + Error while loading ROM! Xảy ra lỗi khi đang nạp ROM! - + The ROM format is not supported. Định dạng ROM này không hỗ trợ. - + An error occurred initializing the video core. Đã xảy ra lỗi khi khởi tạo lõi video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Đã xảy ra lỗi không xác định. Vui lòng kiểm tra sổ ghi chép để biết thêm chi tiết. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data - + Mod Data - + Error Opening %1 Folder Xảy ra lỗi khi mở %1 thư mục - - + + Folder does not exist! Thư mục này không tồn tại! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - + Contents - + Update Cập nhật - + DLC - + Remove Entry - + Remove Installed Game %1? - - - - - - + + + + + + Successfully Removed - + Successfully removed the installed base game. - - - + + + Error Removing %1 - + The base game is not installed in the NAND and cannot be removed. - + Successfully removed the installed update. - + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! Khai thác RomFS không thành công! - + There was an error copying the RomFS files or the user cancelled the operation. Đã xảy ra lỗi khi sao chép tệp tin RomFS hoặc người dùng đã hủy bỏ hoạt động này. - + Full - + Skeleton Sườn - + Select RomFS Dump Mode Chọn chế độ kết xuất RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Vui lòng chọn RomFS mà bạn muốn kết xuất như thế nào.<br>Đầy đủ sẽ sao chép toàn bộ tệp tin vào một danh mục mới trong khi <br>bộ xương chỉ tạo kết cấu danh mục. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Khai thác RomFS... - - + + Cancel Hủy bỏ - + RomFS Extraction Succeeded! Khai thác RomFS thành công! - + The operation completed successfully. Các hoạt động đã hoàn tất thành công. - + Error Opening %1 - + Select Directory Chọn danh mục - + Properties Thuộc tính - + The game properties could not be loaded. Thuộc tính của trò chơi không thể nạp được. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Thực thi Switch (%1);;Tất cả tệp tin (*.*) - + Load File Nạp tệp tin - + Open Extracted ROM Directory Mở danh mục ROM đã trích xuất - + Invalid Directory Selected Danh mục đã chọn không hợp lệ - + The directory you have selected does not contain a 'main' file. Danh mục mà bạn đã chọn không có chứa tệp tin 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Những tệp tin Switch cài được (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Đang cài đặt tệp tin "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Hệ thống ứng dụng - + System Archive Hệ thống lưu trữ - + System Application Update Cập nhật hệ thống ứng dụng - + Firmware Package (Type A) Gói phần mềm (Loại A) - + Firmware Package (Type B) Gói phần mềm (Loại B) - + Game Trò chơi - + Game Update Cập nhật trò chơi - + Game DLC Nội dung trò chơi có thể tải xuống - + Delta Title Tiêu đề Delta - + Select NCA Install Type... Chọn loại NCA để cài đặt... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vui lòng chọn loại tiêu đề mà bạn muốn cài đặt NCA này: (Trong hầu hết trường hợp, chọn mặc định 'Game' là tốt nhất.) - + Failed to Install Cài đặt đã không thành công - + The title type you selected for the NCA is invalid. Loại tiêu đề NCA mà bạn chọn nó không hợp lệ. - + File not found Không tìm thấy tệp tin - + File "%1" not found Không tìm thấy "%1" tệp tin - + OK OK - + Missing yuzu Account Thiếu tài khoản yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Để gửi trường hợp thử nghiệm trò chơi tương thích, bạn phải liên kết tài khoản yuzu.<br><br/>Để liên kết tải khoản yuzu của bạn, hãy đến Giả lập &gt; Thiết lập &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Error - - + + The current game is not looking for amiibos - - + + Amiibo - - + + The current amiibo has been removed - + Amiibo File (%1);; All Files (*.*) Tệp tin Amiibo (%1);; Tất cả tệp tin (*.*) - + Load Amiibo Nạp dữ liệu Amiibo - - Error opening Amiibo data file - Xảy ra lỗi khi mở dữ liệu tệp tin Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Không thể mở tệp tin Amiibo "%1" để đọc. - - - - Error reading Amiibo data file - Xảy ra lỗi khi đọc dữ liệu tệp tin Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Hoàn toàn không thể đọc được dữ liệu Amiibo. Dự kiến byte sẽ đọc là %1, nhưng byte chỉ có thể đọc là %2. - - - + Error loading Amiibo data Xảy ra lỗi khi nạp dữ liệu Amiibo - + Unable to load Amiibo data. Không thể nạp dữ liệu Amiibo. - + Capture Screenshot Chụp ảnh màn hình - + PNG Image (*.png) Hình ảnh PNG (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Start &Bắt đầu - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Tốc độ: %1% / %2% - + Speed: %1% Tốc độ: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Trò chơi: %1 FPS - + Frame: %1 ms Khung hình: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Trò chơi bạn muốn chạy yêu cầu một số tệp tin được sao chép từ thiết từ máy Switch của bạn trước khi bắt đầu chơi.<br/><br/>Để biết thêm thông tin về cách sao chép những tệp tin đó, vui lòng tham khảo những wiki sau: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Sao chép dữ liệu hệ thống và font dùng chung từ máy Switch</a>.<br/><br/>Bạn có muốn trở về danh sách trò chơi? Nếu bạn vẫn tiếp tục thì trò chơi có thể gặp sự cố, dữ liệu lưu tiến trình có thể bị lỗi, hoặc bạn sẽ gặp những lỗi khác. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found Không tìm thấy tệp tin hệ thống - + System Archive Missing Bị thiếu tệp tin hệ thống - + yuzu was unable to locate the Switch shared fonts. %1 yuzu không thể tìm thấy vị trí font dùng chung của Switch. %1 - + Shared Fonts Not Found Không tìm thấy font dùng chung - + Shared Font Missing Bị thiếu font dùng chung - + Fatal Error Lỗi nghiêm trọng - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu đã xảy ra lỗi nghiêm trọng, vui lòng kiểm tra sổ ghi chép để biết thêm chi tiết. Để biết thêm thông tin về cách truy cập sổ ghi chép, vui lòng xem trang sau: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Làm sao để tải tệp tin sổ ghi chép lên</a>.<br/><br/>Bạn có muốn trở về danh sách game? Tiếp tục có thể khiến giả lập gặp sự cố, gây hỏng dữ liệu đã lưu, hoặc gây các lỗi khác. - + Fatal Error encountered - + Confirm Key Rederivation Xác nhận mã khóa Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5052,37 +5101,37 @@ và phải tạo ra một bản sao lưu lại. Điều này sẽ xóa mã khóa tự động tạo trên tệp tin của bạn và chạy lại mô-đun mã khóa derivation. - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5091,39 +5140,39 @@ on your system's performance. vào hiệu suất hệ thống của bạn. - + Deriving Keys Mã khóa xuất phát - + Select RomFS Dump Target Chọn thư mục để sao chép RomFS - + Please select which RomFS you would like to dump. Vui lòng chọn RomFS mà bạn muốn sao chép. - + Are you sure you want to close yuzu? Bạn có chắc chắn muốn đóng yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Bạn có chắc rằng muốn dừng giả lập? Bất kì tiến trình nào chưa được lưu sẽ bị mất. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5500,12 +5549,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5514,11 +5563,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5540,112 +5590,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Chụp ảnh màn hình - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Toàn màn hình - + Load File Nạp tệp tin - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5759,42 +5808,42 @@ Debug Message: - + Password Required to Join - + Password: - + Room Name - + Preferred Game - + Host - + Players Người chơi - + Refreshing - + Refresh List @@ -6121,7 +6170,7 @@ Debug Message: - + Connected Đã kết nối @@ -6143,7 +6192,7 @@ Debug Message: - + New Messages Received @@ -6247,22 +6296,39 @@ They may have left the room. - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6270,7 +6336,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6325,7 +6391,7 @@ p, li { white-space: pre-wrap; } - + Not playing a game @@ -6380,7 +6446,7 @@ p, li { white-space: pre-wrap; } - + [not set] [chưa đặt nút] @@ -6395,10 +6461,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Trục %1%2 @@ -6412,9 +6478,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [không xác định] @@ -6579,15 +6645,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6595,35 +6661,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [không sử dụng] @@ -6664,7 +6730,7 @@ p, li { white-space: pre-wrap; } - + %1%2%3 diff --git a/dist/languages/zh_CN.ts b/dist/languages/zh_CN.ts index a70052b5cf..f8137e297e 100644 --- a/dist/languages/zh_CN.ts +++ b/dist/languages/zh_CN.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } 发送消息 - + Members 成员 - + %1 has joined %1 已加入 - + %1 has left %1 已离开 - + %1 has been kicked %1 已被踢出房间 - + %1 has been banned %1 已被封禁 - + %1 has been unbanned %1 已被解封 - + View Profile 查看个人资料 - - + + Block Player 屏蔽玩家 - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - 当你屏蔽玩家后,你将无法收到他们的聊天消息。<br><br>您确定要屏蔽 %1 吗? + 屏蔽玩家后,你将无法收到他们的聊天消息。<br><br>您确定要屏蔽 %1 吗? - + Kick 踢出房间 - + Ban 封禁 - + Kick Player 踢出玩家 - + Are you sure you would like to <b>kick</b> %1? 您确定要将 %1 <b>踢出房间</b>吗? - + Ban Player 封禁玩家 - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -768,200 +768,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger 调试器 - + Enable GDB Stub 开启 GDB 调试 - + Port: 端口: - + Logging 日志 - + Global Log Filter 全局日志过滤器 - + Show Log in Console 显示日志窗口 - + Open Log Location 打开日志位置 - + When checked, the max size of the log increases from 100 MB to 1 GB 选中此项后,日志文件的最大大小从 100MB 增加到 1GB - + Enable Extended Logging** 启用扩展的日志记录** - + Homebrew 自制游戏 - + Arguments String 参数字符串 - + Graphics 图形 - + When checked, the graphics API enters a slower debugging mode 启用时,图形 API 将进入较慢的调试模式。 - + Enable Graphics Debugging 启用图形调试 - + When checked, it enables Nsight Aftermath crash dumps 启用后,yuzu 将会保存 Nsight Aftermath 格式的崩溃转储文件 - + Enable Nsight Aftermath 启用 Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found 启用时,将从磁盘着色器缓存或游戏中转储所有的着色器文件。 - + Dump Game Shaders 转储着色器文件 - + When checked, it will dump all the macro programs of the GPU 选中后,将转储 GPU 的所有宏程序 - + Dump Maxwell Macros 转储 Maxwell 宏 - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower 启用时,将禁用宏即时编译器。这会降低游戏运行速度。 - + Disable Macro JIT 禁用宏 JIT - + When checked, yuzu will log statistics about the compiled pipeline cache 选中时,yuzu 将记录有关已编译着色器缓存的统计信息。 - + Enable Shader Feedback 启用着色器反馈 - + When checked, it executes shaders without loop logic changes 启用后,yuzu 在执行着色器时,不会修改循环结构的条件判断 - + Disable Loop safety checks 禁用循环体安全检查 - + Debugging 调试选项 - - Enable FS Access Log - 启用文件系统访问记录 - - - - Dump Audio Commands To Console** - 将音频命令转储至控制台** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 - - - + Enable Verbose Reporting Services** 启用详细报告服务** - + + Enable FS Access Log + 启用文件系统访问记录 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 + + + + Dump Audio Commands To Console** + 将音频命令转储至控制台** + + + + Create Minidump After Crash + 微型故障转储 + + + Advanced 高级选项 - + Kiosk (Quest) Mode Kiosk (Quest) 模式 - + Enable CPU Debugging 启用 CPU 模拟调试 - + Enable Debug Asserts 启用调试 - + Enable Auto-Stub** 启用自动函数打桩(Auto-Stub)** - + Enable All Controller Types 启用其他类型的控制器 - + Disable Web Applet 禁用 Web 应用程序 - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + 允许 yuzu 在启动时检查 Vulkan 环境是否正常工作。如果是其他程序导致 yuzu 出现此问题,请禁用此选项。 + + + + Perform Startup Vulkan Check + 启动时进行 Vulkan 检测 + + + **This will be reset automatically when yuzu closes. **该选项将在 yuzu 关闭时自动重置。 + + + Restart Required + 需要重启 + + + + yuzu is required to restart in order to apply this setting. + 重启 yuzu 后才能应用此设置。 + + + + Web applet not compiled + Web 应用程序未编译 + + + + MiniDump creation not compiled + 微型转储未编译 + ConfigureDebugController @@ -1580,37 +1615,47 @@ This would ban both their forum username and their IP address. 启用快速 GPU 时钟 (不稳定) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 启用悲观缓冲区刷新。此选项将强制刷新未修改的缓冲区,可能会降低性能。 + + + + Use pessimistic buffer flushes (Hack) + 启用悲观缓冲区刷新 (不稳定) + + + Anisotropic Filtering: 各向异性过滤: - + Automatic 自动 - + Default 系统默认 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2102,7 +2147,7 @@ This would ban both their forum username and their IP address. - + Left Stick 左摇杆 @@ -2196,14 +2241,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2222,7 +2267,7 @@ This would ban both their forum username and their IP address. - + Plus @@ -2235,15 +2280,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2300,231 +2345,236 @@ This would ban both their forum username and their IP address. - + Right Stick 右摇杆 - - - - + + + + Clear 清除 - - - - - + + + + + [not set] [未设置] - - - Toggle button - 切换按键 - - - - + + Invert button 反转按钮 - - + + + Toggle button + 切换按键 + + + + Invert axis 体感方向倒置 - - - + + + Set threshold 阈值设定 - - + + Choose a value between 0% and 100% 选择一个介于 0% 和 100% 之间的值 - + + Toggle axis + 切换轴 + + + Set gyro threshold 陀螺仪阈值设定 - + Map Analog Stick 映射摇杆 - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. 在按下确定后,首先水平移动你的手柄,然后垂直移动它。 如果要使体感方向倒置,首先垂直移动你的手柄,然后水平移动它。 - + Center axis 中心轴 - - + + Deadzone: %1% 摇杆死区:%1% - - + + Modifier Range: %1% 摇杆灵敏度:%1% - - + + Pro Controller Pro Controller - + Dual Joycons 双 Joycons 手柄 - + Left Joycon 左 Joycon 手柄 - + Right Joycon 右 Joycon 手柄 - + Handheld 掌机模式 - + GameCube Controller GameCube 控制器 - + Poke Ball Plus 精灵球 PLUS - + NES Controller NES 控制器 - + SNES Controller SNES 控制器 - + N64 Controller N64 控制器 - + Sega Genesis 世嘉创世纪 - + Start / Pause 开始 / 暂停 - + Z Z - + Control Stick 控制摇杆 - + C-Stick C 摇杆 - + Shake! 摇动! - + [waiting] [等待中] - + New Profile 保存自定义设置 - + Enter a profile name: 输入配置文件名称: - - + + Create Input Profile 新建输入配置文件 - + The given profile name is not valid! 输入的配置文件名称无效! - + Failed to create the input profile "%1" 新建输入配置文件 "%1" 失败 - + Delete Input Profile 删除输入配置文件 - + Failed to delete the input profile "%1" 删除输入配置文件 "%1" 失败 - + Load Input Profile 加载输入配置文件 - + Failed to load the input profile "%1" 加载输入配置文件 "%1" 失败 - + Save Input Profile 保存输入配置文件 - + Failed to save the input profile "%1" 保存输入配置文件 "%1" 失败 @@ -2779,42 +2829,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 开发商 - + Add-Ons 附加项 - + General 通用 - + System 系统 - + CPU CPU - + Graphics 图形 - + Adv. Graphics 高级图形 - + Audio 声音 - + Properties 属性 @@ -3526,47 +3576,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>通过读取与 TAS-nx 脚本具有相同格式的脚本来读取控制器的输入。<br/>有关详细信息,请参阅 yuzu 官方网站的<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">帮助页面</span></a>。</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). 要确认是哪些热键控制播放/录制,请参阅热键设置。(设置—>通用—>热键) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 警告:这是一个实验性功能。<br/>由于暂不完善的同步方式,可能无法完整地进行回放。 - + Settings 设置 - + Enable TAS features 启用 TAS 功能 - + Loop script 循环脚本 - + Pause execution during loads 遇到载入画面时暂停执行 - + Script Directory 脚本目录 - + Path 路径 - + ... ... @@ -3976,7 +4026,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 验证 @@ -4003,7 +4053,7 @@ Drag points to change position, or double-click table cells to edit values. Web Service configuration can only be changed when a public room isn't being hosted. - 公共房间未被开放时,才能更改网络服务设置。 + 公共房间未被创建时,才能更改网络服务设置。 @@ -4063,7 +4113,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 未指定 @@ -4078,17 +4128,36 @@ Drag points to change position, or double-click table cells to edit values.令牌未被验证。您对用户名和令牌的更改尚未保存。 - + + Unverified, please click Verify before saving configuration + Tooltip + 令牌未验证,请在保存配置前先进行验证。 + + + + Verifying... 验证中... - + + Verified + Tooltip + 已验证 + + + + Verification failed + Tooltip + 验证失败 + + + Verification failed 验证失败 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 验证失败。请检查您输入的令牌是否正确,并且确保您的互联网连接正常。 @@ -4157,12 +4226,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting 连接中 - + Connect 连接 @@ -4170,913 +4239,893 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>我们收集匿名数据</a>来帮助改进 yuzu 。<br/><br/>您愿意和我们分享您的使用数据吗? - + Telemetry 使用数据共享 - + Broken Vulkan Installation Detected 检测到 Vulkan 的安装已损坏 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. Vulkan 初始化失败。<br><br>点击<a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>这里</a>获取此问题的相关信息。 - + Loading Web Applet... 正在加载 Web 应用程序... - + Disable Web Applet 禁用 Web 应用程序 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 禁用 Web 应用程序可能会发生未知的行为,且只能在《超级马里奥 3D 全明星》中使用。您确定要禁用 Web 应用程序吗? (您可以在调试选项中重新启用它。) - + The amount of shaders currently being built 当前正在构建的着色器数量 - + The current selected resolution scaling multiplier. 当前选定的分辨率缩放比例。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 当前的模拟速度。高于或低于 100% 的值表示模拟正在运行得比实际 Switch 更快或更慢。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 游戏当前运行的帧率。这将因游戏和场景的不同而有所变化。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 在不计算速度限制和垂直同步的情况下,模拟一个 Switch 帧的实际时间。若要进行全速模拟,这个数值不应超过 16.67 毫秒。 - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files 清除最近文件 (&C) - + &Continue 继续 (&C) - + &Pause 暂停 (&P) - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu 正在运行中 - + Warning Outdated Game Format 过时游戏格式警告 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. 目前使用的游戏为解体的 ROM 目录格式,这是一种过时的格式,已被其他格式替代,如 NCA,NAX,XCI 或 NSP。解体的 ROM 目录缺少图标、元数据和更新支持。<br><br>有关 yuzu 支持的各种 Switch 格式的说明,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>请查看我们的 wiki</a>。此消息将不会再次出现。 - - + + Error while loading ROM! 加载 ROM 时出错! - + The ROM format is not supported. 该 ROM 格式不受支持。 - + An error occurred initializing the video core. 在初始化视频核心时发生错误。 - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu 在运行视频核心时发生错误。这可能是由 GPU 驱动程序过旧造成的。有关详细信息,请参阅日志文件。关于日志文件的更多信息,请参考以下页面:<a href='https://yuzu-emu.org/help/reference/log-files/'>如何上传日志文件</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. 加载 ROM 时出错! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>请参考<a href='https://yuzu-emu.org/help/quickstart/'>yuzu 快速导航</a>以获取相关文件。<br>您可以参考 yuzu 的 wiki 页面</a>或 Discord 社区</a>以获得帮助。 - + An unknown error occurred. Please see the log for more details. 发生了未知错误。请查看日志了解详情。 - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data 保存数据 - + Mod Data Mod 数据 - + Error Opening %1 Folder 打开 %1 文件夹时出错 - - + + Folder does not exist! 文件夹不存在! - + Error Opening Transferable Shader Cache 打开可转移着色器缓存时出错 - + Failed to create the shader cache directory for this title. 为该游戏创建着色器缓存目录时失败。 - + Contents 目录 - + Update 游戏更新 - + DLC DLC - + Remove Entry 删除项目 - + Remove Installed Game %1? 删除已安装的游戏 %1 ? - - - - - - + + + + + + Successfully Removed 删除成功 - + Successfully removed the installed base game. 成功删除已安装的游戏。 - - - + + + Error Removing %1 删除 %1 时出错 - + The base game is not installed in the NAND and cannot be removed. 该游戏未安装于 NAND 中,无法删除。 - + Successfully removed the installed update. 成功删除已安装的游戏更新。 - + There is no update installed for this title. 这个游戏没有任何已安装的更新。 - + There are no DLC installed for this title. 这个游戏没有任何已安装的 DLC 。 - + Successfully removed %1 installed DLC. 成功删除游戏 %1 安装的 DLC 。 - + Delete OpenGL Transferable Shader Cache? 删除 OpenGL 模式的着色器缓存? - + Delete Vulkan Transferable Shader Cache? 删除 Vulkan 模式的着色器缓存? - + Delete All Transferable Shader Caches? 删除所有的着色器缓存? - + Remove Custom Game Configuration? 移除自定义游戏设置? - + Remove File 删除文件 - - + + Error Removing Transferable Shader Cache 删除着色器缓存时出错 - - + + A shader cache for this title does not exist. 这个游戏的着色器缓存不存在。 - + Successfully removed the transferable shader cache. 成功删除着色器缓存。 - + Failed to remove the transferable shader cache. 删除着色器缓存失败。 - - + + Error Removing Transferable Shader Caches 删除着色器缓存时出错 - + Successfully removed the transferable shader caches. 着色器缓存删除成功。 - + Failed to remove the transferable shader cache directory. 删除着色器缓存目录失败。 - - + + Error Removing Custom Configuration 移除自定义游戏设置时出错 - + A custom configuration for this title does not exist. 这个游戏的自定义设置不存在。 - + Successfully removed the custom game configuration. 成功移除自定义游戏设置。 - + Failed to remove the custom game configuration. 移除自定义游戏设置失败。 - - + + RomFS Extraction Failed! RomFS 提取失败! - + There was an error copying the RomFS files or the user cancelled the operation. 复制 RomFS 文件时出错,或用户取消了操作。 - + Full 完整 - + Skeleton 框架 - + Select RomFS Dump Mode 选择 RomFS 转储模式 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. 请选择希望 RomFS 转储的方式。<br>“Full” 会将所有文件复制到新目录中,而<br>“Skeleton” 只会创建目录结构。 - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 没有足够的空间用于提取 RomFS。请保持足够的空间或于模拟—>设置—>系统—>文件系统—>转储根目录中选择一个其他目录。 - + Extracting RomFS... 正在提取 RomFS... - - + + Cancel 取消 - + RomFS Extraction Succeeded! RomFS 提取成功! - + The operation completed successfully. 操作成功完成。 - + Error Opening %1 打开 %1 时出错 - + Select Directory 选择目录 - + Properties 属性 - + The game properties could not be loaded. 无法加载该游戏的属性信息。 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 可执行文件 (%1);;所有文件 (*.*) - + Load File 加载文件 - + Open Extracted ROM Directory 打开提取的 ROM 目录 - + Invalid Directory Selected 选择的目录无效 - + The directory you have selected does not contain a 'main' file. 选择的目录不包含 “main” 文件。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 可安装的 Switch 文件 (*.nca *.nsp *.xci);;任天堂内容档案 (*.nca);;任天堂应用包 (*.nsp);;NX 卡带镜像 (*.xci) - + Install Files 安装文件 - + %n file(s) remaining 剩余 %n 个文件 - + Installing file "%1"... 正在安装文件 "%1"... - - + + Install Results 安装结果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 为了避免可能存在的冲突,我们不建议将游戏本体安装到 NAND 中。 此功能仅用于安装游戏更新和 DLC 。 - + %n file(s) were newly installed 最近安装了 %n 个文件 - + %n file(s) were overwritten %n 个文件被覆盖 - + %n file(s) failed to install %n 个文件安装失败 - + System Application 系统应用 - + System Archive 系统档案 - + System Application Update 系统应用更新 - + Firmware Package (Type A) 固件包 (A型) - + Firmware Package (Type B) 固件包 (B型) - + Game 游戏 - + Game Update 游戏更新 - + Game DLC 游戏 DLC - + Delta Title 差量程序 - + Select NCA Install Type... 选择 NCA 安装类型... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 请选择此 NCA 的程序类型: (在大多数情况下,选择默认的“游戏”即可。) - + Failed to Install 安装失败 - + The title type you selected for the NCA is invalid. 选择的 NCA 程序类型无效。 - + File not found 找不到文件 - + File "%1" not found 文件 "%1" 未找到 - + OK 确定 - + Missing yuzu Account 未设置 yuzu 账户 - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. 要提交游戏兼容性测试用例,您必须设置您的 yuzu 帐户。<br><br/>要设置您的 yuzu 帐户,请转到模拟 &gt; 设置 &gt; 网络。 - + Error opening URL 打开 URL 时出错 - + Unable to open the URL "%1". 无法打开 URL : "%1" 。 - + TAS Recording TAS 录制中 - + Overwrite file of player 1? 覆盖玩家 1 的文件? - + Invalid config detected 检测到无效配置 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 掌机手柄无法在主机模式中使用。将会选择 Pro controller。 - - + + Error 错误 - - + + The current game is not looking for amiibos 当前游戏并没有在寻找 Amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed 当前的 Amiibo 已被移除。 - + Amiibo File (%1);; All Files (*.*) Amiibo 文件 (%1);; 全部文件 (*.*) - + Load Amiibo 加载 Amiibo - - Error opening Amiibo data file - 打开 Amiibo 数据文件时出错 - - - - Unable to open Amiibo file "%1" for reading. - 无法打开 Amiibo 文件 %1。 - - - - Error reading Amiibo data file - 读取 Amiibo 数据文件时出错 - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - 无法完全读取 Amiibo 数据。应读取 %1 个字节,但实际仅能读取 %2 个字节。 - - - + Error loading Amiibo data 加载 Amiibo 数据时出错 - + Unable to load Amiibo data. 无法加载 Amiibo 数据。 - + Capture Screenshot 捕获截图 - + PNG Image (*.png) PNG 图像 (*.png) - + TAS state: Running %1/%2 TAS 状态:正在运行 %1/%2 - + TAS state: Recording %1 TAS 状态:正在录制 %1 - + TAS state: Idle %1/%2 TAS 状态:空闲 %1/%2 - + TAS State: Invalid TAS 状态:无效 - + &Stop Running 停止运行 (&S) - + &Start 开始 (&S) - + Stop R&ecording 停止录制 (&E) - + R&ecord 录制 (&E) - + Building: %n shader(s) 正在编译 %n 个着色器文件 - + Scale: %1x %1 is the resolution scaling factor 缩放比例: %1x - + Speed: %1% / %2% 速度: %1% / %2% - + Speed: %1% 速度: %1% - + Game: %1 FPS (Unlocked) 游戏: %1 FPS (未锁定) - + Game: %1 FPS FPS: %1 - + Frame: %1 ms 帧延迟:%1 毫秒 - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HIGH - + GPU EXTREME GPU EXTREME - + GPU ERROR GPU ERROR - + DOCKED 主机模式 - + HANDHELD 掌机模式 - + NEAREST 邻近取样 - - + + BILINEAR 双线性过滤 - + BICUBIC 双三线过滤 - + GAUSSIAN 高斯模糊 - + SCALEFORCE 强制缩放 - + FSR FSR - - + + NO AA 抗锯齿关 - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 您正尝试启动的游戏需要从 Switch 转储的其他文件。<br/><br/>有关转储这些文件的更多信息,请参阅以下 wiki 页面:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>。<br/><br/>您要退出并返回至游戏列表吗?继续模拟可能会导致崩溃,存档损坏或其他错误。 - + yuzu was unable to locate a Switch system archive. %1 Yuzu 找不到 Switch 系统档案 %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Yuzu 找不到 Switch 系统档案: %1, %2 - + System Archive Not Found 未找到系统档案 - + System Archive Missing 系统档案缺失 - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu 找不到 Swtich 共享字体 %1 - + Shared Fonts Not Found 未找到共享字体 - + Shared Font Missing 共享字体文件缺失 - + Fatal Error 致命错误 - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu 遇到了致命错误,请查看日志了解详情。有关查找日志的更多信息,请参阅以下页面:<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>。<br/><br/>您要退出并返回至游戏列表吗?继续模拟可能会导致崩溃,存档损坏或其他错误。 - + Fatal Error encountered 发生致命错误 - + Confirm Key Rederivation 确认重新生成密钥 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5092,37 +5141,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 这将删除您自动生成的密钥文件并重新运行密钥生成模块。 - + Missing fuses 项目丢失 - + - Missing BOOT0 - 丢失 BOOT0 - + - Missing BCPKG2-1-Normal-Main - 丢失 BCPKG2-1-Normal-Main - + - Missing PRODINFO - 丢失 PRODINFO - + Derivation Components Missing 组件丢失 - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 密钥缺失。<br>请查看<a href='https://yuzu-emu.org/help/quickstart/'>yuzu 快速导航</a>以获得你的密钥、固件和游戏。<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5131,39 +5180,39 @@ on your system's performance. 您的系统性能。 - + Deriving Keys 生成密钥 - + Select RomFS Dump Target 选择 RomFS 转储目标 - + Please select which RomFS you would like to dump. 请选择希望转储的 RomFS。 - + Are you sure you want to close yuzu? 您确定要关闭 yuzu 吗? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 您确定要停止模拟吗?未保存的进度将会丢失。 - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5534,32 +5583,33 @@ Screen. Host Room - 管理房间 + 创建房间 HostRoomWindow - + Error 错误 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - 向公共大厅公开房间时失败。为了管理公开房间,您必须在模拟 -> 设置 -> 网络中配置有效的 yuzu 帐户。如果不想在公共大厅中公开房间,请选择“未列出”。 + 向公共大厅公开房间时失败。为了创建公开房间,您必须在模拟 -> 设置 -> 网络中配置有效的 yuzu 帐户。如果不想在公共大厅中公开房间,请选择“未列出”。 调试消息: Hotkeys - + Audio Mute/Unmute 开启/关闭静音 + @@ -5581,112 +5631,111 @@ Debug Message: - Main Window 主窗口 - + Audio Volume Down 调低音量 - + Audio Volume Up 调高音量 - + Capture Screenshot 捕获截图 - + Change Adapting Filter 更改窗口滤镜 - + Change Docked Mode 更改主机运行模式 - + Change GPU Accuracy 更改 GPU 精度 - + Continue/Pause Emulation 继续/暂停模拟 - + Exit Fullscreen 退出全屏 - + Exit yuzu 退出 yuzu - + Fullscreen 全屏 - + Load File 加载文件 - + Load/Remove Amiibo 加载/移除 Amiibo - + Restart Emulation 重新启动模拟 - + Stop Emulation 停止模拟 - + TAS Record TAS 录制 - + TAS Reset 重置 TAS - + TAS Start/Stop TAS 开始/停止 - + Toggle Filter Bar 切换搜索栏 - + Toggle Framerate Limit 切换帧率限制 - + Toggle Mouse Panning 切换鼠标平移 - + Toggle Status Bar 切换状态栏 @@ -5788,7 +5837,7 @@ Debug Message: Games I Own - 游戏 I 我的 + 我拥有的游戏 @@ -5801,42 +5850,42 @@ Debug Message: 刷新游戏大厅 - + Password Required to Join 加入此房间需要密码 - + Password: 密码: - + Room Name 房间名称 - + Preferred Game 首选游戏 - + Host - 管理 + 房主 - + Players - 玩家 + 玩家数 - + Refreshing 刷新中 - + Refresh List 刷新列表 @@ -6163,7 +6212,7 @@ Debug Message: - + Connected 已连接 @@ -6186,7 +6235,7 @@ Debug Message: 调试信息: - + New Messages Received 收到了新消息 @@ -6291,22 +6340,41 @@ They may have left the room. 他们可能已经离开了房间。 - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + 未选择网络接口。 +请于设置 -> 系统 -> 网络中进行相关设置。 + + + + Game already running + 游戏正在运行中 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + 不推荐游戏正在运行时加入房间,这可能会导致房间的某些功能出现异常。 +是否继续? + + + Leave Room 离开房间 - + You are about to close the room. Any network connections will be closed. 您正要关闭房间。所有的网络连接都将关闭。 - + Disconnect 断开连接 - + You are about to leave the room. Any network connections will be closed. 您正要离开房间。所有的网络连接都将关闭。 @@ -6314,7 +6382,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error 错误 @@ -6373,7 +6441,7 @@ p, li { white-space: pre-wrap; } %1 正在玩 %2 - + Not playing a game 不在玩游戏 @@ -6428,7 +6496,7 @@ p, li { white-space: pre-wrap; } - + [not set] [未设置] @@ -6443,10 +6511,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 轴 %1%2 @@ -6460,9 +6528,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [未知] @@ -6627,15 +6695,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [无效] - - + + %1%2Hat %3 %1%2Hat 控制器 %3 @@ -6643,35 +6711,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2轴 %3 - + %1%2Axis %3,%4,%5 %1%2轴 %3,%4,%5 - + %1%2Motion %3 %1%2体感 %3 - - + + %1%2Button %3 %1%2按键 %3 - + [unused] [未使用] @@ -6712,7 +6780,7 @@ p, li { white-space: pre-wrap; } 额外按键 - + %1%2%3 %1%2%3 diff --git a/dist/languages/zh_TW.ts b/dist/languages/zh_TW.ts index ed797e876c..0e976d03db 100644 --- a/dist/languages/zh_TW.ts +++ b/dist/languages/zh_TW.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } 发送消息 - + Members 成员 - + %1 has joined %1 已加入 - + %1 has left %1 已离开 - + %1 has been kicked %1 已被踢出房间 - + %1 has been banned %1 已被封禁 - + %1 has been unbanned %1 已被解封 - + View Profile 查看个人资料 - - + + Block Player 屏蔽玩家 - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? 当你屏蔽玩家后,你将无法收到他们的聊天消息。<br><br>您确定要屏蔽 %1 吗? - + Kick 踢出房间 - + Ban 封禁 - + Kick Player 踢出玩家 - + Are you sure you would like to <b>kick</b> %1? 您确定要将 %1 <b>踢出房间</b>吗? - + Ban Player 封禁玩家 - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -770,200 +770,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger 调试器 - + Enable GDB Stub 开启 GDB 调试 - + Port: 通訊埠: - + Logging 紀錄 - + Global Log Filter 全域紀錄篩選器 - + Show Log in Console 在終端機中顯示紀錄 - + Open Log Location 開啟紀錄位置 - + When checked, the max size of the log increases from 100 MB to 1 GB 啟用後紀錄檔案大小上限從 100MB 增加到 1GB - + Enable Extended Logging** 啟用延伸紀錄** - + Homebrew Homebrew - + Arguments String 參數字串 - + Graphics 圖形 - + When checked, the graphics API enters a slower debugging mode 啟用時圖形 API 會進入較慢的偵錯模式。 - + Enable Graphics Debugging 啟用圖形偵錯 - + When checked, it enables Nsight Aftermath crash dumps 啟用時 yuzu 將會儲存 Nsight Aftermath 格式的錯誤傾印檔案。 - + Enable Nsight Aftermath 啟用 Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found 啟用時,將從磁碟著色器快娶或遊戲中轉儲所有的著色器檔案。 - + Dump Game Shaders 傾印遊戲著色器 - + When checked, it will dump all the macro programs of the GPU 选中后,将转储 GPU 的所有宏程序 - + Dump Maxwell Macros 转储 Maxwell 宏 - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower 啟用時將停用 Macro Just In Time 編譯器,會使得效能降低。 - + Disable Macro JIT 停用 Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache 啟用時 yuzu 將記錄有關編譯著色器快取的統計資訊。 - + Enable Shader Feedback 啟用著色器回饋 - + When checked, it executes shaders without loop logic changes 啟用時 yuzu 在執行著色器時,不會修改循環結構的條件判斷。 - + Disable Loop safety checks 停用循環安全檢查 - + Debugging 偵錯 - - Enable FS Access Log - 啟用檔案系統存取記錄 - - - - Dump Audio Commands To Console** - 将音频命令转储至控制台** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 - - - + Enable Verbose Reporting Services** 啟用詳細報告服務 - + + Enable FS Access Log + 啟用檔案系統存取記錄 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 + + + + Dump Audio Commands To Console** + 将音频命令转储至控制台** + + + + Create Minidump After Crash + 微型故障转储 + + + Advanced 進階 - + Kiosk (Quest) Mode Kiosk (Quest) 模式 - + Enable CPU Debugging 啟用 CPU 模擬偵錯 - + Enable Debug Asserts 啟用偵錯 - + Enable Auto-Stub** 啟用自動偵錯** - + Enable All Controller Types 启用其他控制器 - + Disable Web Applet 停用 Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + 允许 yuzu 在启动时检查 Vulkan 环境是否正常工作。如果是其他程序导致 yuzu 出现此问题,请禁用此选项。 + + + + Perform Startup Vulkan Check + 启动时进行 Vulkan 检测 + + + **This will be reset automatically when yuzu closes. **當 yuzu 關閉時會自動重設。 + + + Restart Required + 需要重启 + + + + yuzu is required to restart in order to apply this setting. + 重启 yuzu 后才能应用此设置。 + + + + Web applet not compiled + Web 应用程序未编译 + + + + MiniDump creation not compiled + 小型转储创建未编译 + ConfigureDebugController @@ -1582,37 +1617,47 @@ This would ban both their forum username and their IP address. 使用快速 GPU 時間(不穩定) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 启用悲观缓冲区刷新。此选项将强制刷新未修改的缓冲区,可能会降低性能。 + + + + Use pessimistic buffer flushes (Hack) + 启用悲观缓冲区刷新 (不稳定) + + + Anisotropic Filtering: 各向異性過濾: - + Automatic 自動 - + Default 預設 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2104,7 +2149,7 @@ This would ban both their forum username and their IP address. - + Left Stick 左搖桿 @@ -2198,14 +2243,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2224,7 +2269,7 @@ This would ban both their forum username and their IP address. - + Plus @@ -2237,15 +2282,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2302,231 +2347,236 @@ This would ban both their forum username and their IP address. - + Right Stick 右搖桿 - - - - + + + + Clear 清除 - - - - - + + + + + [not set] [未設定] - - - Toggle button - 切換按鍵 - - - - + + Invert button 無效按鈕 - - + + + Toggle button + 切換按鍵 + + + + Invert axis 方向反轉 - - - + + + Set threshold 設定閾值 - - + + Choose a value between 0% and 100% 選擇介於 0% 和 100% 之間的值 - + + Toggle axis + 切换轴 + + + Set gyro threshold 陀螺仪阈值设定 - + Map Analog Stick 搖桿映射 - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. 按下確定後,先水平再上下移動您的搖桿。 要反轉方向,則先上下再水平移動您的搖桿。 - + Center axis 中心轴 - - + + Deadzone: %1% 無感帶:%1% - - + + Modifier Range: %1% 輕推靈敏度:%1% - - + + Pro Controller Pro 手把 - + Dual Joycons 雙 Joycon 手把 - + Left Joycon 左 Joycon 手把 - + Right Joycon 右 Joycon 手把 - + Handheld 掌機模式 - + GameCube Controller GameCube 手把 - + Poke Ball Plus 精靈球 PLUS - + NES Controller NES 控制器 - + SNES Controller SNES 控制器 - + N64 Controller N64 控制器 - + Sega Genesis Mega Drive - + Start / Pause 開始 / 暫停 - + Z Z - + Control Stick 控制搖桿 - + C-Stick C 搖桿 - + Shake! 搖動! - + [waiting] [等待中] - + New Profile 新增設定檔 - + Enter a profile name: 輸入設定檔名稱: - - + + Create Input Profile 建立輸入設定檔 - + The given profile name is not valid! 輸入的設定檔名稱無效! - + Failed to create the input profile "%1" 建立輸入設定檔「%1」失敗 - + Delete Input Profile 刪除輸入設定檔 - + Failed to delete the input profile "%1" 刪除輸入設定檔「%1」失敗 - + Load Input Profile 載入輸入設定檔 - + Failed to load the input profile "%1" 載入輸入設定檔「%1」失敗 - + Save Input Profile 儲存輸入設定檔 - + Failed to save the input profile "%1" 儲存輸入設定檔「%1」失敗 @@ -2781,42 +2831,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 出版商 - + Add-Ons 延伸模組 - + General 一般 - + System 系統 - + CPU CPU - + Graphics 圖形 - + Adv. Graphics 進階圖形 - + Audio 音訊 - + Properties 屬性 @@ -3528,47 +3578,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>通過讀取與 TAS-nx 腳本具有相同格式的腳本來讀取控制器的輸入。<br/>有關詳細資訊,請參閱 yuzu 官方網站的<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">說明網頁</span></a>。</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). 要確認是哪些快速鍵控制播放/錄製,請參閱快速鍵設定。(設定 > 一般 > 快速鍵) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 警告:這是實驗性功能。<br/>由於不完善的同步方式,可能無法完整的重現。 - + Settings 設定 - + Enable TAS features 啟用 TAS 功能 - + Loop script 循環腳本 - + Pause execution during loads 載入畫面時暫停執行 - + Script Directory 腳本資料夾 - + Path 路徑 - + ... ... @@ -3978,7 +4028,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 驗證 @@ -4065,7 +4115,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 未指定 @@ -4080,17 +4130,36 @@ Drag points to change position, or double-click table cells to edit values.Token 未驗證,因此未儲存您對使用者名稱和 Token 的修改。 - + + Unverified, please click Verify before saving configuration + Tooltip + 令牌未验证,请在保存配置之前进行验证。 + + + + Verifying... 驗證中... - + + Verified + Tooltip + 已验证 + + + + Verification failed + Tooltip + 驗證失敗 + + + Verification failed 驗證失敗 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 驗證失敗。請檢查您输入的 Token 是否正確,並確保您的網路連線正常。 @@ -4159,12 +4228,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting 连接中 - + Connect 连接 @@ -4172,912 +4241,892 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? 我們<a href='https://yuzu-emu.org/help/feature/telemetry/'>蒐集匿名的資料</a>以幫助改善 yuzu。<br/><br/>您願意和我們分享您的使用資料嗎? - + Telemetry 遙測 - + Broken Vulkan Installation Detected 检测到 Vulkan 的安装已损坏 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. Vulkan 初始化失败。<br><br>点击<a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>这里</a>获取此问题的相关信息。 - + Loading Web Applet... 載入 Web Applet... - + Disable Web Applet 停用 Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 禁用 Web 应用程序可能会导致未知的行为,且只能在《超级马里奥 3D 全明星》中使用。您确定要禁用 Web 应用程序吗? (您可以在调试选项中重新启用它。) - + The amount of shaders currently being built 目前正在建構的著色器數量 - + The current selected resolution scaling multiplier. 目前選擇的解析度縮放比例。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 目前的模擬速度。高於或低於 100% 表示比實際 Switch 執行速度更快或更慢。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 遊戲即時 FPS。會因遊戲和場景的不同而改變。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 在不考慮幀數限制和垂直同步的情況下模擬一個 Switch 畫格的實際時間,若要全速模擬,此數值不得超過 16.67 毫秒。 - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files 清除最近的檔案(&C) - + &Continue 繼續(&C) - + &Pause &暫停 - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu 正在執行中 - + Warning Outdated Game Format 過時遊戲格式警告 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. 此遊戲為解構的 ROM 資料夾格式,這是一種過時的格式,已被其他格式取代,如 NCA、NAX、XCI、NSP。解構的 ROM 目錄缺少圖示、中繼資料和更新支援。<br><br>有關 yuzu 支援的各種 Switch 格式說明,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>請參閱我們的 wiki </a>。此訊息將不再顯示。 - - + + Error while loading ROM! 載入 ROM 時發生錯誤! - + The ROM format is not supported. 此 ROM 格式不支援 - + An error occurred initializing the video core. 初始化視訊核心時發生錯誤 - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu 在執行視訊核心時發生錯誤。 這可能是 GPU 驅動程序過舊造成的。 詳細資訊請查閱日誌檔案。 關於日誌檔案的更多資訊,請參考以下頁面:<a href='https://yuzu-emu.org/help/reference/log-files/'>如何上傳日誌檔案</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. 載入 ROM 時發生錯誤!%1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>請參閱 <a href='https://yuzu-emu.org/help/quickstart/'>yuzu 快速指引</a>以重新傾印檔案。<br>您可以前往 yuzu 的 wiki</a> 或 Discord 社群</a>以獲得幫助。 - + An unknown error occurred. Please see the log for more details. 發生未知錯誤,請檢視紀錄了解細節。 - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data 儲存資料 - + Mod Data 模組資料 - + Error Opening %1 Folder 開啟資料夾 %1 時發生錯誤 - - + + Folder does not exist! 資料夾不存在 - + Error Opening Transferable Shader Cache 開啟通用著色器快取位置時發生錯誤 - + Failed to create the shader cache directory for this title. 無法新增此遊戲的著色器快取資料夾。 - + Contents 內容 - + Update 遊戲更新 - + DLC DLC - + Remove Entry 移除項目 - + Remove Installed Game %1? 移除已安裝的遊戲「%1」? - - - - - - + + + + + + Successfully Removed 移除成功 - + Successfully removed the installed base game. 成功移除已安裝的遊戲。 - - - + + + Error Removing %1 移除 %1 失敗 - + The base game is not installed in the NAND and cannot be removed. 此遊戲並非安裝在內部儲存空間,因此無法移除。 - + Successfully removed the installed update. 成功移除已安裝的遊戲更新。 - + There is no update installed for this title. 此遊戲沒有已安裝的更新。 - + There are no DLC installed for this title. 此遊戲沒有已安裝的 DLC。 - + Successfully removed %1 installed DLC. 成功移除遊戲 %1 已安裝的 DLC。 - + Delete OpenGL Transferable Shader Cache? 刪除 OpenGL 模式的著色器快取? - + Delete Vulkan Transferable Shader Cache? 刪除 Vulkan 模式的著色器快取? - + Delete All Transferable Shader Caches? 刪除所有的著色器快取? - + Remove Custom Game Configuration? 移除額外遊戲設定? - + Remove File 刪除檔案 - - + + Error Removing Transferable Shader Cache 刪除通用著色器快取時發生錯誤 - - + + A shader cache for this title does not exist. 此遊戲沒有著色器快取 - + Successfully removed the transferable shader cache. 成功刪除著色器快取。 - + Failed to remove the transferable shader cache. 刪除通用著色器快取失敗。 - - + + Error Removing Transferable Shader Caches 刪除通用著色器快取時發生錯誤 - + Successfully removed the transferable shader caches. 成功刪除通用著色器快取。 - + Failed to remove the transferable shader cache directory. 無法刪除著色器快取資料夾。 - - + + Error Removing Custom Configuration 移除額外遊戲設定時發生錯誤 - + A custom configuration for this title does not exist. 此遊戲沒有額外設定。 - + Successfully removed the custom game configuration. 成功移除額外遊戲設定。 - + Failed to remove the custom game configuration. 移除額外遊戲設定失敗。 - - + + RomFS Extraction Failed! RomFS 抽取失敗! - + There was an error copying the RomFS files or the user cancelled the operation. 複製 RomFS 檔案時發生錯誤或使用者取消動作。 - + Full 全部 - + Skeleton 部分 - + Select RomFS Dump Mode 選擇RomFS傾印模式 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. 請選擇如何傾印 RomFS。<br>「全部」會複製所有檔案到新資料夾中,而<br>「部分」只會建立資料夾結構。 - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 沒有足夠的空間用於抽取 RomFS。請確保有足夠的空間或於模擬 > 設定 >系統 >檔案系統 > 傾印根目錄中選擇其他資料夾。 - + Extracting RomFS... 抽取 RomFS 中... - - + + Cancel 取消 - + RomFS Extraction Succeeded! RomFS 抽取完成! - + The operation completed successfully. 動作已成功完成 - + Error Opening %1 開啟 %1 時發生錯誤 - + Select Directory 選擇資料夾 - + Properties 屬性 - + The game properties could not be loaded. 無法載入遊戲屬性 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 執行檔 (%1);;所有檔案 (*.*) - + Load File 開啟檔案 - + Open Extracted ROM Directory 開啟已抽取的 ROM 資料夾 - + Invalid Directory Selected 選擇的資料夾無效 - + The directory you have selected does not contain a 'main' file. 選擇的資料夾未包含「main」檔案。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 可安装的 Switch 檔案 (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX 卡帶映像 (*.xci) - + Install Files 安裝檔案 - + %n file(s) remaining 剩餘 %n 個檔案 - + Installing file "%1"... 正在安裝檔案「%1」... - - + + Install Results 安裝結果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 為了避免潛在的衝突,不建議將遊戲本體安裝至內部儲存空間。 此功能僅用於安裝遊戲更新和 DLC。 - + %n file(s) were newly installed 最近安裝了 %n 個檔案 - + %n file(s) were overwritten %n 個檔案被取代 - + %n file(s) failed to install %n 個檔案安裝失敗 - + System Application 系統應用程式 - + System Archive 系統檔案 - + System Application Update 系統應用程式更新 - + Firmware Package (Type A) 韌體包(A型) - + Firmware Package (Type B) 韌體包(B型) - + Game 遊戲 - + Game Update 遊戲更新 - + Game DLC 遊戲 DLC - + Delta Title Delta Title - + Select NCA Install Type... 選擇 NCA 安裝類型... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 請選擇此 NCA 的安裝類型: (在多數情況下,選擇預設的「遊戲」即可。) - + Failed to Install 安裝失敗 - + The title type you selected for the NCA is invalid. 選擇的 NCA 安裝類型無效。 - + File not found 找不到檔案 - + File "%1" not found 找不到「%1」檔案 - + OK 確定 - + Missing yuzu Account 未設定 yuzu 帳號 - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. 為了上傳相容性測試結果,您必須登入 yuzu 帳號。<br><br/>欲登入 yuzu 帳號請至模擬 &gt; 設定 &gt; 網路。 - + Error opening URL 開啟 URL 時發生錯誤 - + Unable to open the URL "%1". 無法開啟 URL:「%1」。 - + TAS Recording TAS 錄製 - + Overwrite file of player 1? 覆寫玩家 1 的檔案? - + Invalid config detected 偵測到無效設定 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 掌機手把無法在主機模式中使用。將會選擇 Pro 手把。 - - + + Error 错误 - - + + The current game is not looking for amiibos 当前游戏并没有在寻找 Amiibos - - + + Amiibo Amiibo - - + + The current amiibo has been removed 当前的 Amiibo 已被移除。 - + Amiibo File (%1);; All Files (*.*) Amiibo 檔案 (%1);; 所有檔案 (*.*) - + Load Amiibo 開啟 Amiibo - - Error opening Amiibo data file - 開啟 Amiibo 檔案時發生錯誤 - - - - Unable to open Amiibo file "%1" for reading. - 無法開啟 Amiibo 檔案 %1。 - - - - Error reading Amiibo data file - 讀取 Amiibo 檔案時發生錯誤 - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - 無法讀取完整的 Amiibo 資料。應讀取 %1 位元組,但實際僅讀取到 %2 位元組。 - - - + Error loading Amiibo data 載入 Amiibo 資料時發生錯誤 - + Unable to load Amiibo data. 無法載入 Amiibo 資料。 - + Capture Screenshot 截圖 - + PNG Image (*.png) PNG 圖片 (*.png) - + TAS state: Running %1/%2 TAS 狀態:正在執行 %1/%2 - + TAS state: Recording %1 TAS 狀態:正在錄製 %1 - + TAS state: Idle %1/%2 TAS 狀態:閒置 %1/%2 - + TAS State: Invalid TAS 狀態:無效 - + &Stop Running &停止執行 - + &Start 開始(&S) - + Stop R&ecording 停止錄製 - + R&ecord 錄製 (&E) - + Building: %n shader(s) 正在編譯 %n 個著色器檔案 - + Scale: %1x %1 is the resolution scaling factor 縮放比例:%1x - + Speed: %1% / %2% 速度:%1% / %2% - + Speed: %1% 速度:%1% - + Game: %1 FPS (Unlocked) 遊戲: %1 FPS(未限制) - + Game: %1 FPS 遊戲:%1 FPS - + Frame: %1 ms 畫格延遲:%1 ms - + GPU NORMAL GPU 一般效能 - + GPU HIGH GPU 高效能 - + GPU EXTREME GPU 最高效能 - + GPU ERROR GPU 錯誤 - + DOCKED 主机模式 - + HANDHELD 掌机模式 - + NEAREST 最近鄰域 - - + + BILINEAR 雙線性 - + BICUBIC 雙三次 - + GAUSSIAN 高斯 - + SCALEFORCE 強制縮放 - + FSR FSR - - + + NO AA 抗鋸齒關 - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 此遊戲需要從您的 Switch 傾印額外檔案。<br/><br/>有關傾印這些檔案的更多資訊,請參閱以下 wiki 網頁:Dumping System Archives and the Shared Fonts from a Switch Console<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>。<br/><br/>您要停止並回到遊戲清單嗎?繼續模擬可能會導致當機、存檔損毀或其他錯誤。 - + yuzu was unable to locate a Switch system archive. %1 Yuzu 找不到 Switch 系統檔案 %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Yuzu 找不到 Switch 系統檔案:%1。%2 - + System Archive Not Found 找不到系統檔案 - + System Archive Missing 系統檔案遺失 - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu 找不到 Switch 共享字型 %1 - + Shared Fonts Not Found 找不到共享字型 - + Shared Font Missing 遺失共享字型 - + Fatal Error 嚴重錯誤 - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu 發生嚴重錯誤,請檢視紀錄以了解細節。更多資訊請參閱網頁:<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>。<br/><br/>您要停止模擬並回到遊戲清單嗎?繼續模擬可能會導致當機、存檔損毀或其他錯誤。 - + Fatal Error encountered 發生嚴重錯誤 - + Confirm Key Rederivation 確認重新產生金鑰 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5093,37 +5142,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 這將刪除您自動產生的金鑰檔案並重新執行產生金鑰模組。 - + Missing fuses 遺失項目 - + - Missing BOOT0 - 遺失 BOOT0 - + - Missing BCPKG2-1-Normal-Main - 遺失 BCPKG2-1-Normal-Main - + - Missing PRODINFO - 遺失 PRODINFO - + Derivation Components Missing 遺失產生元件 - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 缺少加密金鑰。 <br>請按照<a href='https://yuzu-emu.org/help/quickstart/'>《Yuzu快速入門指南》來取得所有金鑰、韌體、遊戲<br><br><small>(%1)。 - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5132,39 +5181,39 @@ on your system's performance. 您的系統效能。 - + Deriving Keys 產生金鑰 - + Select RomFS Dump Target 選擇 RomFS 傾印目標 - + Please select which RomFS you would like to dump. 請選擇希望傾印的 RomFS。 - + Are you sure you want to close yuzu? 您確定要關閉 yuzu 嗎? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 您確定要停止模擬嗎?未儲存的進度將會遺失。 - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5541,12 +5590,12 @@ Screen. HostRoomWindow - + Error 错误 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 向公共大厅公开房间时失败。为了管理公开房间,您必须在模拟 -> 设置 -> 网络中配置有效的 yuzu 帐户。如果不想在公共大厅中公开房间,请选择“未列出”。 @@ -5556,11 +5605,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute 静音/关闭静音 + @@ -5582,112 +5632,111 @@ Debug Message: - Main Window 主窗口 - + Audio Volume Down 调低音量 - + Audio Volume Up 调高音量 - + Capture Screenshot 截圖 - + Change Adapting Filter 更改窗口滤镜 - + Change Docked Mode 更改运行模式 - + Change GPU Accuracy 更改 GPU 精度 - + Continue/Pause Emulation 继续/暂停模拟 - + Exit Fullscreen 退出全屏 - + Exit yuzu 退出 yuzu - + Fullscreen 全屏 - + Load File 開啟檔案 - + Load/Remove Amiibo 加载/移除 Amiibo - + Restart Emulation 重新启动模拟 - + Stop Emulation 停止模拟 - + TAS Record TAS 录制 - + TAS Reset 重设 TAS - + TAS Start/Stop TAS 开始/停止 - + Toggle Filter Bar 切换搜索栏 - + Toggle Framerate Limit 切换帧率限制 - + Toggle Mouse Panning 切换鼠标平移 - + Toggle Status Bar 切换状态栏 @@ -5801,42 +5850,42 @@ Debug Message: 刷新游戏大厅 - + Password Required to Join 加入此房间需要密码 - + Password: 密码: - + Room Name 房间名称 - + Preferred Game 首选游戏 - + Host 管理 - + Players 玩家 - + Refreshing 刷新中 - + Refresh List 刷新列表 @@ -6163,7 +6212,7 @@ Debug Message: - + Connected 已連線 @@ -6186,7 +6235,7 @@ Debug Message: 调试信息: - + New Messages Received 收到了新消息 @@ -6291,22 +6340,41 @@ They may have left the room. 他们可能已经离开了房间。 - + + No network interface is selected. +Please go to Configure -> System -> Network and make a selection. + 未选择网络接口。 +请于设置 -> 系统 -> 网络中进行相关设置。 + + + + Game already running + 游戏已在运行中 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + 不推荐游戏正在运行时加入房间,这可能会导致房间的某些功能出现异常。 +是否继续? + + + Leave Room 离开房间 - + You are about to close the room. Any network connections will be closed. 您正要关闭房间。所有的网络连接都将关闭。 - + Disconnect 断开连接 - + You are about to leave the room. Any network connections will be closed. 您正要离开房间。所有的网络连接都将关闭。 @@ -6314,7 +6382,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error 错误 @@ -6373,7 +6441,7 @@ p, li { white-space: pre-wrap; } %1 正在玩 %2 - + Not playing a game 不在玩游戏 @@ -6428,7 +6496,7 @@ p, li { white-space: pre-wrap; } - + [not set] [未設定] @@ -6443,10 +6511,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axis %1%2 @@ -6460,9 +6528,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [未知] @@ -6627,15 +6695,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [無效] - - + + %1%2Hat %3 %1%2Hat 控制器 %3 @@ -6643,35 +6711,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2軸 %3 - + %1%2Axis %3,%4,%5 %1%2軸 %3,%4,%5 - + %1%2Motion %3 %1%2體感 %3 - - + + %1%2Button %3 %1%2按鈕 %3 - + [unused] [未使用] @@ -6712,7 +6780,7 @@ p, li { white-space: pre-wrap; } 額外按鍵 - + %1%2%3 %1%2%3 From b80f7faebe83864b7c1f53a5cc4fc22b9901c853 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 1 Oct 2022 01:32:24 -0400 Subject: [PATCH 020/245] macro_jit_x64: cancel exit for taken branch --- src/video_core/macro/macro_jit_x64.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index aca25d902b..30d6186955 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -429,17 +429,11 @@ void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { Xbyak::Label handle_post_exit{}; Xbyak::Label skip{}; jmp(skip, T_NEAR); - if (opcode.is_exit) { - L(handle_post_exit); - // Execute 1 instruction - mov(BRANCH_HOLDER, end_of_code); - // Jump to next instruction to skip delay slot check - jmp(labels[jump_address], T_NEAR); - } else { - L(handle_post_exit); - xor_(BRANCH_HOLDER, BRANCH_HOLDER); - jmp(labels[jump_address], T_NEAR); - } + + L(handle_post_exit); + xor_(BRANCH_HOLDER, BRANCH_HOLDER); + jmp(labels[jump_address], T_NEAR); + L(skip); mov(BRANCH_HOLDER, handle_post_exit); jmp(delay_skip[pc], T_NEAR); From 31e3437a2fd4dc4efdae4401bbc57a1a6929dd16 Mon Sep 17 00:00:00 2001 From: Zwip-Zwap Zapony Date: Sat, 1 Oct 2022 18:18:08 +0200 Subject: [PATCH 021/245] Fix "controller.colors_state.right" being "left" --- src/core/hid/emulated_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 01c43be934..2cff279b11 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -93,7 +93,7 @@ void EmulatedController::ReloadFromSettings() { .body = GetNpadColor(player.body_color_left), .button = GetNpadColor(player.button_color_left), }; - controller.colors_state.left = { + controller.colors_state.right = { .body = GetNpadColor(player.body_color_right), .button = GetNpadColor(player.button_color_right), }; From 700f1d498e7c3d5c6f1dff11fad7bc7fb928bb47 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 1 Oct 2022 10:25:28 -0700 Subject: [PATCH 022/245] Migrate deploy-linux.sh from lat9nq's repo. --- .ci/scripts/linux/docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index 67726ee0fb..35c4a43687 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh @@ -33,7 +33,7 @@ DESTDIR="$PWD/AppDir" ninja install rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester # Download tools needed to build an AppImage -wget -nc https://raw.githubusercontent.com/lat9nq/deploy/main/linux/deploy-linux.sh +wget -nc https://raw.githubusercontent.com/yuzu-emu/ext-linux-bin/main/gcc/deploy-linux.sh wget -nc https://raw.githubusercontent.com/yuzu-emu/AppImageKit-checkrt/old/AppRun.sh wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/exec-x86_64.so # Set executable bit From 1dba5fab626b2b441d069a668fd148232384da14 Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Sat, 1 Oct 2022 15:27:23 -0700 Subject: [PATCH 023/245] Qt: work around Qt5's font choice for Chinese On Windows there are currently two fonts used. The first, does the Menu, QTreeView and Tooltips Second is Everything else which is a default font. From inspecting QApplication::font() at runtime Windows 10 English: QFont(MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0) Windows 11 Japanese: MS UI Gothic,9 ,-1,5,50,0,0,0,0,0 Windows 11 Traditional Chinese: PMingLiU,9 ,-1,5,50,0,0,0,0,0 Windows 11 Simplified Chinese: SimSun,9 ,-1,5,50,0,0,0,0,0 Windows 11 Korean: Gulim,9 ,-1,5,50,0,0,0,0,0 I initially investigated dynamically changing the font when the UI language is English, but this was getting quite messy Qt6 makes changes to default font in some situations, so this PR is being narrowed in scope to only effect Chinese font choices. This change only effects rendering of Latin/Cyrillic characters. --- src/yuzu/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 612b8dbb89..fc094ea1d0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -261,6 +261,18 @@ static QString PrettyProductName() { return QSysInfo::prettyProductName(); } +#ifdef _WIN32 +static void OverrideWindowsFont() { + // Qt5 chooses these fonts on Windows and they have fairly ugly alphanumeric/cyrllic characters + // Asking to use "MS Shell Dlg 2" gives better other chars while leaving the Chinese Characters. + const QString startup_font = QApplication::font().family(); + const QStringList ugly_fonts = {QStringLiteral("SimSun"), QStringLiteral("PMingLiU")}; + if (ugly_fonts.contains(startup_font)) { + QApplication::setFont(QFont(QStringLiteral("MS Shell Dlg 2"), 9, QFont::Normal)); + } +} +#endif + bool GMainWindow::CheckDarkMode() { #ifdef __linux__ const QPalette test_palette(qApp->palette()); @@ -4134,6 +4146,10 @@ int main(int argc, char* argv[]) { QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QApplication app(argc, argv); +#ifdef _WIN32 + OverrideWindowsFont(); +#endif + // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021 // so we can see if we get \u3008 instead // TL;DR all other number formats are consecutive in unicode code points From 12256275156112ea7dc7c80bfbc73bc679326464 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 1 Oct 2022 20:31:21 -0400 Subject: [PATCH 024/245] macro_jit_x64: fix miscompilation of bit extraction operations --- src/video_core/macro/macro_jit_x64.cpp | 46 +++++--------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index aca25d902b..3b55228b9d 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -279,28 +279,13 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { auto dst = Compile_GetRegister(opcode.src_a, RESULT); auto src = Compile_GetRegister(opcode.src_b, eax); - if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) { - shr(src, opcode.bf_src_bit); - } else if (opcode.bf_src_bit == 31) { - xor_(src, src); - } - // Don't bother masking the whole register since we're using a 32 bit register - if (opcode.bf_size != 31 && opcode.bf_size != 0) { - and_(src, opcode.GetBitfieldMask()); - } else if (opcode.bf_size == 0) { - xor_(src, src); - } - if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) { - shl(src, opcode.bf_dst_bit); - } else if (opcode.bf_dst_bit == 31) { - xor_(src, src); - } - const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); - if (mask != 0xffffffff) { - and_(dst, mask); - } + and_(dst, mask); + shr(src, opcode.bf_src_bit); + and_(src, opcode.GetBitfieldMask()); + shl(src, opcode.bf_dst_bit); or_(dst, src); + Compile_ProcessResult(opcode.result_operation, opcode.dst); } @@ -309,17 +294,9 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { const auto src = Compile_GetRegister(opcode.src_b, RESULT); shr(src, dst.cvt8()); - if (opcode.bf_size != 0 && opcode.bf_size != 31) { - and_(src, opcode.GetBitfieldMask()); - } else if (opcode.bf_size == 0) { - xor_(src, src); - } + and_(src, opcode.GetBitfieldMask()); + shl(src, opcode.bf_dst_bit); - if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) { - shl(src, opcode.bf_dst_bit); - } else if (opcode.bf_dst_bit == 31) { - xor_(src, src); - } Compile_ProcessResult(opcode.result_operation, opcode.dst); } @@ -327,13 +304,8 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { const auto dst = Compile_GetRegister(opcode.src_a, ecx); const auto src = Compile_GetRegister(opcode.src_b, RESULT); - if (opcode.bf_src_bit != 0) { - shr(src, opcode.bf_src_bit); - } - - if (opcode.bf_size != 31) { - and_(src, opcode.GetBitfieldMask()); - } + shr(src, opcode.bf_src_bit); + and_(src, opcode.GetBitfieldMask()); shl(src, dst.cvt8()); Compile_ProcessResult(opcode.result_operation, opcode.dst); From a60b669ef4e4105734bfbfe2320ce0291bfe5dac Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 10 Jul 2021 19:32:34 +0200 Subject: [PATCH 025/245] MacroHLE: Add MultidrawIndirect HLE Macro. --- src/video_core/macro/macro_hle.cpp | 63 +++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 58382755b8..cabe8dcbf1 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -3,6 +3,8 @@ #include #include +#include "common/scope_exit.h" +#include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" @@ -58,6 +60,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& maxwell3d.regs.index_array.first = parameters[3]; maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? maxwell3d.regs.index_array.count = parameters[1]; + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; maxwell3d.regs.vb_element_base = element_base; maxwell3d.regs.vb_base_instance = base_instance; maxwell3d.mme_draw.instance_count = instance_count; @@ -80,10 +83,67 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } -constexpr std::array, 3> hle_funcs{{ +// Multidraw Indirect +void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { + SCOPE_EXIT({ + // Clean everything. + maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? + maxwell3d.regs.index_array.count = 0; + maxwell3d.regs.vb_element_base = 0x0; + maxwell3d.regs.vb_base_instance = 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.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + }); + const u32 start_indirect = parameters[0]; + const u32 end_indirect = parameters[1]; + if (start_indirect >= end_indirect) { + // Nothing to do. + return; + } + const auto topology = + static_cast(parameters[2]); + maxwell3d.regs.draw.topology.Assign(topology); + const u32 padding = parameters[3]; + const std::size_t max_draws = parameters[4]; + + const u32 indirect_words = 5 + padding; + const std::size_t first_draw = start_indirect; + const std::size_t effective_draws = end_indirect - start_indirect; + const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); + + for (std::size_t index = first_draw; index < last_draw; index++) { + const std::size_t base = index * indirect_words + 5; + const u32 num_vertices = parameters[base]; + const u32 instance_count = parameters[base + 1]; + const u32 first_index = parameters[base + 2]; + const u32 base_vertex = parameters[base + 3]; + const u32 base_instance = parameters[base + 4]; + maxwell3d.regs.index_array.first = first_index; + maxwell3d.regs.reg_array[0x446] = base_vertex; + maxwell3d.regs.index_array.count = num_vertices; + maxwell3d.regs.vb_element_base = base_vertex; + maxwell3d.regs.vb_base_instance = base_instance; + maxwell3d.mme_draw.instance_count = instance_count; + maxwell3d.CallMethodFromMME(0x8e3, 0x640); + maxwell3d.CallMethodFromMME(0x8e4, base_vertex); + maxwell3d.CallMethodFromMME(0x8e5, base_instance); + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + if (maxwell3d.ShouldExecute()) { + maxwell3d.Rasterizer().Draw(true, true); + } + maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; + } +} + +constexpr std::array, 4> hle_funcs{{ {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, {0x0217920100488FF7, &HLE_0217920100488FF7}, + {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164}, }}; class HLEMacroImpl final : public CachedMacro { @@ -99,6 +159,7 @@ private: Engines::Maxwell3D& maxwell3d; HLEFunction func; }; + } // Anonymous namespace HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} From e8d71712e7054748e7e18de9362de1f5a394b46b Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 19:46:49 -0500 Subject: [PATCH 026/245] input_common: Create virtual amiibo driver --- src/common/input.h | 27 ++++++ src/input_common/CMakeLists.txt | 2 + src/input_common/drivers/virtual_amiibo.cpp | 101 ++++++++++++++++++++ src/input_common/drivers/virtual_amiibo.h | 61 ++++++++++++ src/input_common/input_engine.cpp | 37 +++++++ src/input_common/input_engine.h | 16 ++++ 6 files changed, 244 insertions(+) create mode 100644 src/input_common/drivers/virtual_amiibo.cpp create mode 100644 src/input_common/drivers/virtual_amiibo.h diff --git a/src/common/input.h b/src/common/input.h index 825b0d650d..8365cc36ed 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -76,6 +76,19 @@ enum class PollingError { Unknown, }; +// Nfc reply from the controller +enum class NfcState { + Success, + NewAmiibo, + WaitingForAmiibo, + AmiiboRemoved, + NotAnAmiibo, + NotSupported, + WrongDeviceState, + WriteFailed, + Unknown, +}; + // Ir camera reply from the controller enum class CameraError { None, @@ -202,6 +215,11 @@ struct CameraStatus { std::vector data{}; }; +struct NfcStatus { + NfcState state{}; + std::vector data{}; +}; + // List of buttons to be passed to Qt that can be translated enum class ButtonNames { Undefined, @@ -260,6 +278,7 @@ struct CallbackStatus { BatteryStatus battery_status{}; VibrationStatus vibration_status{}; CameraStatus camera_status{}; + NfcStatus nfc_status{}; }; // Triggered once every input change @@ -312,6 +331,14 @@ public: virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { return CameraError::NotSupported; } + + virtual NfcState SupportsNfc() { + return NfcState::NotSupported; + } + + virtual NfcState WriteNfcData([[maybe_unused]] const std::vector& data) { + return NfcState::NotSupported; + } }; /// An abstract class template for a factory that can create input devices. diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 4b91b88cee..2cf9eb97f4 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -18,6 +18,8 @@ add_library(input_common STATIC drivers/touch_screen.h drivers/udp_client.cpp drivers/udp_client.h + drivers/virtual_amiibo.cpp + drivers/virtual_amiibo.h helpers/stick_from_buttons.cpp helpers/stick_from_buttons.h helpers/touch_from_buttons.cpp diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp new file mode 100644 index 0000000000..8fadb1322a --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "input_common/drivers/virtual_amiibo.h" + +namespace InputCommon { +constexpr PadIdentifier identifier = { + .guid = Common::UUID{}, + .port = 0, + .pad = 0, +}; + +VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} + +VirtualAmiibo::~VirtualAmiibo() {} + +Common::Input::PollingError VirtualAmiibo::SetPollingMode( + [[maybe_unused]] const PadIdentifier& identifier_, + const Common::Input::PollingMode polling_mode_) { + polling_mode = polling_mode_; + + if (polling_mode == Common::Input::PollingMode::NFC) { + if (state == State::Initialized) { + state = State::WaitingForAmiibo; + } + } else { + if (state == State::AmiiboIsOpen) { + CloseAmiibo(); + } + } + + return Common::Input::PollingError::None; +} + +Common::Input::NfcState VirtualAmiibo::SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier_) { + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteNfcData( + [[maybe_unused]] const PadIdentifier& identifier_, const std::vector& data) { + const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, + Common::FS::FileType::BinaryFile}; + + if (!amiibo_file.IsOpen()) { + LOG_ERROR(Core, "Amiibo is already on use"); + return Common::Input::NfcState::WriteFailed; + } + + if (!amiibo_file.Write(data)) { + LOG_ERROR(Service_NFP, "Error writting to file"); + return Common::Input::NfcState::WriteFailed; + } + + return Common::Input::NfcState::Success; +} + +VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { + return state; +} + +VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { + const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (state != State::WaitingForAmiibo) { + return Info::WrongDeviceState; + } + + if (!amiibo_file.IsOpen()) { + return Info::UnableToLoad; + } + + amiibo_data.resize(amiibo_size); + + if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) { + return Info::NotAnAmiibo; + } + + file_path = filename; + state = State::AmiiboIsOpen; + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); + return Info::Success; +} + +VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { + state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo + : State::Initialized; + SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); + return Info::Success; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h new file mode 100644 index 0000000000..5790e4a1ff --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "input_common/input_engine.h" + +namespace Common::FS { +class IOFile; +} + +namespace InputCommon { + +class VirtualAmiibo final : public InputEngine { +public: + enum class State { + Initialized, + WaitingForAmiibo, + AmiiboIsOpen, + }; + + enum class Info { + Success, + UnableToLoad, + NotAnAmiibo, + WrongDeviceState, + Unknown, + }; + + explicit VirtualAmiibo(std::string input_engine_); + ~VirtualAmiibo() override; + + // Sets polling mode to a controller + Common::Input::PollingError SetPollingMode( + const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; + + Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) override; + + Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, + const std::vector& data) override; + + State GetCurrentState() const; + + Info LoadAmiibo(const std::string& amiibo_file); + Info CloseAmiibo(); + +private: + static constexpr std::size_t amiibo_size = 0x21C; + static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; + + std::string file_path{}; + State state{State::Initialized}; + std::vector amiibo_data; + Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; +}; +} // namespace InputCommon diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 6ede0e4b0e..61cfd0911c 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp @@ -102,6 +102,17 @@ void InputEngine::SetCamera(const PadIdentifier& identifier, TriggerOnCameraChange(identifier, value); } +void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) { + { + std::scoped_lock lock{mutex}; + ControllerData& controller = controller_list.at(identifier); + if (!configuring) { + controller.nfc = value; + } + } + TriggerOnNfcChange(identifier, value); +} + bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); @@ -189,6 +200,18 @@ Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifi return controller.camera; } +Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const { + std::scoped_lock lock{mutex}; + const auto controller_iter = controller_list.find(identifier); + if (controller_iter == controller_list.cend()) { + LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), + identifier.pad, identifier.port); + return {}; + } + const ControllerData& controller = controller_iter->second; + return controller.nfc; +} + void InputEngine::ResetButtonState() { for (const auto& controller : controller_list) { for (const auto& button : controller.second.buttons) { @@ -355,6 +378,20 @@ void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier, } } +void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier, + [[maybe_unused]] const Common::Input::NfcStatus& value) { + std::scoped_lock lock{mutex_callback}; + for (const auto& poller_pair : callback_list) { + const InputIdentifier& poller = poller_pair.second; + if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) { + continue; + } + if (poller.callback.on_change) { + poller.callback.on_change(); + } + } +} + bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, const PadIdentifier& identifier, EngineInputType type, int index) const { diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index f6b3c46104..9b8470c6f0 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -42,6 +42,7 @@ enum class EngineInputType { Camera, HatButton, Motion, + Nfc, }; namespace std { @@ -127,6 +128,17 @@ public: return Common::Input::CameraError::NotSupported; } + // Request nfc data from a controller + virtual Common::Input::NfcState SupportsNfc([[maybe_unused]] const PadIdentifier& identifier) { + return Common::Input::NfcState::NotSupported; + } + + // Writes data to an nfc tag + virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, + [[maybe_unused]] const std::vector& data) { + return Common::Input::NfcState::NotSupported; + } + // Returns the engine name [[nodiscard]] const std::string& GetEngineName() const; @@ -183,6 +195,7 @@ public: Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; + Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; int SetCallback(InputIdentifier input_identifier); void SetMappingCallback(MappingCallback callback); @@ -195,6 +208,7 @@ protected: void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); + void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { return "Unknown"; @@ -208,6 +222,7 @@ private: std::unordered_map motions; Common::Input::BatteryLevel battery{}; Common::Input::CameraStatus camera{}; + Common::Input::NfcStatus nfc{}; }; void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); @@ -218,6 +233,7 @@ private: const BasicMotion& value); void TriggerOnCameraChange(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); + void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, const PadIdentifier& identifier, EngineInputType type, From da8864d00261894ebac8764786cd7fc51f8c566c Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 20:28:27 -0500 Subject: [PATCH 027/245] input_common: Enable virtual amiibo driver --- src/input_common/input_poller.cpp | 64 +++++++++++++++++++++++++++++++ src/input_common/input_poller.h | 10 +++++ src/input_common/main.cpp | 21 ++++++++++ src/input_common/main.h | 7 ++++ 4 files changed, 102 insertions(+) diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ffb9b945e5..a8eb1442b1 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -705,6 +705,47 @@ private: InputEngine* input_engine; }; +class InputFromNfc final : public Common::Input::InputDevice { +public: + explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_) + : identifier(identifier_), input_engine(input_engine_) { + UpdateCallback engine_callback{[this]() { OnChange(); }}; + const InputIdentifier input_identifier{ + .identifier = identifier, + .type = EngineInputType::Nfc, + .index = 0, + .callback = engine_callback, + }; + callback_key = input_engine->SetCallback(input_identifier); + } + + ~InputFromNfc() override { + input_engine->DeleteCallback(callback_key); + } + + Common::Input::NfcStatus GetStatus() const { + return input_engine->GetNfc(identifier); + } + + void ForceUpdate() override { + OnChange(); + } + + void OnChange() { + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::Nfc, + .nfc_status = GetStatus(), + }; + + TriggerOnChange(status); + } + +private: + const PadIdentifier identifier; + int callback_key; + InputEngine* input_engine; +}; + class OutputFromIdentifier final : public Common::Input::OutputDevice { public: explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) @@ -727,6 +768,14 @@ public: return input_engine->SetCameraFormat(identifier, camera_format); } + Common::Input::NfcState SupportsNfc() override { + return input_engine->SupportsNfc(identifier); + } + + Common::Input::NfcState WriteNfcData(const std::vector& data) override { + return input_engine->WriteNfcData(identifier, data); + } + private: const PadIdentifier identifier; InputEngine* input_engine; @@ -978,6 +1027,18 @@ std::unique_ptr InputFactory::CreateCameraDevice( return std::make_unique(identifier, input_engine.get()); } +std::unique_ptr InputFactory::CreateNfcDevice( + const Common::ParamPackage& params) { + const PadIdentifier identifier = { + .guid = Common::UUID{params.Get("guid", "")}, + .port = static_cast(params.Get("port", 0)), + .pad = static_cast(params.Get("pad", 0)), + }; + + input_engine->PreSetController(identifier); + return std::make_unique(identifier, input_engine.get()); +} + InputFactory::InputFactory(std::shared_ptr input_engine_) : input_engine(std::move(input_engine_)) {} @@ -989,6 +1050,9 @@ std::unique_ptr InputFactory::Create( if (params.Has("camera")) { return CreateCameraDevice(params); } + if (params.Has("nfc")) { + return CreateNfcDevice(params); + } if (params.Has("button") && params.Has("axis")) { return CreateTriggerDevice(params); } diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h index 4410a84154..d7db13ce42 100644 --- a/src/input_common/input_poller.h +++ b/src/input_common/input_poller.h @@ -222,6 +222,16 @@ private: std::unique_ptr CreateCameraDevice( const Common::ParamPackage& params); + /** + * Creates a nfc device from the parameters given. + * @param params contains parameters for creating the device: + * - "guid": text string for identifying controllers + * - "port": port of the connected device + * - "pad": slot of the connected controller + * @returns a unique input device with the parameters specified + */ + std::unique_ptr CreateNfcDevice(const Common::ParamPackage& params); + std::shared_ptr input_engine; }; } // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 75a57b9fc6..b2064ef955 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -11,6 +11,7 @@ #include "input_common/drivers/tas_input.h" #include "input_common/drivers/touch_screen.h" #include "input_common/drivers/udp_client.h" +#include "input_common/drivers/virtual_amiibo.h" #include "input_common/helpers/stick_from_buttons.h" #include "input_common/helpers/touch_from_buttons.h" #include "input_common/input_engine.h" @@ -87,6 +88,15 @@ struct InputSubsystem::Impl { Common::Input::RegisterFactory(camera->GetEngineName(), camera_output_factory); + virtual_amiibo = std::make_shared("virtual_amiibo"); + virtual_amiibo->SetMappingCallback(mapping_callback); + virtual_amiibo_input_factory = std::make_shared(virtual_amiibo); + virtual_amiibo_output_factory = std::make_shared(virtual_amiibo); + Common::Input::RegisterFactory(virtual_amiibo->GetEngineName(), + virtual_amiibo_input_factory); + Common::Input::RegisterFactory(virtual_amiibo->GetEngineName(), + virtual_amiibo_output_factory); + #ifdef HAVE_SDL2 sdl = std::make_shared("sdl"); sdl->SetMappingCallback(mapping_callback); @@ -327,6 +337,7 @@ struct InputSubsystem::Impl { std::shared_ptr tas_input; std::shared_ptr udp_client; std::shared_ptr camera; + std::shared_ptr virtual_amiibo; std::shared_ptr keyboard_factory; std::shared_ptr mouse_factory; @@ -335,6 +346,7 @@ struct InputSubsystem::Impl { std::shared_ptr udp_client_input_factory; std::shared_ptr tas_input_factory; std::shared_ptr camera_input_factory; + std::shared_ptr virtual_amiibo_input_factory; std::shared_ptr keyboard_output_factory; std::shared_ptr mouse_output_factory; @@ -342,6 +354,7 @@ struct InputSubsystem::Impl { std::shared_ptr udp_client_output_factory; std::shared_ptr tas_output_factory; std::shared_ptr camera_output_factory; + std::shared_ptr virtual_amiibo_output_factory; #ifdef HAVE_SDL2 std::shared_ptr sdl; @@ -402,6 +415,14 @@ const Camera* InputSubsystem::GetCamera() const { return impl->camera.get(); } +VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { + return impl->virtual_amiibo.get(); +} + +const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const { + return impl->virtual_amiibo.get(); +} + std::vector InputSubsystem::GetInputDevices() const { return impl->GetInputDevices(); } diff --git a/src/input_common/main.h b/src/input_common/main.h index 9a969e747b..ced2523839 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -33,6 +33,7 @@ class Camera; class Keyboard; class Mouse; class TouchScreen; +class VirtualAmiibo; struct MappingData; } // namespace InputCommon @@ -101,6 +102,12 @@ public: /// Retrieves the underlying camera input device. [[nodiscard]] const Camera* GetCamera() const; + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo(); + + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; + /** * Returns all available input devices that this Factory can create a new device with. * Each returned ParamPackage should have a `display` field used for display, a `engine` field From f6d57d7dd97751df7ee5fe4bc3667305d594010f Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 20:31:54 -0500 Subject: [PATCH 028/245] yuzu: Use virtual amiibo driver instead of nfp service --- src/yuzu/main.cpp | 51 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e2c2b92925..632f7c9c9b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -105,12 +105,12 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/hle/kernel/k_process.h" #include "core/hle/service/am/am.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/nfp/nfp.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" #include "input_common/drivers/tas_input.h" +#include "input_common/drivers/virtual_amiibo.h" #include "input_common/main.h" #include "ui_main.h" #include "util/overlay_dialog.h" @@ -3211,21 +3211,16 @@ void GMainWindow::OnLoadAmiibo() { return; } - Service::SM::ServiceManager& sm = system->ServiceManager(); - auto nfc = sm.GetService("nfp:user"); - if (nfc == nullptr) { - QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); - return; - } - const auto nfc_state = nfc->GetCurrentState(); - if (nfc_state == Service::NFP::DeviceState::TagFound || - nfc_state == Service::NFP::DeviceState::TagMounted) { - nfc->CloseAmiibo(); + auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); + + // Remove amiibo if one is connected + if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { + virtual_amiibo->CloseAmiibo(); QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); return; } - if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { + if (virtual_amiibo->GetCurrentState() != InputCommon::VirtualAmiibo::State::WaitingForAmiibo) { QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); return; } @@ -3244,24 +3239,30 @@ void GMainWindow::OnLoadAmiibo() { } void GMainWindow::LoadAmiibo(const QString& filename) { - Service::SM::ServiceManager& sm = system->ServiceManager(); - auto nfc = sm.GetService("nfp:user"); - if (nfc == nullptr) { - return; - } - + auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); + const QString title = tr("Error loading Amiibo data"); // Remove amiibo if one is connected - const auto nfc_state = nfc->GetCurrentState(); - if (nfc_state == Service::NFP::DeviceState::TagFound || - nfc_state == Service::NFP::DeviceState::TagMounted) { - nfc->CloseAmiibo(); + if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { + virtual_amiibo->CloseAmiibo(); QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); return; } - if (!nfc->LoadAmiibo(filename.toStdString())) { - QMessageBox::warning(this, tr("Error loading Amiibo data"), - tr("Unable to load Amiibo data.")); + switch (virtual_amiibo->LoadAmiibo(filename.toStdString())) { + case InputCommon::VirtualAmiibo::Info::NotAnAmiibo: + QMessageBox::warning(this, title, tr("The selected file is not a valid amiibo")); + break; + case InputCommon::VirtualAmiibo::Info::UnableToLoad: + QMessageBox::warning(this, title, tr("The selected file is already on use")); + break; + case InputCommon::VirtualAmiibo::Info::WrongDeviceState: + QMessageBox::warning(this, title, tr("The current game is not looking for amiibos")); + break; + case InputCommon::VirtualAmiibo::Info::Unknown: + QMessageBox::warning(this, title, tr("An unkown error occured")); + break; + default: + break; } } From 8a3d22c4bd54c18edb51fe6513761ec2189e3369 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 20:36:40 -0500 Subject: [PATCH 029/245] core: hid: Add nfc support to emulated controller --- src/core/hid/emulated_controller.cpp | 70 ++++++++++++++++++++++++++++ src/core/hid/emulated_controller.h | 34 ++++++++++++-- src/core/hid/input_converter.cpp | 14 ++++++ src/core/hid/input_converter.h | 8 ++++ 4 files changed, 123 insertions(+), 3 deletions(-) diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 01c43be934..142c39003c 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -131,13 +131,16 @@ void EmulatedController::LoadDevices() { battery_params[RightIndex].Set("battery", true); camera_params = Common::ParamPackage{"engine:camera,camera:1"}; + nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; output_params[LeftIndex] = left_joycon; output_params[RightIndex] = right_joycon; output_params[2] = camera_params; + output_params[3] = nfc_params; output_params[LeftIndex].Set("output", true); output_params[RightIndex].Set("output", true); output_params[2].Set("output", true); + output_params[3].Set("output", true); LoadTASParams(); @@ -155,6 +158,7 @@ void EmulatedController::LoadDevices() { std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), Common::Input::CreateDevice); camera_devices = Common::Input::CreateDevice(camera_params); + nfc_devices = Common::Input::CreateDevice(nfc_params); std::transform(output_params.begin(), output_params.end(), output_devices.begin(), Common::Input::CreateDevice); @@ -284,6 +288,16 @@ void EmulatedController::ReloadInput() { camera_devices->ForceUpdate(); } + if (nfc_devices) { + if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { + nfc_devices->SetCallback({ + .on_change = + [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, + }); + nfc_devices->ForceUpdate(); + } + } + // Use a common UUID for TAS static constexpr Common::UUID TAS_UUID = Common::UUID{ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; @@ -339,6 +353,8 @@ void EmulatedController::UnloadInput() { for (auto& stick : tas_stick_devices) { stick.reset(); } + camera_devices.reset(); + nfc_devices.reset(); } void EmulatedController::EnableConfiguration() { @@ -903,6 +919,25 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback TriggerOnChange(ControllerTriggerType::IrSensor, true); } +void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { + std::unique_lock lock{mutex}; + controller.nfc_values = TransformToNfc(callback); + + if (is_configuring) { + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Nfc, false); + return; + } + + controller.nfc_state = { + controller.nfc_values.state, + controller.nfc_values.data, + }; + + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Nfc, true); +} + bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { if (device_index >= output_devices.size()) { return false; @@ -980,6 +1015,10 @@ bool EmulatedController::TestVibration(std::size_t device_index) { bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); auto& output_device = output_devices[static_cast(DeviceIndex::Right)]; + auto& nfc_output_device = output_devices[3]; + + nfc_output_device->SetPollingMode(polling_mode); + return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; } @@ -1000,6 +1039,32 @@ bool EmulatedController::SetCameraFormat( camera_format)) == Common::Input::CameraError::None; } +bool EmulatedController::HasNfc() const { + const auto& nfc_output_device = output_devices[3]; + + switch (npad_type) { + case NpadStyleIndex::JoyconRight: + case NpadStyleIndex::JoyconDual: + case NpadStyleIndex::ProController: + break; + default: + return false; + } + + const bool has_virtual_nfc = + npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; + const bool is_virtual_nfc_supported = + nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; + + return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); +} + +bool EmulatedController::WriteNfc(const std::vector& data) { + auto& nfc_output_device = output_devices[3]; + + return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; +} + void EmulatedController::SetLedPattern() { for (auto& device : output_devices) { if (!device) { @@ -1363,6 +1428,11 @@ const CameraState& EmulatedController::GetCamera() const { return controller.camera_state; } +const NfcState& EmulatedController::GetNfc() const { + std::scoped_lock lock{mutex}; + return controller.nfc_state; +} + NpadColor EmulatedController::GetNpadColor(u32 color) { return { .r = static_cast((color >> 16) & 0xFF), diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index c3aa8f9d37..319226bf82 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -20,7 +20,7 @@ namespace Core::HID { const std::size_t max_emulated_controllers = 2; -const std::size_t output_devices = 3; +const std::size_t output_devices_size = 4; struct ControllerMotionInfo { Common::Input::MotionStatus raw_status{}; MotionInput emulated{}; @@ -37,7 +37,8 @@ using TriggerDevices = using BatteryDevices = std::array, max_emulated_controllers>; using CameraDevices = std::unique_ptr; -using OutputDevices = std::array, output_devices>; +using NfcDevices = std::unique_ptr; +using OutputDevices = std::array, output_devices_size>; using ButtonParams = std::array; using StickParams = std::array; @@ -45,7 +46,8 @@ using ControllerMotionParams = std::array; using BatteryParams = std::array; using CameraParams = Common::ParamPackage; -using OutputParams = std::array; +using NfcParams = Common::ParamPackage; +using OutputParams = std::array; using ButtonValues = std::array; using SticksValues = std::array; @@ -55,6 +57,7 @@ using ControllerMotionValues = std::array; using BatteryValues = std::array; using CameraValues = Common::Input::CameraStatus; +using NfcValues = Common::Input::NfcStatus; using VibrationValues = std::array; struct AnalogSticks { @@ -80,6 +83,11 @@ struct CameraState { std::size_t sample{}; }; +struct NfcState { + Common::Input::NfcState state{}; + std::vector data{}; +}; + struct ControllerMotion { Common::Vec3f accel{}; Common::Vec3f gyro{}; @@ -107,6 +115,7 @@ struct ControllerStatus { BatteryValues battery_values{}; VibrationValues vibration_values{}; CameraValues camera_values{}; + NfcValues nfc_values{}; // Data for HID serices HomeButtonState home_button_state{}; @@ -119,6 +128,7 @@ struct ControllerStatus { ControllerColors colors_state{}; BatteryLevelState battery_state{}; CameraState camera_state{}; + NfcState nfc_state{}; }; enum class ControllerTriggerType { @@ -130,6 +140,7 @@ enum class ControllerTriggerType { Battery, Vibration, IrSensor, + Nfc, Connected, Disconnected, Type, @@ -315,6 +326,9 @@ public: /// Returns the latest camera status from the controller const CameraState& GetCamera() const; + /// Returns the latest ntag status from the controller + const NfcState& GetNfc() const; + /** * Sends a specific vibration to the output device * @return true if vibration had no errors @@ -341,6 +355,12 @@ public: */ bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); + /// Returns true if the device has nfc support + bool HasNfc() const; + + /// Returns true if the nfc tag was written + bool WriteNfc(const std::vector& data); + /// Returns the led pattern corresponding to this emulated controller LedPattern GetLedPattern() const; @@ -424,6 +444,12 @@ private: */ void SetCamera(const Common::Input::CallbackStatus& callback); + /** + * Updates the nfc status of the controller + * @param callback A CallbackStatus containing the nfc status + */ + void SetNfc(const Common::Input::CallbackStatus& callback); + /** * Converts a color format from bgra to rgba * @param color in bgra format @@ -458,6 +484,7 @@ private: TriggerParams trigger_params; BatteryParams battery_params; CameraParams camera_params; + NfcParams nfc_params; OutputParams output_params; ButtonDevices button_devices; @@ -466,6 +493,7 @@ private: TriggerDevices trigger_devices; BatteryDevices battery_devices; CameraDevices camera_devices; + NfcDevices nfc_devices; OutputDevices output_devices; // TAS related variables diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 52fb69e9c8..e7b871accb 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -287,6 +287,20 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu return camera; } +Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { + Common::Input::NfcStatus nfc{}; + switch (callback.type) { + case Common::Input::InputType::Nfc: + nfc = callback.nfc_status; + break; + default: + LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); + break; + } + + return nfc; +} + void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { const auto& properties = analog.properties; float& raw_value = analog.raw_value; diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h index 143c50cc06..b7eb6e660c 100644 --- a/src/core/hid/input_converter.h +++ b/src/core/hid/input_converter.h @@ -84,6 +84,14 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu */ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); +/** + * Converts raw input data into a valid nfc status. + * + * @param callback Supported callbacks: Nfc. + * @return A valid CameraObject object. + */ +Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); + /** * Converts raw analog data into a valid analog value * @param analog An analog object containing raw data and properties From afea5c163fd2bd4b99753f6780c256c7280052a8 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 21:28:06 -0500 Subject: [PATCH 030/245] service: nfp: Rewrite and implement applet calls --- src/core/CMakeLists.txt | 5 +- src/core/hle/service/mii/mii_manager.cpp | 80 +- src/core/hle/service/mii/mii_manager.h | 3 +- src/core/hle/service/nfp/amiibo_crypto.cpp | 12 +- src/core/hle/service/nfp/amiibo_crypto.h | 4 +- src/core/hle/service/nfp/nfp.cpp | 1097 +---------------- src/core/hle/service/nfp/nfp.h | 161 --- src/core/hle/service/nfp/nfp_device.cpp | 572 +++++++++ src/core/hle/service/nfp/nfp_device.h | 97 ++ src/core/hle/service/nfp/nfp_result.h | 21 + .../nfp/{amiibo_types.h => nfp_types.h} | 79 +- src/core/hle/service/nfp/nfp_user.cpp | 634 +++++++++- src/core/hle/service/nfp/nfp_user.h | 44 +- 13 files changed, 1544 insertions(+), 1265 deletions(-) create mode 100644 src/core/hle/service/nfp/nfp_device.cpp create mode 100644 src/core/hle/service/nfp/nfp_device.h create mode 100644 src/core/hle/service/nfp/nfp_result.h rename src/core/hle/service/nfp/{amiibo_types.h => nfp_types.h} (73%) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c176623234..7fd2d0276c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -523,9 +523,12 @@ add_library(core STATIC hle/service/nfc/nfc.h hle/service/nfp/amiibo_crypto.cpp hle/service/nfp/amiibo_crypto.h - hle/service/nfp/amiibo_types.h hle/service/nfp/nfp.cpp hle/service/nfp/nfp.h + hle/service/nfp/nfp_device.cpp + hle/service/nfp/nfp_device.h + hle/service/nfp/nfp_result.h + hle/service/nfp/nfp_types.h hle/service/nfp/nfp_user.cpp hle/service/nfp/nfp_user.h hle/service/ngct/ngct.cpp diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index c484a9c8de..3e92152ec0 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -427,12 +427,12 @@ CharInfo MiiManager::BuildDefault(std::size_t index) { return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); } -CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { +CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { Service::Mii::MiiManager manager; auto mii = manager.BuildDefault(0); // Check if mii data exist - if (mii_v3.mii_name[0] == 0) { + if (mii_v3.version == 0) { return mii; } @@ -443,8 +443,8 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { mii.height = mii_v3.height; mii.build = mii_v3.build; - memset(mii.name.data(), 0, sizeof(mii.name)); - memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); + memset(mii.name.data(), 0, mii.name.size()); + memcpy(mii.name.data(), mii_v3.mii_name.data(), mii_v3.mii_name.size()); mii.font_region = mii_v3.region_information.character_set; mii.faceline_type = mii_v3.appearance_bits1.face_shape; @@ -504,6 +504,78 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { return mii; } +Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { + Service::Mii::MiiManager manager; + Ver3StoreData mii_v3{}; + + // TODO: We are ignoring a bunch of data from the mii_v3 + + mii_v3.version = 1; + mii_v3.mii_information.gender.Assign(mii.gender); + mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); + mii_v3.height = mii.height; + mii_v3.build = mii.build; + + memcpy(mii_v3.mii_name.data(), mii.name.data(), mii.name.size()); + mii_v3.region_information.character_set.Assign(mii.font_region); + + mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); + mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color); + mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); + mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); + + mii_v3.hair_style = mii.hair_type; + mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color); + mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); + + mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); + mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color); + mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); + mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); + mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); + mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); + mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); + + mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); + mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color); + mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); + mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); + mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); + mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); + mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); + + mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); + mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); + mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); + + mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); + mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color); + mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); + mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); + mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); + + mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); + mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); + mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); + + mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); + mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color); + + mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type); + mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color); + mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); + mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); + + mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); + mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); + mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); + mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); + + // TODO: Validate mii_v3 data + + return mii_v3; +} + ResultVal> MiiManager::GetDefault(SourceFlag source_flag) { std::vector result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index d847de0bda..b68fdd54c0 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -22,7 +22,8 @@ public: ResultVal UpdateLatest(const CharInfo& info, SourceFlag source_flag); CharInfo BuildRandom(Age age, Gender gender, Race race); CharInfo BuildDefault(std::size_t index); - CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const; + CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; + Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const; ResultVal> GetDefault(SourceFlag source_flag); Result GetIndex(const CharInfo& info, u32& index); diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index 31dd3a307f..c87da5ae42 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -20,13 +20,13 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { const auto& amiibo_data = ntag_file.user_memory; LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); - LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter); + LOG_DEBUG(Service_NFP, "write_count={}", static_cast(amiibo_data.write_counter)); - LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); - LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); - LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); - LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); - LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); + LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); + LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); + LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); + LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); + LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index af7335912f..e3fa3d07e4 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -5,7 +5,7 @@ #include -#include "core/hle/service/nfp/amiibo_types.h" +#include "core/hle/service/nfp/nfp_types.h" struct mbedtls_md_context_t; @@ -22,7 +22,7 @@ using HmacKey = std::array; using DrgbOutput = std::array; struct HashSeed { - u16 magic; + u16_be magic; std::array padding; std::array uuid1; std::array uuid2; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 037b866536..0cb55ca49c 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -1,1098 +1,43 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - -#include "common/fs/file.h" -#include "common/fs/path_util.h" #include "common/logging/log.h" -#include "common/string_util.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/amiibo_crypto.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -namespace ErrCodes { -constexpr Result DeviceNotFound(ErrorModule::NFP, 64); -constexpr Result WrongDeviceState(ErrorModule::NFP, 73); -constexpr Result NfcDisabled(ErrorModule::NFP, 80); -constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); -constexpr Result TagRemoved(ErrorModule::NFP, 97); -constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); -constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); -constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); -} // namespace ErrCodes -IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) - : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, - nfp_interface{nfp_interface_} { - static const FunctionInfo functions[] = { - {0, &IUser::Initialize, "Initialize"}, - {1, &IUser::Finalize, "Finalize"}, - {2, &IUser::ListDevices, "ListDevices"}, - {3, &IUser::StartDetection, "StartDetection"}, - {4, &IUser::StopDetection, "StopDetection"}, - {5, &IUser::Mount, "Mount"}, - {6, &IUser::Unmount, "Unmount"}, - {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, - {8, &IUser::GetApplicationArea, "GetApplicationArea"}, - {9, &IUser::SetApplicationArea, "SetApplicationArea"}, - {10, &IUser::Flush, "Flush"}, - {11, nullptr, "Restore"}, - {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, - {13, &IUser::GetTagInfo, "GetTagInfo"}, - {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, - {15, &IUser::GetCommonInfo, "GetCommonInfo"}, - {16, &IUser::GetModelInfo, "GetModelInfo"}, - {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, - {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, - {19, &IUser::GetState, "GetState"}, - {20, &IUser::GetDeviceState, "GetDeviceState"}, - {21, &IUser::GetNpadId, "GetNpadId"}, - {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, - {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, - }; - RegisterHandlers(functions); - - availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); -} - -void IUser::Initialize(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::Initialized; - - // TODO(german77): Loop through all interfaces - nfp_interface.Initialize(); - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); -} - -void IUser::Finalize(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::NonInitialized; - - // TODO(german77): Loop through all interfaces - nfp_interface.Finalize(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IUser::ListDevices(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - std::vector devices; - - // TODO(german77): Loop through all interfaces - devices.push_back(nfp_interface.GetHandle()); - - if (devices.size() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); - return; - } - - ctx.WriteBuffer(devices); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(devices.size())); -} - -void IUser::StartDetection(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto nfp_protocol{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.StartDetection(nfp_protocol); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::StopDetection(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.StopDetection(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Mount(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto model_type{rp.PopEnum()}; - const auto mount_target{rp.PopEnum()}; - LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, - model_type, mount_target); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.Mount(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Unmount(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.Unmount(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto access_id{rp.Pop()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, - access_id); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.OpenApplicationArea(access_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - ApplicationArea data{}; - const auto result = nfp_interface.GetApplicationArea(data); - ctx.WriteBuffer(data); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(static_cast(data.size())); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, - data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.SetApplicationArea(data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Flush(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.Flush(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto access_id{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", - device_handle, access_id, data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.CreateApplicationArea(access_id, data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - TagInfo tag_info{}; - const auto result = nfp_interface.GetTagInfo(tag_info); - ctx.WriteBuffer(tag_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - RegisterInfo register_info{}; - const auto result = nfp_interface.GetRegisterInfo(register_info); - ctx.WriteBuffer(register_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - CommonInfo common_info{}; - const auto result = nfp_interface.GetCommonInfo(common_info); - ctx.WriteBuffer(common_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - ModelInfo model_info{}; - const auto result = nfp_interface.GetModelInfo(model_info); - ctx.WriteBuffer(model_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(nfp_interface.GetActivateEvent()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(nfp_interface.GetDeactivateEvent()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3, 0}; - rb.Push(ResultSuccess); - rb.PushEnum(state); -} - -void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nfp_interface.GetCurrentState()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nfp_interface.GetNpadId()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(sizeof(ApplicationArea)); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "(STUBBED) called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); -} - -void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto access_id{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", - device_handle, access_id, data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.RecreateApplicationArea(access_id, data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -Module::Interface::Interface(std::shared_ptr module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)}, - npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} { - activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); - deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); -} - -Module::Interface::~Interface() = default; - -void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(*this, system); -} - -bool Module::Interface::LoadAmiiboFile(const std::string& filename) { - constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); - const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - - if (!amiibo_file.IsOpen()) { - LOG_ERROR(Service_NFP, "Amiibo is already on use"); - return false; - } - - // Workaround for files with missing password data - std::array buffer{}; - if (amiibo_file.Read(buffer) < tag_size_without_password) { - LOG_ERROR(Service_NFP, "Failed to read amiibo file"); - return false; - } - memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); - - if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { - LOG_INFO(Service_NFP, "Invalid amiibo"); - return false; - } - - file_path = filename; - return true; -} - -bool Module::Interface::LoadAmiibo(const std::string& filename) { - if (device_state != DeviceState::SearchingForTag) { - LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); - return false; - } - - if (!LoadAmiiboFile(filename)) { - return false; - } - - device_state = DeviceState::TagFound; - activate_event->GetWritableEvent().Signal(); - return true; -} - -void Module::Interface::CloseAmiibo() { - LOG_INFO(Service_NFP, "Remove amiibo"); - device_state = DeviceState::TagRemoved; - is_data_decoded = false; - is_application_area_initialized = false; - encrypted_tag_data = {}; - tag_data = {}; - deactivate_event->GetWritableEvent().Signal(); -} - -Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { - return activate_event->GetReadableEvent(); -} - -Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { - return deactivate_event->GetReadableEvent(); -} - -void Module::Interface::Initialize() { - device_state = DeviceState::Initialized; - is_data_decoded = false; - is_application_area_initialized = false; - encrypted_tag_data = {}; - tag_data = {}; -} - -void Module::Interface::Finalize() { - if (device_state == DeviceState::TagMounted) { - Unmount(); - } - if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { - StopDetection(); - } - device_state = DeviceState::Unaviable; -} - -Result Module::Interface::StartDetection(s32 protocol_) { - auto npad_device = system.HIDCore().GetEmulatedController(npad_id); - - // TODO(german77): Add callback for when nfc data is available - - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { - npad_device->SetPollingMode(Common::Input::PollingMode::NFC); - device_state = DeviceState::SearchingForTag; - protocol = protocol_; - return ResultSuccess; - } - - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; -} - -Result Module::Interface::StopDetection() { - auto npad_device = system.HIDCore().GetEmulatedController(npad_id); - npad_device->SetPollingMode(Common::Input::PollingMode::Active); - - if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { - CloseAmiibo(); - return ResultSuccess; - } - if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { - device_state = DeviceState::Initialized; - return ResultSuccess; - } - - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; -} - -Result Module::Interface::Flush() { - // Ignore write command if we can't encrypt the data - if (!is_data_decoded) { - return ResultSuccess; - } - - constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); - EncryptedNTAG215File tmp_encrypted_tag_data{}; - const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, - Common::FS::FileType::BinaryFile}; - - if (!amiibo_file.IsOpen()) { - LOG_ERROR(Core, "Amiibo is already on use"); - return ErrCodes::WriteAmiiboFailed; - } - - // Workaround for files with missing password data - std::array buffer{}; - if (amiibo_file.Read(buffer) < tag_size_without_password) { - LOG_ERROR(Core, "Failed to read amiibo file"); - return ErrCodes::WriteAmiiboFailed; - } - memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); - - if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) { - LOG_INFO(Service_NFP, "Invalid amiibo"); - return ErrCodes::WriteAmiiboFailed; - } - - bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0; - bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id == - tag_data.model_info.character_id; - if (!is_uuid_equal || !is_character_equal) { - LOG_ERROR(Service_NFP, "Not the same amiibo"); - return ErrCodes::WriteAmiiboFailed; - } - - if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { - LOG_ERROR(Service_NFP, "Failed to encode data"); - return ErrCodes::WriteAmiiboFailed; - } - - // Return to the start of the file - if (!amiibo_file.Seek(0)) { - LOG_ERROR(Service_NFP, "Error writing to file"); - return ErrCodes::WriteAmiiboFailed; - } - - if (!amiibo_file.Write(encrypted_tag_data)) { - LOG_ERROR(Service_NFP, "Error writing to file"); - return ErrCodes::WriteAmiiboFailed; - } - - return ResultSuccess; -} - -Result Module::Interface::Mount() { - if (device_state != DeviceState::TagFound) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data); - LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); - - is_application_area_initialized = false; - device_state = DeviceState::TagMounted; - return ResultSuccess; -} - -Result Module::Interface::Unmount() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - is_data_decoded = false; - is_application_area_initialized = false; - device_state = DeviceState::TagFound; - return ResultSuccess; -} - -Result Module::Interface::GetTagInfo(TagInfo& tag_info) const { - if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - tag_info = { - .uuid = encrypted_tag_data.uuid, - .uuid_length = static_cast(encrypted_tag_data.uuid.size()), - .protocol = protocol, - .tag_type = static_cast(encrypted_tag_data.user_memory.model_info.amiibo_type), - }; - - return ResultSuccess; -} - -Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { - const auto& settings = tag_data.settings; - // TODO: Validate this data - common_info = { - .last_write_year = settings.write_date.GetYear(), - .last_write_month = settings.write_date.GetMonth(), - .last_write_day = settings.write_date.GetDay(), - .write_counter = settings.crc_counter, - .version = 1, - .application_area_size = sizeof(ApplicationArea), +class IUserManager final : public ServiceFramework { +public: + explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IUserManager::CreateUserInterface, "CreateUserInterface"}, }; - return ResultSuccess; + // clang-format on + + RegisterHandlers(functions); } - // Generate a generic answer - common_info = { - .last_write_year = 2022, - .last_write_month = 2, - .last_write_day = 7, - .write_counter = 0, - .version = 1, - .application_area_size = sizeof(ApplicationArea), - }; - return ResultSuccess; -} +private: + void CreateUserInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); -Result Module::Interface::GetModelInfo(ModelInfo& model_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - const auto& model_info_data = encrypted_tag_data.user_memory.model_info; - model_info = { - .character_id = model_info_data.character_id, - .character_variant = model_info_data.character_variant, - .amiibo_type = model_info_data.amiibo_type, - .model_number = model_info_data.model_number, - .series = model_info_data.series, - .constant_value = model_info_data.constant_value, - }; - return ResultSuccess; -} - -Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; + if (user_interface == nullptr) { + user_interface = std::make_shared(system); } - return ErrCodes::WrongDeviceState; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(user_interface); } - Service::Mii::MiiManager manager; - - if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { - const auto& settings = tag_data.settings; - - // TODO: Validate this data - register_info = { - .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), - .first_write_year = settings.init_date.GetYear(), - .first_write_month = settings.init_date.GetMonth(), - .first_write_day = settings.init_date.GetDay(), - .amiibo_name = GetAmiiboName(settings), - .font_region = {}, - }; - - return ResultSuccess; - } - - // Generate a generic answer - register_info = { - .mii_char_info = manager.BuildDefault(0), - .first_write_year = 2022, - .first_write_month = 2, - .first_write_day = 7, - .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0}, - .font_region = {}, - }; - return ResultSuccess; -} - -Result Module::Interface::OpenApplicationArea(u32 access_id) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - // Fallback for lack of amiibo keys - if (!is_data_decoded) { - LOG_WARNING(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - if (tag_data.settings.settings.appdata_initialized == 0) { - LOG_WARNING(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - if (tag_data.application_area_id != access_id) { - LOG_WARNING(Service_NFP, "Wrong application area id"); - return ErrCodes::WrongApplicationAreaId; - } - - is_application_area_initialized = true; - return ResultSuccess; -} - -Result Module::Interface::GetApplicationArea(ApplicationArea& data) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (!is_application_area_initialized) { - LOG_ERROR(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - data = tag_data.application_area; - - return ResultSuccess; -} - -Result Module::Interface::SetApplicationArea(const std::vector& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (!is_application_area_initialized) { - LOG_ERROR(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - if (data.size() != sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); - return ResultSuccess; -} - -Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized != 0) { - LOG_ERROR(Service_NFP, "Application area already exist"); - return ErrCodes::ApplicationAreaExist; - } - - if (data.size() != sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); - tag_data.application_area_id = access_id; - - return ResultSuccess; -} - -Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (data.size() != sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); - tag_data.application_area_id = access_id; - - return ResultSuccess; -} - -u64 Module::Interface::GetHandle() const { - // Generate a handle based of the npad id - return static_cast(npad_id); -} - -DeviceState Module::Interface::GetCurrentState() const { - return device_state; -} - -Core::HID::NpadIdType Module::Interface::GetNpadId() const { - // Return first connected npad id as a workaround for lack of a single nfc interface per - // controller - return system.HIDCore().GetFirstNpadId(); -} - -AmiiboName Module::Interface::GetAmiiboName(const AmiiboSettings& settings) const { - std::array settings_amiibo_name{}; - AmiiboName amiibo_name{}; - - // Convert from big endian to little endian - for (std::size_t i = 0; i < amiibo_name_length; i++) { - settings_amiibo_name[i] = static_cast(settings.amiibo_name[i]); - } - - // Convert from utf16 to utf8 - const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); - memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); - - return amiibo_name; -} + std::shared_ptr user_interface; +}; void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - auto module = std::make_shared(); - std::make_shared(module, system)->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 0de0b48e71..a25c362b8a 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -3,170 +3,9 @@ #pragma once -#include -#include - -#include "common/common_funcs.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/mii/types.h" -#include "core/hle/service/nfp/amiibo_types.h" #include "core/hle/service/service.h" -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Core::HID { -enum class NpadIdType : u32; -} // namespace Core::HID - namespace Service::NFP { -using AmiiboName = std::array; - -struct TagInfo { - TagUuid uuid; - u8 uuid_length; - INSERT_PADDING_BYTES(0x15); - s32 protocol; - u32 tag_type; - INSERT_PADDING_BYTES(0x30); -}; -static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); - -struct CommonInfo { - u16 last_write_year; - u8 last_write_month; - u8 last_write_day; - u16 write_counter; - u16 version; - u32 application_area_size; - INSERT_PADDING_BYTES(0x34); -}; -static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); - -struct ModelInfo { - u16 character_id; - u8 character_variant; - AmiiboType amiibo_type; - u16 model_number; - AmiiboSeries series; - u8 constant_value; // Must be 02 - INSERT_PADDING_BYTES(0x38); // Unknown -}; -static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); - -struct RegisterInfo { - Service::Mii::CharInfo mii_char_info; - u16 first_write_year; - u8 first_write_month; - u8 first_write_day; - AmiiboName amiibo_name; - u8 font_region; - INSERT_PADDING_BYTES(0x7A); -}; -static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); - -class Module final { -public: - class Interface : public ServiceFramework { - public: - explicit Interface(std::shared_ptr module_, Core::System& system_, - const char* name); - ~Interface() override; - - void CreateUserInterface(Kernel::HLERequestContext& ctx); - bool LoadAmiibo(const std::string& filename); - bool LoadAmiiboFile(const std::string& filename); - void CloseAmiibo(); - - void Initialize(); - void Finalize(); - - Result StartDetection(s32 protocol_); - Result StopDetection(); - Result Mount(); - Result Unmount(); - Result Flush(); - - Result GetTagInfo(TagInfo& tag_info) const; - Result GetCommonInfo(CommonInfo& common_info) const; - Result GetModelInfo(ModelInfo& model_info) const; - Result GetRegisterInfo(RegisterInfo& register_info) const; - - Result OpenApplicationArea(u32 access_id); - Result GetApplicationArea(ApplicationArea& data) const; - Result SetApplicationArea(const std::vector& data); - Result CreateApplicationArea(u32 access_id, const std::vector& data); - Result RecreateApplicationArea(u32 access_id, const std::vector& data); - - u64 GetHandle() const; - DeviceState GetCurrentState() const; - Core::HID::NpadIdType GetNpadId() const; - - Kernel::KReadableEvent& GetActivateEvent() const; - Kernel::KReadableEvent& GetDeactivateEvent() const; - - protected: - std::shared_ptr module; - - private: - AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; - - const Core::HID::NpadIdType npad_id; - - bool is_data_decoded{}; - bool is_application_area_initialized{}; - s32 protocol; - std::string file_path{}; - Kernel::KEvent* activate_event; - Kernel::KEvent* deactivate_event; - DeviceState device_state{DeviceState::Unaviable}; - KernelHelpers::ServiceContext service_context; - - NTAG215File tag_data{}; - EncryptedNTAG215File encrypted_tag_data{}; - }; -}; - -class IUser final : public ServiceFramework { -public: - explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); - -private: - void Initialize(Kernel::HLERequestContext& ctx); - void Finalize(Kernel::HLERequestContext& ctx); - void ListDevices(Kernel::HLERequestContext& ctx); - void StartDetection(Kernel::HLERequestContext& ctx); - void StopDetection(Kernel::HLERequestContext& ctx); - void Mount(Kernel::HLERequestContext& ctx); - void Unmount(Kernel::HLERequestContext& ctx); - void OpenApplicationArea(Kernel::HLERequestContext& ctx); - void GetApplicationArea(Kernel::HLERequestContext& ctx); - void SetApplicationArea(Kernel::HLERequestContext& ctx); - void Flush(Kernel::HLERequestContext& ctx); - void CreateApplicationArea(Kernel::HLERequestContext& ctx); - void GetTagInfo(Kernel::HLERequestContext& ctx); - void GetRegisterInfo(Kernel::HLERequestContext& ctx); - void GetCommonInfo(Kernel::HLERequestContext& ctx); - void GetModelInfo(Kernel::HLERequestContext& ctx); - void AttachActivateEvent(Kernel::HLERequestContext& ctx); - void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); - void GetState(Kernel::HLERequestContext& ctx); - void GetDeviceState(Kernel::HLERequestContext& ctx); - void GetNpadId(Kernel::HLERequestContext& ctx); - void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); - void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); - void RecreateApplicationArea(Kernel::HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; - - // TODO(german77): We should have a vector of interfaces - Module::Interface& nfp_interface; - - State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; -}; void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp new file mode 100644 index 0000000000..da7daae882 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -0,0 +1,572 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/input.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/tiny_mt.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/amiibo_crypto.h" +#include "core/hle/service/nfp/nfp.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" + +namespace Service::NFP { +NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_) + : npad_id{npad_id_}, system{system_}, service_context{service_context_}, + availability_change_event{availability_change_event_} { + activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); + deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); + npad_device = system.HIDCore().GetEmulatedController(npad_id); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, + .is_npad_service = false, + }; + is_controller_set = true; + callback_key = npad_device->SetCallback(engine_callback); +} + +NfpDevice::~NfpDevice() { + if (is_controller_set) { + npad_device->DeleteCallback(callback_key); + is_controller_set = false; + } +}; + +void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Connected || + type == Core::HID::ControllerTriggerType::Disconnected) { + availability_change_event->GetWritableEvent().Signal(); + return; + } + + if (type != Core::HID::ControllerTriggerType::Nfc) { + return; + } + + if (!npad_device->IsConnected()) { + return; + } + + const auto nfc_status = npad_device->GetNfc(); + switch (nfc_status.state) { + case Common::Input::NfcState::NewAmiibo: + LoadAmiibo(nfc_status.data); + break; + case Common::Input::NfcState::AmiiboRemoved: + if (device_state != DeviceState::SearchingForTag) { + CloseAmiibo(); + } + break; + default: + break; + } +} + +bool NfpDevice::LoadAmiibo(const std::vector& data) { + if (device_state != DeviceState::SearchingForTag) { + LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); + return false; + } + + if (data.size() != sizeof(EncryptedNTAG215File)) { + LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); + return false; + } + + memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); + + if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { + LOG_INFO(Service_NFP, "Invalid amiibo"); + return false; + } + + device_state = DeviceState::TagFound; + activate_event->GetWritableEvent().Signal(); + return true; +} + +void NfpDevice::CloseAmiibo() { + LOG_INFO(Service_NFP, "Remove amiibo"); + + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + + device_state = DeviceState::TagRemoved; + encrypted_tag_data = {}; + tag_data = {}; + deactivate_event->GetWritableEvent().Signal(); +} + +Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { + return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { + return deactivate_event->GetReadableEvent(); +} + +void NfpDevice::Initialize() { + device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; + encrypted_tag_data = {}; + tag_data = {}; +} + +void NfpDevice::Finalize() { + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + StopDetection(); + } + device_state = DeviceState::Unavailable; +} + +Result NfpDevice::StartDetection(s32 protocol_) { + // TODO(german77): Add callback for when nfc data is available + + if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { + npad_device->SetPollingMode(Common::Input::PollingMode::NFC); + device_state = DeviceState::SearchingForTag; + protocol = protocol_; + return ResultSuccess; + } + + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; +} + +Result NfpDevice::StopDetection() { + npad_device->SetPollingMode(Common::Input::PollingMode::Active); + + if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { + CloseAmiibo(); + return ResultSuccess; + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::Initialized; + return ResultSuccess; + } + + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; +} + +Result NfpDevice::Flush() { + auto& settings = tag_data.settings; + + if (settings.write_date.raw_date != settings.write_date.raw_date) { + // TODO: Read current system date + settings.write_date.SetYear(2022); + settings.write_date.SetMonth(9); + settings.write_date.SetDay(9); + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + } + + tag_data.write_counter++; + + if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Failed to encode data"); + return WriteAmiiboFailed; + } + + std::vector data(sizeof(encrypted_tag_data)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + + if (!npad_device->WriteNfc(data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return WriteAmiiboFailed; + } + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfpDevice::Mount() { + if (device_state != DeviceState::TagFound) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); + return CorruptedData; + } + + device_state = DeviceState::TagMounted; + return ResultSuccess; +} + +Result NfpDevice::Unmount() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + // Save data before unloading the amiibo + if (is_data_moddified) { + Flush(); + } + + device_state = DeviceState::TagFound; + return ResultSuccess; +} + +Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + tag_info = { + .uuid = encrypted_tag_data.uuid, + .uuid_length = static_cast(encrypted_tag_data.uuid.size()), + .protocol = protocol, + .tag_type = static_cast(encrypted_tag_data.user_memory.model_info.amiibo_type), + }; + + return ResultSuccess; +} + +Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + const auto& settings = tag_data.settings; + const u32 application_area_size = + tag_data.settings.settings.appdata_initialized == 0 ? 0 : sizeof(ApplicationArea); + + // TODO: Validate this data + common_info = { + .last_write_date = + { + settings.write_date.GetYear(), + settings.write_date.GetMonth(), + settings.write_date.GetDay(), + }, + .write_counter = tag_data.write_counter, + .version = 1, + .application_area_size = application_area_size, + }; + return ResultSuccess; +} + +Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + const auto& model_info_data = encrypted_tag_data.user_memory.model_info; + model_info = { + .character_id = model_info_data.character_id, + .character_variant = model_info_data.character_variant, + .amiibo_type = model_info_data.amiibo_type, + .model_number = model_info_data.model_number, + .series = model_info_data.series, + }; + return ResultSuccess; +} + +Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return RegistrationIsNotInitialized; + } + + Service::Mii::MiiManager manager; + const auto& settings = tag_data.settings; + + // TODO: Validate this data + register_info = { + .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), + .creation_date = + { + settings.init_date.GetYear(), + settings.init_date.GetMonth(), + settings.init_date.GetDay(), + }, + .amiibo_name = GetAmiiboName(settings), + .font_region = {}, + }; + + return ResultSuccess; +} + +Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + Service::Mii::MiiManager manager; + auto& settings = tag_data.settings; + + // TODO: Read current system date + settings.init_date.SetYear(2022); + settings.init_date.SetMonth(9); + settings.init_date.SetDay(9); + settings.write_date.SetYear(2022); + settings.write_date.SetMonth(9); + settings.write_date.SetDay(9); + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + + SetAmiiboName(settings, amiibo_name); + tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); + settings.settings.amiibo_initialized.Assign(1); + + return Flush(); +} + +Result NfpDevice::RestoreAmiibo() { + // TODO: Load amiibo from backup on system + LOG_ERROR(Service_NFP, "Not Implemented"); + return ResultSuccess; +} + +Result NfpDevice::DeleteAllData() { + const auto result = DeleteApplicationArea(); + if (result.IsError()) { + return result; + } + + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); + tag_data.settings.settings.amiibo_initialized.Assign(0); + + return Flush(); +} + +Result NfpDevice::OpenApplicationArea(u32 access_id) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (tag_data.application_area_id != access_id) { + LOG_WARNING(Service_NFP, "Wrong application area id"); + return WrongApplicationAreaId; + } + + return ResultSuccess; +} + +Result NfpDevice::GetApplicationArea(std::vector& data) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + memcpy(data.data(), tag_data.application_area.data(), data.size()); + + return ResultSuccess; +} + +Result NfpDevice::SetApplicationArea(const std::vector& data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + + tag_data.applicaton_write_counter++; + is_data_moddified = true; + + return ResultSuccess; +} + +Result NfpDevice::CreateApplicationArea(u32 access_id, const std::vector& data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() != 0) { + LOG_ERROR(Service_NFP, "Application area already exist"); + return ApplicationAreaExist; + } + + return RecreateApplicationArea(access_id, data); +} + +Result NfpDevice::RecreateApplicationArea(u32 access_id, const std::vector& data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // HW seems to fill excess data with garbage + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + // TODO: Investigate why the title id needs to be moddified + tag_data.title_id = system.GetCurrentProcessProgramID(); + tag_data.title_id = tag_data.title_id | 0x30000000ULL; + tag_data.settings.settings.appdata_initialized.Assign(1); + tag_data.application_area_id = access_id; + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +Result NfpDevice::DeleteApplicationArea() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); + rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); + rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); + tag_data.settings.settings.appdata_initialized.Assign(0); + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +u64 NfpDevice::GetHandle() const { + // Generate a handle based of the npad id + return static_cast(npad_id); +} + +u32 NfpDevice::GetApplicationAreaSize() const { + // Investigate if this value is really constant + return sizeof(ApplicationArea); +} + +DeviceState NfpDevice::GetCurrentState() const { + return device_state; +} + +Core::HID::NpadIdType NfpDevice::GetNpadId() const { + return npad_id; +} + +AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { + std::array settings_amiibo_name{}; + AmiiboName amiibo_name{}; + + // Convert from big endian to little endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings_amiibo_name[i] = static_cast(settings.amiibo_name[i]); + } + + // Convert from utf16 to utf8 + const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); + memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); + + return amiibo_name; +} + +void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { + std::array settings_amiibo_name{}; + + // Convert from utf8 to utf16 + const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); + memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), + amiibo_name_utf16.size() * sizeof(char16_t)); + + // Convert from little endian to big endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings.amiibo_name[i] = static_cast(settings_amiibo_name[i]); + } +} + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h new file mode 100644 index 0000000000..53cc0833fc --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#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" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFP { +class NfpDevice { +public: + NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_); + ~NfpDevice(); + + void Initialize(); + void Finalize(); + + Result StartDetection(s32 protocol_); + Result StopDetection(); + Result Mount(); + Result Unmount(); + Result Flush(); + + Result GetTagInfo(TagInfo& tag_info) const; + Result GetCommonInfo(CommonInfo& common_info) const; + Result GetModelInfo(ModelInfo& model_info) const; + Result GetRegisterInfo(RegisterInfo& register_info) const; + + Result SetNicknameAndOwner(const AmiiboName& amiibo_name); + Result RestoreAmiibo(); + Result DeleteAllData(); + + Result OpenApplicationArea(u32 access_id); + Result GetApplicationArea(std::vector& data) const; + Result SetApplicationArea(const std::vector& data); + Result CreateApplicationArea(u32 access_id, const std::vector& data); + Result RecreateApplicationArea(u32 access_id, const std::vector& data); + Result DeleteApplicationArea(); + + u64 GetHandle() const; + u32 GetApplicationAreaSize() const; + DeviceState GetCurrentState() const; + Core::HID::NpadIdType GetNpadId() const; + + Kernel::KReadableEvent& GetActivateEvent() const; + Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: + void NpadUpdate(Core::HID::ControllerTriggerType type); + bool LoadAmiibo(const std::vector& data); + void CloseAmiibo(); + + AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; + void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); + + bool is_controller_set{}; + int callback_key; + const Core::HID::NpadIdType npad_id; + Core::System& system; + Core::HID::EmulatedController* npad_device = nullptr; + KernelHelpers::ServiceContext& service_context; + Kernel::KEvent* activate_event = nullptr; + Kernel::KEvent* deactivate_event = nullptr; + Kernel::KEvent* availability_change_event = nullptr; + + bool is_data_moddified{}; + s32 protocol{}; + DeviceState device_state{DeviceState::Unavailable}; + + NTAG215File tag_data{}; + EncryptedNTAG215File encrypted_tag_data{}; +}; + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h new file mode 100644 index 0000000000..15bc02b15d --- /dev/null +++ b/src/core/hle/service/nfp/nfp_result.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFP { + +constexpr Result DeviceNotFound(ErrorModule::NFP, 64); +constexpr Result WrongDeviceState(ErrorModule::NFP, 73); +constexpr Result NfcDisabled(ErrorModule::NFP, 80); +constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result TagRemoved(ErrorModule::NFP, 97); +constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result CorruptedData(ErrorModule::NFP, 144); +constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/nfp_types.h similarity index 73% rename from src/core/hle/service/nfp/amiibo_types.h rename to src/core/hle/service/nfp/nfp_types.h index bf2de811ad..d58657a216 100644 --- a/src/core/hle/service/nfp/amiibo_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -27,7 +27,7 @@ enum class DeviceState : u32 { TagFound, TagRemoved, TagMounted, - Unaviable, + Unavailable, Finalized, }; @@ -36,6 +36,7 @@ enum class ModelType : u32 { }; enum class MountTarget : u32 { + None, Rom, Ram, All, @@ -76,18 +77,36 @@ enum class AmiiboSeries : u8 { using TagUuid = std::array; using HashData = std::array; using ApplicationArea = std::array; +using AmiiboName = std::array; struct AmiiboDate { - u16 raw_date{}; + u16_be raw_date{}; + + u16 DateRaw() const { + return static_cast(raw_date); + } u16 GetYear() const { - return static_cast(((raw_date & 0xFE00) >> 9) + 2000); + return static_cast(((DateRaw() & 0xFE00) >> 9) + 2000); } u8 GetMonth() const { - return static_cast(((raw_date & 0x01E0) >> 5) - 1); + return static_cast(((DateRaw() & 0x01E0) >> 5) - 1); } u8 GetDay() const { - return static_cast(raw_date & 0x001F); + return static_cast(DateRaw() & 0x001F); + } + + void SetYear(u16 year) { + raw_date = DateRaw() & ~0xFE00; + raw_date |= static_cast((year - 2000) << 9); + } + void SetMonth(u8 month) { + raw_date = DateRaw() & ~0x01E0; + raw_date |= static_cast((month + 1) << 5); + } + void SetDay(u8 day) { + raw_date = DateRaw() & ~0x001F; + raw_date |= static_cast(day); } }; static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); @@ -134,7 +153,7 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz #pragma pack(1) struct EncryptedAmiiboFile { u8 constant_value; // Must be A5 - u16 write_counter; // Number of times the amiibo has been written? + u16_be write_counter; // Number of times the amiibo has been written? INSERT_PADDING_BYTES(0x1); // Unknown 1 AmiiboSettings settings; // Encrypted amiibo settings HashData hmac_tag; // Hash @@ -157,7 +176,7 @@ struct NTAG215File { u32 compability_container; // Defines available memory HashData hmac_data; // Hash u8 constant_value; // Must be A5 - u16 write_counter; // Number of times the amiibo has been written? + u16_be write_counter; // Number of times the amiibo has been written? INSERT_PADDING_BYTES(0x1); // Unknown 1 AmiiboSettings settings; Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data @@ -194,4 +213,50 @@ static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an static_assert(std::is_trivially_copyable_v, "EncryptedNTAG215File must be trivially copyable."); +struct TagInfo { + TagUuid uuid; + u8 uuid_length; + INSERT_PADDING_BYTES(0x15); + s32 protocol; + u32 tag_type; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); + +struct WriteDate { + u16 year; + u8 month; + u8 day; +}; +static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + +struct CommonInfo { + WriteDate last_write_date; + u16 write_counter; + u8 version; + INSERT_PADDING_BYTES(0x1); + u32 application_area_size; + INSERT_PADDING_BYTES(0x34); +}; +static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); + +struct ModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16 model_number; + AmiiboSeries series; + INSERT_PADDING_BYTES(0x39); // Unknown +}; +static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); + +struct RegisterInfo { + Service::Mii::CharInfo mii_char_info; + WriteDate creation_date; + AmiiboName amiibo_name; + u8 font_region; + INSERT_PADDING_BYTES(0x7A); +}; +static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); + } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 2d7b156cfd..f8f1975db1 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -1,18 +1,644 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +#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" namespace Service::NFP { -NFP_User::NFP_User(std::shared_ptr module_, Core::System& system_) - : Interface(std::move(module_), system_, "nfp:user") { +IUser::IUser(Core::System& system_) + : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} { static const FunctionInfo functions[] = { - {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, + {0, &IUser::Initialize, "Initialize"}, + {1, &IUser::Finalize, "Finalize"}, + {2, &IUser::ListDevices, "ListDevices"}, + {3, &IUser::StartDetection, "StartDetection"}, + {4, &IUser::StopDetection, "StopDetection"}, + {5, &IUser::Mount, "Mount"}, + {6, &IUser::Unmount, "Unmount"}, + {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, + {8, &IUser::GetApplicationArea, "GetApplicationArea"}, + {9, &IUser::SetApplicationArea, "SetApplicationArea"}, + {10, &IUser::Flush, "Flush"}, + {11, &IUser::Restore, "Restore"}, + {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, + {13, &IUser::GetTagInfo, "GetTagInfo"}, + {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, + {15, &IUser::GetCommonInfo, "GetCommonInfo"}, + {16, &IUser::GetModelInfo, "GetModelInfo"}, + {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, + {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {19, &IUser::GetState, "GetState"}, + {20, &IUser::GetDeviceState, "GetDeviceState"}, + {21, &IUser::GetNpadId, "GetNpadId"}, + {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, + {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, }; RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } } -NFP_User::~NFP_User() = default; +void IUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void IUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + std::vector nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); + + for (auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.size() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(nfp_devices.size())); +} + +void IUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto nfp_protocol{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(nfp_protocol); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Mount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto model_type{rp.PopEnum()}; + const auto mount_target{rp.PopEnum()}; + LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, + model_type, mount_target); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Mount(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Unmount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Unmount(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto access_id{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->OpenApplicationArea(access_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto data_size = ctx.GetWriteBufferSize(); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + std::vector data(data_size); + const auto result = device.value()->GetApplicationArea(data); + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(static_cast(data_size)); +} + +void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->SetApplicationArea(data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Flush(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Flush(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Restore(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RestoreAmiibo(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto access_id{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->CreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + RegisterInfo register_info{}; + const auto result = device.value()->GetRegisterInfo(register_info); + ctx.WriteBuffer(register_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + CommonInfo common_info{}; + const auto result = device.value()->GetCommonInfo(common_info); + ctx.WriteBuffer(common_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ModelInfo model_info{}; + const auto result = device.value()->GetModelInfo(model_info); + ctx.WriteBuffer(model_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void IUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3, 0}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(device.value()->GetApplicationAreaSize()); +} + +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto access_id{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RecreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +std::optional> IUser::GetNfpDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 519ff56ee2..68c60ae82e 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -3,14 +3,52 @@ #pragma once +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_types.h" namespace Service::NFP { +class NfpDevice; -class NFP_User final : public Module::Interface { +class IUser final : public ServiceFramework { public: - explicit NFP_User(std::shared_ptr module_, Core::System& system_); - ~NFP_User() override; + explicit IUser(Core::System& system_); + +private: + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void Mount(Kernel::HLERequestContext& ctx); + void Unmount(Kernel::HLERequestContext& ctx); + void OpenApplicationArea(Kernel::HLERequestContext& ctx); + void GetApplicationArea(Kernel::HLERequestContext& ctx); + void SetApplicationArea(Kernel::HLERequestContext& ctx); + void Flush(Kernel::HLERequestContext& ctx); + void Restore(Kernel::HLERequestContext& ctx); + void CreateApplicationArea(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void GetRegisterInfo(Kernel::HLERequestContext& ctx); + void GetCommonInfo(Kernel::HLERequestContext& ctx); + void GetModelInfo(Kernel::HLERequestContext& ctx); + void AttachActivateEvent(Kernel::HLERequestContext& ctx); + void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); + void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); + void RecreateApplicationArea(Kernel::HLERequestContext& ctx); + + std::optional> GetNfpDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; }; } // namespace Service::NFP From 3ce0ef04ddcb2420b61f8c6d22f8039fb7359856 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 22:52:33 -0500 Subject: [PATCH 031/245] service: nfp: address comments --- src/common/input.h | 2 +- src/core/hid/input_converter.cpp | 2 +- src/core/hle/service/nfp/nfp_device.cpp | 11 ++++----- src/core/hle/service/nfp/nfp_device.h | 4 ++-- src/core/hle/service/nfp/nfp_types.h | 25 +++++++++++---------- src/input_common/drivers/virtual_amiibo.cpp | 4 ++-- src/input_common/drivers/virtual_amiibo.h | 2 +- src/input_common/input_engine.h | 3 ++- src/input_common/input_poller.cpp | 2 +- 9 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/common/input.h b/src/common/input.h index 8365cc36ed..bfa0639f5e 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -332,7 +332,7 @@ public: return CameraError::NotSupported; } - virtual NfcState SupportsNfc() { + virtual NfcState SupportsNfc() const { return NfcState::NotSupported; } diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index e7b871accb..fe9915abe6 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -291,7 +291,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal Common::Input::NfcStatus nfc{}; switch (callback.type) { case Common::Input::InputType::Nfc: - nfc = callback.nfc_status; + return callback.nfc_status; break; default: LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index da7daae882..ce3bcccf45 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -42,10 +42,11 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, } NfpDevice::~NfpDevice() { - if (is_controller_set) { - npad_device->DeleteCallback(callback_key); - is_controller_set = false; + if (!is_controller_set) { + return; } + npad_device->DeleteCallback(callback_key); + is_controller_set = false; }; void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { @@ -453,7 +454,7 @@ Result NfpDevice::SetApplicationArea(const std::vector& data) { return ResultSuccess; } -Result NfpDevice::CreateApplicationArea(u32 access_id, const std::vector& data) { +Result NfpDevice::CreateApplicationArea(u32 access_id, std::span data) { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { @@ -470,7 +471,7 @@ Result NfpDevice::CreateApplicationArea(u32 access_id, const std::vector& da return RecreateApplicationArea(access_id, data); } -Result NfpDevice::RecreateApplicationArea(u32 access_id, const std::vector& data) { +Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span data) { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index 53cc0833fc..9ceb7b8fde 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h @@ -56,8 +56,8 @@ public: Result OpenApplicationArea(u32 access_id); Result GetApplicationArea(std::vector& data) const; Result SetApplicationArea(const std::vector& data); - Result CreateApplicationArea(u32 access_id, const std::vector& data); - Result RecreateApplicationArea(u32 access_id, const std::vector& data); + Result CreateApplicationArea(u32 access_id, std::span data); + Result RecreateApplicationArea(u32 access_id, std::span data); Result DeleteApplicationArea(); u64 GetHandle() const; diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index d58657a216..2685ae8fe5 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -5,6 +5,7 @@ #include +#include "common/swap.h" #include "core/hle/service/mii/types.h" namespace Service::NFP { @@ -80,33 +81,33 @@ using ApplicationArea = std::array; using AmiiboName = std::array; struct AmiiboDate { - u16_be raw_date{}; + u16 raw_date{}; - u16 DateRaw() const { - return static_cast(raw_date); + u16 GetValue() const { + return Common::swap16(raw_date); } u16 GetYear() const { - return static_cast(((DateRaw() & 0xFE00) >> 9) + 2000); + return static_cast(((GetValue() & 0xFE00) >> 9) + 2000); } u8 GetMonth() const { - return static_cast(((DateRaw() & 0x01E0) >> 5) - 1); + return static_cast(((GetValue() & 0x01E0) >> 5) - 1); } u8 GetDay() const { - return static_cast(DateRaw() & 0x001F); + return static_cast(GetValue() & 0x001F); } void SetYear(u16 year) { - raw_date = DateRaw() & ~0xFE00; - raw_date |= static_cast((year - 2000) << 9); + const u16 year_converted = static_cast((year - 2000) << 9); + raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); } void SetMonth(u8 month) { - raw_date = DateRaw() & ~0x01E0; - raw_date |= static_cast((month + 1) << 5); + const u16 month_converted = static_cast((month + 1) << 5); + raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); } void SetDay(u8 day) { - raw_date = DateRaw() & ~0x001F; - raw_date |= static_cast(day); + const u16 day_converted = static_cast(day); + raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); } }; static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 8fadb1322a..0cd5129da5 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -20,7 +20,7 @@ constexpr PadIdentifier identifier = { VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} -VirtualAmiibo::~VirtualAmiibo() {} +VirtualAmiibo::~VirtualAmiibo() = default; Common::Input::PollingError VirtualAmiibo::SetPollingMode( [[maybe_unused]] const PadIdentifier& identifier_, @@ -41,7 +41,7 @@ Common::Input::PollingError VirtualAmiibo::SetPollingMode( } Common::Input::NfcState VirtualAmiibo::SupportsNfc( - [[maybe_unused]] const PadIdentifier& identifier_) { + [[maybe_unused]] const PadIdentifier& identifier_) const { return Common::Input::NfcState::Success; } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 5790e4a1ff..9eac075445 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -39,7 +39,7 @@ public: Common::Input::PollingError SetPollingMode( const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; - Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) override; + Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, const std::vector& data) override; diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 9b8470c6f0..cfbdb26bd7 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -129,7 +129,8 @@ public: } // Request nfc data from a controller - virtual Common::Input::NfcState SupportsNfc([[maybe_unused]] const PadIdentifier& identifier) { + virtual Common::Input::NfcState SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier) const { return Common::Input::NfcState::NotSupported; } diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index a8eb1442b1..75705b67e7 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -768,7 +768,7 @@ public: return input_engine->SetCameraFormat(identifier, camera_format); } - Common::Input::NfcState SupportsNfc() override { + Common::Input::NfcState SupportsNfc() const override { return input_engine->SupportsNfc(identifier); } From 673de3995b7f08d51fcc281439c05c368b7bd1ae Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 26 Sep 2022 00:58:36 -0500 Subject: [PATCH 032/245] nfp: Multiple fixes against HW --- src/core/hle/service/mii/mii_manager.cpp | 68 +++++++++++++++++++++- src/core/hle/service/mii/mii_manager.h | 1 + src/core/hle/service/nfc/nfc.cpp | 8 +-- src/core/hle/service/nfp/amiibo_crypto.cpp | 55 +++++++++-------- src/core/hle/service/nfp/amiibo_crypto.h | 6 +- src/core/hle/service/nfp/nfp_device.cpp | 60 ++++++++++++------- src/core/hle/service/nfp/nfp_device.h | 2 + src/core/hle/service/nfp/nfp_result.h | 1 + src/core/hle/service/nfp/nfp_types.h | 24 +++++--- 9 files changed, 163 insertions(+), 62 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 3e92152ec0..4bc8703e19 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -431,8 +431,7 @@ CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { Service::Mii::MiiManager manager; auto mii = manager.BuildDefault(0); - // Check if mii data exist - if (mii_v3.version == 0) { + if (!ValidateV3Info(mii_v3)) { return mii; } @@ -576,6 +575,71 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { return mii_v3; } +bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { + bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; + + is_valid = is_valid && (mii_v3.mii_name[0] != 0); + + is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); + is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); + is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); + is_valid = is_valid && (mii_v3.height < 128); + is_valid = is_valid && (mii_v3.build < 128); + + is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); + is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); + is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); + is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); + + is_valid = is_valid && (mii_v3.hair_style < 132); + is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); + + is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); + + is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); + + return is_valid; +} + ResultVal> MiiManager::GetDefault(SourceFlag source_flag) { std::vector result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index b68fdd54c0..83ad3d3431 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -24,6 +24,7 @@ public: CharInfo BuildDefault(std::size_t index); CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const; + bool ValidateV3Info(const Ver3StoreData& mii_v3) const; ResultVal> GetDefault(SourceFlag source_flag); Result GetIndex(const CharInfo& info, u32& index); diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 13a843a28d..046c5f18fd 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -106,10 +106,10 @@ public: {1, &IUser::FinalizeOld, "FinalizeOld"}, {2, &IUser::GetStateOld, "GetStateOld"}, {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, nullptr, "GetState"}, - {403, nullptr, "IsNfcEnabled"}, + {400, &IUser::InitializeOld, "Initialize"}, + {401, &IUser::FinalizeOld, "Finalize"}, + {402, &IUser::GetStateOld, "GetState"}, + {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"}, {404, nullptr, "ListDevices"}, {405, nullptr, "GetDeviceState"}, {406, nullptr, "GetNpadId"}, diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index c87da5ae42..9e06970a4d 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -25,7 +25,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); - LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); + LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", + static_cast(amiibo_data.model_info.model_number)); LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); @@ -35,11 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { // Validate UUID constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` - if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) { + if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != + ntag_file.uuid.uid[3]) { return false; } - if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) != - ntag_file.uuid[8]) { + if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ + ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { return false; } @@ -70,7 +72,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { NTAG215File encoded_data{}; - memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2)); + encoded_data.uid = nfc_data.uuid.uid; + encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; encoded_data.static_lock = nfc_data.static_lock; encoded_data.compability_container = nfc_data.compability_container; encoded_data.hmac_data = nfc_data.user_memory.hmac_data; @@ -85,7 +88,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { encoded_data.hash = nfc_data.user_memory.hash; encoded_data.application_area = nfc_data.user_memory.application_area; encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; - memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid)); + encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; encoded_data.model_info = nfc_data.user_memory.model_info; encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; encoded_data.dynamic_lock = nfc_data.dynamic_lock; @@ -99,8 +102,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { EncryptedNTAG215File nfc_data{}; - memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2)); - memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid)); + nfc_data.uuid.uid = encoded_data.uid; + nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; + nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; nfc_data.static_lock = encoded_data.static_lock; nfc_data.compability_container = encoded_data.compability_container; nfc_data.user_memory.hmac_data = encoded_data.hmac_data; @@ -127,10 +131,10 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { u32 GetTagPassword(const TagUuid& uuid) { // Verifiy that the generated password is correct - u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); - password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; - password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; - password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; + u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); + password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; + password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; + password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; return password; } @@ -138,15 +142,13 @@ HashSeed GetSeed(const NTAG215File& data) { HashSeed seed{ .magic = data.write_counter, .padding = {}, - .uuid1 = {}, - .uuid2 = {}, + .uid_1 = data.uid, + .nintendo_id_1 = data.nintendo_id, + .uid_2 = data.uid, + .nintendo_id_2 = data.nintendo_id, .keygen_salt = data.keygen_salt, }; - // Copy the first 8 bytes of uuid - memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1)); - memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2)); - return seed; } @@ -165,8 +167,10 @@ std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed output.insert(output.end(), key.magic_bytes.begin(), key.magic_bytes.begin() + key.magic_length); - output.insert(output.end(), seed.uuid1.begin(), seed.uuid1.end()); - output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end()); + output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); + output.emplace_back(seed.nintendo_id_1); + output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); + output.emplace_back(seed.nintendo_id_2); for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { output.emplace_back(static_cast(seed.keygen_salt[i] ^ key.xor_pad[i])); @@ -250,14 +254,15 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou reinterpret_cast(&out_data.settings)); // Copy the rest of the data directly - out_data.uuid2 = in_data.uuid2; + out_data.uid = in_data.uid; + out_data.nintendo_id = in_data.nintendo_id; + out_data.lock_bytes = in_data.lock_bytes; out_data.static_lock = in_data.static_lock; out_data.compability_container = in_data.compability_container; out_data.constant_value = in_data.constant_value; out_data.write_counter = in_data.write_counter; - out_data.uuid = in_data.uuid; out_data.model_info = in_data.model_info; out_data.keygen_salt = in_data.keygen_salt; out_data.dynamic_lock = in_data.dynamic_lock; @@ -309,7 +314,7 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), - sizeof(HmacKey), reinterpret_cast(&tag_data.uuid), + sizeof(HmacKey), reinterpret_cast(&tag_data.uid), input_length, reinterpret_cast(&tag_data.hmac_tag)); // Regenerate data HMAC @@ -350,7 +355,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), - sizeof(HmacKey), reinterpret_cast(&tag_data.uuid), + sizeof(HmacKey), reinterpret_cast(&tag_data.uid), input_length, reinterpret_cast(&encoded_tag_data.hmac_tag)); // Init mbedtls HMAC context @@ -364,7 +369,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t input_length2); // Data mbedtls_md_hmac_update(&ctx, reinterpret_cast(&encoded_tag_data.hmac_tag), sizeof(HashData)); // Tag HMAC - mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.uuid), + mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.uid), input_length); mbedtls_md_hmac_finish(&ctx, reinterpret_cast(&encoded_tag_data.hmac_data)); diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index e3fa3d07e4..0175ced919 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -24,8 +24,10 @@ using DrgbOutput = std::array; struct HashSeed { u16_be magic; std::array padding; - std::array uuid1; - std::array uuid2; + UniqueSerialNumber uid_1; + u8 nintendo_id_1; + UniqueSerialNumber uid_2; + u8 nintendo_id_2; std::array keygen_salt; }; static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index ce3bcccf45..f0eaa7df2d 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -22,6 +22,9 @@ #include "core/hle/service/nfp/nfp_device.h" #include "core/hle/service/nfp/nfp_result.h" #include "core/hle/service/nfp/nfp_user.h" +#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/time/time_zone_types.h" namespace Service::NFP { NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, @@ -39,6 +42,9 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, }; is_controller_set = true; callback_key = npad_device->SetCallback(engine_callback); + + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; } NfpDevice::~NfpDevice() { @@ -98,6 +104,7 @@ bool NfpDevice::LoadAmiibo(const std::vector& data) { } device_state = DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); activate_event->GetWritableEvent().Signal(); return true; } @@ -112,6 +119,7 @@ void NfpDevice::CloseAmiibo() { device_state = DeviceState::TagRemoved; encrypted_tag_data = {}; tag_data = {}; + activate_event->GetReadableEvent().Clear(); deactivate_event->GetWritableEvent().Signal(); } @@ -140,8 +148,6 @@ void NfpDevice::Finalize() { } Result NfpDevice::StartDetection(s32 protocol_) { - // TODO(german77): Add callback for when nfc data is available - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { npad_device->SetPollingMode(Common::Input::PollingMode::NFC); device_state = DeviceState::SearchingForTag; @@ -172,11 +178,9 @@ Result NfpDevice::StopDetection() { Result NfpDevice::Flush() { auto& settings = tag_data.settings; - if (settings.write_date.raw_date != settings.write_date.raw_date) { - // TODO: Read current system date - settings.write_date.SetYear(2022); - settings.write_date.SetMonth(9); - settings.write_date.SetDay(9); + const auto& current_date = GetAmiiboDate(current_posix_time); + if (settings.write_date.raw_date != current_date.raw_date) { + settings.write_date = current_date; settings.crc_counter++; // TODO: Find how to calculate the crc check // settings.crc = CalculateCRC(settings); @@ -239,10 +243,10 @@ Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { } tag_info = { - .uuid = encrypted_tag_data.uuid, - .uuid_length = static_cast(encrypted_tag_data.uuid.size()), - .protocol = protocol, - .tag_type = static_cast(encrypted_tag_data.user_memory.model_info.amiibo_type), + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), + .protocol = 1, + .tag_type = 2, }; return ResultSuccess; @@ -255,8 +259,6 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { } const auto& settings = tag_data.settings; - const u32 application_area_size = - tag_data.settings.settings.appdata_initialized == 0 ? 0 : sizeof(ApplicationArea); // TODO: Validate this data common_info = { @@ -267,8 +269,8 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { settings.write_date.GetDay(), }, .write_counter = tag_data.write_counter, - .version = 1, - .application_area_size = application_area_size, + .version = 0, + .application_area_size = sizeof(ApplicationArea), }; return ResultSuccess; } @@ -334,13 +336,8 @@ Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { Service::Mii::MiiManager manager; auto& settings = tag_data.settings; - // TODO: Read current system date - settings.init_date.SetYear(2022); - settings.init_date.SetMonth(9); - settings.init_date.SetDay(9); - settings.write_date.SetYear(2022); - settings.write_date.SetMonth(9); - settings.write_date.SetDay(9); + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date = GetAmiiboDate(current_posix_time); settings.crc_counter++; // TODO: Find how to calculate the crc check // settings.crc = CalculateCRC(settings); @@ -570,4 +567,23 @@ void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo } } +AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { + const auto& time_zone_manager = + system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + Time::TimeZone::CalendarInfo calendar_info{}; + AmiiboDate amiibo_date{}; + + amiibo_date.SetYear(2000); + amiibo_date.SetMonth(1); + amiibo_date.SetDay(1); + + if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { + amiibo_date.SetYear(calendar_info.time.year); + amiibo_date.SetMonth(calendar_info.time.month); + amiibo_date.SetDay(calendar_info.time.day); + } + + return amiibo_date; +} + } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index 9ceb7b8fde..c020506a6b 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h @@ -75,6 +75,7 @@ private: AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); + AmiiboDate GetAmiiboDate(s64 posix_time) const; bool is_controller_set{}; int callback_key; @@ -88,6 +89,7 @@ private: bool is_data_moddified{}; s32 protocol{}; + s64 current_posix_time{}; DeviceState device_state{DeviceState::Unavailable}; NTAG215File tag_data{}; diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index 15bc02b15d..ac259e2ff7 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h @@ -17,5 +17,6 @@ constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); constexpr Result CorruptedData(ErrorModule::NFP, 144); constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); +constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 2685ae8fe5..4487918467 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -75,11 +75,19 @@ enum class AmiiboSeries : u8 { Diablo, }; -using TagUuid = std::array; +using UniqueSerialNumber = std::array; +using LockBytes = std::array; using HashData = std::array; using ApplicationArea = std::array; using AmiiboName = std::array; +struct TagUuid { + UniqueSerialNumber uid; + u8 nintendo_id; + LockBytes lock_bytes; +}; +static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); + struct AmiiboDate { u16 raw_date{}; @@ -91,7 +99,7 @@ struct AmiiboDate { return static_cast(((GetValue() & 0xFE00) >> 9) + 2000); } u8 GetMonth() const { - return static_cast(((GetValue() & 0x01E0) >> 5) - 1); + return static_cast((GetValue() & 0x01E0) >> 5); } u8 GetDay() const { return static_cast(GetValue() & 0x001F); @@ -102,7 +110,7 @@ struct AmiiboDate { raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); } void SetMonth(u8 month) { - const u16 month_converted = static_cast((month + 1) << 5); + const u16 month_converted = static_cast(month << 5); raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); } void SetDay(u8 day) { @@ -137,7 +145,7 @@ struct AmiiboModelInfo { u16 character_id; u8 character_variant; AmiiboType amiibo_type; - u16 model_number; + u16_be model_number; AmiiboSeries series; u8 constant_value; // Must be 02 INSERT_PADDING_BYTES(0x4); // Unknown @@ -172,7 +180,7 @@ struct EncryptedAmiiboFile { static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); struct NTAG215File { - std::array uuid2; + LockBytes lock_bytes; // Tag UUID u16 static_lock; // Set defined pages as read only u32 compability_container; // Defines available memory HashData hmac_data; // Hash @@ -188,7 +196,8 @@ struct NTAG215File { HashData hash; // Probably a SHA256-HMAC hash? ApplicationArea application_area; // Encrypted Game data HashData hmac_tag; // Hash - std::array uuid; + UniqueSerialNumber uid; // Unique serial number + u8 nintendo_id; // Tag UUID AmiiboModelInfo model_info; HashData keygen_salt; // Salt u32 dynamic_lock; // Dynamic lock @@ -215,7 +224,8 @@ static_assert(std::is_trivially_copyable_v, "EncryptedNTAG215File must be trivially copyable."); struct TagInfo { - TagUuid uuid; + UniqueSerialNumber uuid; + INSERT_PADDING_BYTES(0x3); u8 uuid_length; INSERT_PADDING_BYTES(0x15); s32 protocol; From d9d566bd3f6cb8fd4f8d3d2d17851e0568ddf946 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Wed, 28 Sep 2022 00:47:51 -0500 Subject: [PATCH 033/245] service: nfp: Implement mount target and open application area errors, minor fixes --- src/core/hle/service/nfp/amiibo_crypto.cpp | 10 +-- src/core/hle/service/nfp/nfp_device.cpp | 99 ++++++++++++++++++++-- src/core/hle/service/nfp/nfp_device.h | 8 +- src/core/hle/service/nfp/nfp_types.h | 24 +++++- src/core/hle/service/nfp/nfp_user.cpp | 2 +- 5 files changed, 124 insertions(+), 19 deletions(-) diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index 9e06970a4d..ce0bc3f756 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -58,8 +58,9 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { if (amiibo_data.model_info.constant_value != 0x02) { return false; } - // dynamic_lock value apparently is not constant - // ntag_file.dynamic_lock == 0x0F0001 + if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { + return false; + } if (ntag_file.CFG0 != 0x04000000U) { return false; } @@ -85,7 +86,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; encoded_data.application_area_id = nfc_data.user_memory.application_area_id; encoded_data.unknown = nfc_data.user_memory.unknown; - encoded_data.hash = nfc_data.user_memory.hash; + encoded_data.unknown2 = nfc_data.user_memory.unknown2; encoded_data.application_area = nfc_data.user_memory.application_area; encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; @@ -116,7 +117,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; nfc_data.user_memory.application_area_id = encoded_data.application_area_id; nfc_data.user_memory.unknown = encoded_data.unknown; - nfc_data.user_memory.hash = encoded_data.hash; + nfc_data.user_memory.unknown2 = encoded_data.unknown2; nfc_data.user_memory.application_area = encoded_data.application_area; nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; nfc_data.user_memory.model_info = encoded_data.model_info; @@ -181,7 +182,6 @@ std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, const std::vector& seed) { - // Initialize context ctx.used = false; ctx.counter = 0; diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index f0eaa7df2d..0d4ffd3a5f 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -85,7 +85,7 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { } } -bool NfpDevice::LoadAmiibo(const std::vector& data) { +bool NfpDevice::LoadAmiibo(std::span data) { if (device_state != DeviceState::SearchingForTag) { LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); return false; @@ -176,6 +176,19 @@ Result NfpDevice::StopDetection() { } Result NfpDevice::Flush() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + auto& settings = tag_data.settings; const auto& current_date = GetAmiiboDate(current_posix_time); @@ -206,7 +219,7 @@ Result NfpDevice::Flush() { return ResultSuccess; } -Result NfpDevice::Mount() { +Result NfpDevice::Mount(MountTarget mount_target_) { if (device_state != DeviceState::TagFound) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); return WrongDeviceState; @@ -218,6 +231,7 @@ Result NfpDevice::Mount() { } device_state = DeviceState::TagMounted; + mount_target = mount_target_; return ResultSuccess; } @@ -233,6 +247,9 @@ Result NfpDevice::Unmount() { } device_state = DeviceState::TagFound; + mount_target = MountTarget::None; + is_app_area_open = false; + return ResultSuccess; } @@ -245,8 +262,8 @@ Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { tag_info = { .uuid = encrypted_tag_data.uuid.uid, .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), - .protocol = 1, - .tag_type = 2, + .protocol = TagProtocol::TypeA, + .tag_type = TagType::Type2, }; return ResultSuccess; @@ -255,6 +272,14 @@ Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); return WrongDeviceState; } @@ -301,6 +326,11 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + if (tag_data.settings.settings.amiibo_initialized == 0) { return RegistrationIsNotInitialized; } @@ -333,6 +363,11 @@ Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + Service::Mii::MiiManager manager; auto& settings = tag_data.settings; @@ -350,6 +385,19 @@ Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { } Result NfpDevice::RestoreAmiibo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + // TODO: Load amiibo from backup on system LOG_ERROR(Service_NFP, "Not Implemented"); return ResultSuccess; @@ -385,6 +433,11 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) { return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { LOG_WARNING(Service_NFP, "Application area is not initialized"); return ApplicationAreaIsNotInitialized; @@ -395,6 +448,8 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) { return WrongApplicationAreaId; } + is_app_area_open = true; + return ResultSuccess; } @@ -407,6 +462,16 @@ Result NfpDevice::GetApplicationArea(std::vector& data) const { return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { LOG_ERROR(Service_NFP, "Application area is not initialized"); return ApplicationAreaIsNotInitialized; @@ -422,7 +487,7 @@ Result NfpDevice::GetApplicationArea(std::vector& data) const { return ResultSuccess; } -Result NfpDevice::SetApplicationArea(const std::vector& data) { +Result NfpDevice::SetApplicationArea(std::span data) { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { @@ -431,6 +496,16 @@ Result NfpDevice::SetApplicationArea(const std::vector& data) { return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { LOG_ERROR(Service_NFP, "Application area is not initialized"); return ApplicationAreaIsNotInitialized; @@ -442,8 +517,10 @@ Result NfpDevice::SetApplicationArea(const std::vector& data) { } Common::TinyMT rng{}; - rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // HW seems to fill excess data with garbage + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); tag_data.applicaton_write_counter++; is_data_moddified = true; @@ -477,6 +554,11 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span dat return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + if (data.size() > sizeof(ApplicationArea)) { LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); return ResultUnknown; @@ -508,6 +590,11 @@ Result NfpDevice::DeleteApplicationArea() { return WrongDeviceState; } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + Common::TinyMT rng{}; rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index c020506a6b..a5b72cf193 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h @@ -40,7 +40,7 @@ public: Result StartDetection(s32 protocol_); Result StopDetection(); - Result Mount(); + Result Mount(MountTarget mount_target); Result Unmount(); Result Flush(); @@ -55,7 +55,7 @@ public: Result OpenApplicationArea(u32 access_id); Result GetApplicationArea(std::vector& data) const; - Result SetApplicationArea(const std::vector& data); + Result SetApplicationArea(std::span data); Result CreateApplicationArea(u32 access_id, std::span data); Result RecreateApplicationArea(u32 access_id, std::span data); Result DeleteApplicationArea(); @@ -70,7 +70,7 @@ public: private: void NpadUpdate(Core::HID::ControllerTriggerType type); - bool LoadAmiibo(const std::vector& data); + bool LoadAmiibo(std::span data); void CloseAmiibo(); AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; @@ -88,8 +88,10 @@ private: Kernel::KEvent* availability_change_event = nullptr; bool is_data_moddified{}; + bool is_app_area_open{}; s32 protocol{}; s64 current_posix_time{}; + MountTarget mount_target{MountTarget::None}; DeviceState device_state{DeviceState::Unavailable}; NTAG215File tag_data{}; diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 4487918467..dd4525b614 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -75,6 +75,22 @@ enum class AmiiboSeries : u8 { Diablo, }; +enum class TagType : u32 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class TagProtocol : u32 { + None, + TypeA, // ISO14443A + TypeB, // ISO14443B + TypeF, // Sony Felica +}; + using UniqueSerialNumber = std::array; using LockBytes = std::array; using HashData = std::array; @@ -174,7 +190,7 @@ struct EncryptedAmiiboFile { u16_be applicaton_write_counter; // Encrypted Counter u32_be application_area_id; // Encrypted Game id std::array unknown; - HashData hash; // Probably a SHA256-HMAC hash? + std::array unknown2; ApplicationArea application_area; // Encrypted Game data }; static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); @@ -193,7 +209,7 @@ struct NTAG215File { u16_be applicaton_write_counter; // Encrypted Counter u32_be application_area_id; std::array unknown; - HashData hash; // Probably a SHA256-HMAC hash? + std::array unknown2; ApplicationArea application_area; // Encrypted Game data HashData hmac_tag; // Hash UniqueSerialNumber uid; // Unique serial number @@ -228,8 +244,8 @@ struct TagInfo { INSERT_PADDING_BYTES(0x3); u8 uuid_length; INSERT_PADDING_BYTES(0x15); - s32 protocol; - u32 tag_type; + TagProtocol protocol; + TagType tag_type; INSERT_PADDING_BYTES(0x30); }; static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index f8f1975db1..c61df94011 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -189,7 +189,7 @@ void IUser::Mount(Kernel::HLERequestContext& ctx) { return; } - const auto result = device.value()->Mount(); + const auto result = device.value()->Mount(mount_target); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } From 1485daff06b7e2aeb7077a4bdb1574956a4c3b82 Mon Sep 17 00:00:00 2001 From: german77 Date: Thu, 29 Sep 2022 01:03:47 -0500 Subject: [PATCH 034/245] service: mii: Copy only valid name bytes --- src/core/hle/service/mii/mii_manager.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 4bc8703e19..3a2fe938f1 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -442,8 +442,15 @@ CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { mii.height = mii_v3.height; mii.build = mii_v3.build; - memset(mii.name.data(), 0, mii.name.size()); - memcpy(mii.name.data(), mii_v3.mii_name.data(), mii_v3.mii_name.size()); + // Copy name until string terminator + mii.name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii.name[index] = mii_v3.mii_name[index]; + if (mii.name[index] == 0) { + break; + } + } + mii.font_region = mii_v3.region_information.character_set; mii.faceline_type = mii_v3.appearance_bits1.face_shape; @@ -515,7 +522,15 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { mii_v3.height = mii.height; mii_v3.build = mii.build; - memcpy(mii_v3.mii_name.data(), mii.name.data(), mii.name.size()); + // Copy name until string terminator + mii_v3.mii_name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii_v3.mii_name[index] = mii.name[index]; + if (mii_v3.mii_name[index] == 0) { + break; + } + } + mii_v3.region_information.character_set.Assign(mii.font_region); mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); From ae7062d5225bb486a7325398a639c27ec6098a97 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 2 Oct 2022 17:32:54 -0400 Subject: [PATCH 035/245] shader_recompiler: add extended LDC to GLASM backend --- .../glasm/emit_glasm_context_get_set.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index b5c08d6117..7e8f375634 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -13,9 +13,6 @@ namespace Shader::Backend::GLASM { namespace { void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset, std::string_view size) { - if (!binding.IsImmediate()) { - throw NotImplementedException("Indirect constant buffer loading"); - } const Register ret{ctx.reg_alloc.Define(inst)}; if (offset.type == Type::U32) { // Avoid reading arrays out of bounds, matching hardware's behavior @@ -24,7 +21,27 @@ void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU return; } } - ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset); + + if (binding.IsImmediate()) { + ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset); + return; + } + + const ScalarU32 idx{ctx.reg_alloc.Consume(binding)}; + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { + ctx.Add("SEQ.S.CC RC.x,{},{};" + "IF NE.x;" + "LDC.{} {},c{}[{}];", + idx, i, size, ret, i, offset); + + if (i != Info::MAX_INDIRECT_CBUFS - 1) { + ctx.Add("ELSE;"); + } + } + + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { + ctx.Add("ENDIF;"); + } } bool IsInputArray(Stage stage) { From 35d3e7db2a0413a921e0846a3d76f9d9f36a2500 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 3 Oct 2022 18:43:56 -0400 Subject: [PATCH 036/245] common: remove "yuzu:" prefix from thread names --- src/audio_core/renderer/adsp/audio_renderer.cpp | 2 +- src/audio_core/renderer/system_manager.cpp | 2 +- src/common/logging/backend.cpp | 2 +- src/core/core_timing.cpp | 2 +- src/core/cpu_manager.cpp | 4 ++-- src/core/debugger/debugger.cpp | 2 +- src/core/hle/kernel/k_worker_task_manager.cpp | 2 +- src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/service_thread.cpp | 2 +- src/core/hle/service/nvflinger/nvflinger.cpp | 2 +- src/input_common/drivers/gc_adapter.cpp | 4 ++-- src/input_common/drivers/mouse.cpp | 2 +- src/input_common/drivers/sdl_driver.cpp | 4 ++-- src/video_core/gpu_thread.cpp | 2 +- src/video_core/renderer_opengl/gl_shader_cache.cpp | 2 +- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 4 ++-- src/video_core/renderer_vulkan/vk_scheduler.cpp | 2 +- src/video_core/textures/astc.cpp | 2 +- src/yuzu/bootmanager.cpp | 2 +- 19 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp index ab2257bd8a..d982ef6302 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp @@ -132,7 +132,7 @@ void AudioRenderer::CreateSinkStreams() { } void AudioRenderer::ThreadFunc() { - constexpr char name[]{"yuzu:AudioRenderer"}; + constexpr char name[]{"AudioRenderer"}; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index 9c1331e193..f66b2b890b 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp @@ -94,7 +94,7 @@ bool SystemManager::Remove(System& system_) { } void SystemManager::ThreadFunc() { - constexpr char name[]{"yuzu:AudioRenderSystemManager"}; + constexpr char name[]{"AudioRenderSystemManager"}; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8ce1c2fd18..15d92505e3 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -219,7 +219,7 @@ private: void StartBackendThread() { backend_thread = std::jthread([this](std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:Log"); + Common::SetCurrentThreadName("Logger"); Entry entry; const auto write_logs = [this, &entry]() { ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index f6c4567baf..6c0fcb7b54 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -43,7 +43,7 @@ CoreTiming::CoreTiming() CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { - constexpr char name[] = "yuzu:HostTiming"; + constexpr char name[] = "HostTiming"; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 9b1565ae1d..0dd4c2196d 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -189,9 +189,9 @@ void CpuManager::RunThread(std::size_t core) { system.RegisterCoreThread(core); std::string name; if (is_multicore) { - name = "yuzu:CPUCore_" + std::to_string(core); + name = "CPUCore_" + std::to_string(core); } else { - name = "yuzu:CPUThread"; + name = "CPUThread"; } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index e42bdd17db..339f971e6f 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -140,7 +140,7 @@ private: } void ThreadLoop(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:Debugger"); + Common::SetCurrentThreadName("Debugger"); // Set up the client signals for new data. AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 221f341ee6..04042bf8f6 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp @@ -23,7 +23,7 @@ void KWorkerTask::DoWorkerTask() { } } -KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} +KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { ASSERT(type <= WorkerType::Count); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ce7fa82757..9251f29ad7 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -48,7 +48,7 @@ namespace Kernel { struct KernelCore::Impl { explicit Impl(Core::System& system_, KernelCore& kernel_) : time_manager{system_}, - service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} + service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {} void SetMulticore(bool is_multi) { is_multicore = is_multi; diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 2e87b4ea4c..d23d767068 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -36,7 +36,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std : service_name{name} { for (std::size_t i = 0; i < num_threads; ++i) { threads.emplace_back([this, &kernel](std::stop_token stop_token) { - Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); + Common::SetCurrentThreadName(std::string{service_name}.c_str()); // Wait for first request before trying to acquire a render context { diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 93057e800a..4246e5e259 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -31,7 +31,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; void NVFlinger::SplitVSync(std::stop_token stop_token) { system.RegisterHostThread(); - std::string name = "yuzu:VSyncThread"; + std::string name = "VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); // Cleanup diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 27a0ffb0dc..f4dd24e7df 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -90,7 +90,7 @@ GCAdapter::~GCAdapter() { void GCAdapter::AdapterInputThread(std::stop_token stop_token) { LOG_DEBUG(Input, "Input thread started"); - Common::SetCurrentThreadName("yuzu:input:GCAdapter"); + Common::SetCurrentThreadName("GCAdapter"); s32 payload_size{}; AdapterPayload adapter_payload{}; @@ -214,7 +214,7 @@ void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_ } void GCAdapter::AdapterScanThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter"); + Common::SetCurrentThreadName("ScanGCAdapter"); usb_adapter_handle = nullptr; pads = {}; while (!stop_token.stop_requested() && !Setup()) { diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 4909fa8d7e..98c3157a89 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -37,7 +37,7 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) } void Mouse::UpdateThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:input:Mouse"); + Common::SetCurrentThreadName("Mouse"); constexpr int update_time = 10; while (!stop_token.stop_requested()) { if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 5cc1ccbd97..b72e4b3975 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -436,7 +436,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en initialized = true; if (start_thread) { poll_thread = std::thread([this] { - Common::SetCurrentThreadName("yuzu:input:SDL"); + Common::SetCurrentThreadName("SDL_MainLoop"); using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); @@ -444,7 +444,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en } }); vibration_thread = std::thread([this] { - Common::SetCurrentThreadName("yuzu:input:SDL_Vibration"); + Common::SetCurrentThreadName("SDL_Vibration"); using namespace std::chrono_literals; while (initialized) { SendVibrations(); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index d43f7175af..f0e48cfbd0 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -19,7 +19,7 @@ namespace VideoCommon::GPUThread { static void RunThread(std::stop_token stop_token, Core::System& system, VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, SynchState& state) { - std::string name = "yuzu:GPU"; + std::string name = "GPU"; MicroProfileOnThreadCreate(name.c_str()); SCOPE_EXIT({ MicroProfileOnThreadExit(); }); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index ddb70934c1..0b8d8ec927 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -546,7 +546,7 @@ std::unique_ptr ShaderCache::CreateComputePipeline( std::unique_ptr ShaderCache::CreateWorkers() const { return std::make_unique(std::max(std::thread::hardware_concurrency(), 2U) - 1, - "yuzu:ShaderBuilder", + "GlShaderBuilder", [this] { return Context{emu_window}; }); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9708dc45e4..accbfc8e11 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -271,8 +271,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw update_descriptor_queue{update_descriptor_queue_}, render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, - workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "yuzu:PipelineBuilder"), - serialization_thread(1, "yuzu:PipelineSerialization") { + workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), + serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; const VkDriverIdKHR driver_id{device.GetDriverID()}; profile = Shader::Profile{ diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index a331ff37ee..d96720b803 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -136,7 +136,7 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) { } void Scheduler::WorkerThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:VulkanWorker"); + Common::SetCurrentThreadName("VulkanWorker"); do { std::unique_ptr work; { diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index e3742ddf55..15b9d4182b 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -1656,7 +1656,7 @@ void Decompress(std::span data, uint32_t width, uint32_t height, const u32 cols = Common::DivideUp(width, block_width); Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2, - "yuzu:ASTCDecompress"}; + "ASTCDecompress"}; for (u32 z = 0; z < depth; ++z) { const u32 depth_offset = z * height * width * 4; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index d3fbdb09d1..24251247d2 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -47,7 +47,7 @@ EmuThread::EmuThread(Core::System& system_) : system{system_} {} EmuThread::~EmuThread() = default; void EmuThread::run() { - std::string name = "yuzu:EmuControlThread"; + std::string name = "EmuControlThread"; MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); From e85c19adcb71149922332150d45d671f90bd1f9f Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Mon, 3 Oct 2022 18:06:55 -0500 Subject: [PATCH 037/245] service: nfp: Fix errors to pass unit testing --- src/core/hid/emulated_controller.cpp | 6 +- src/core/hle/service/nfp/amiibo_crypto.cpp | 4 +- src/core/hle/service/nfp/nfp_device.cpp | 65 ++++++++++++---------- src/core/hle/service/nfp/nfp_result.h | 2 + src/core/hle/service/nfp/nfp_types.h | 47 +++++++++++++--- src/core/hle/service/nfp/nfp_user.cpp | 30 ++++++++++ 6 files changed, 112 insertions(+), 42 deletions(-) diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index e27d84734b..025f1c78e4 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1017,9 +1017,11 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) auto& output_device = output_devices[static_cast(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[3]; - nfc_output_device->SetPollingMode(polling_mode); + const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); + const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); - return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; + return virtual_nfc_result == Common::Input::PollingError::None || + mapped_nfc_result == Common::Input::PollingError::None; } bool EmulatedController::SetCameraFormat( diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index ce0bc3f756..c32a6816b7 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -28,7 +28,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", static_cast(amiibo_data.model_info.model_number)); LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); - LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); + LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); @@ -55,7 +55,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { if (amiibo_data.constant_value != 0xA5) { return false; } - if (amiibo_data.model_info.constant_value != 0x02) { + if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { return false; } if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 0d4ffd3a5f..ec895ac014 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -98,11 +98,6 @@ bool NfpDevice::LoadAmiibo(std::span data) { memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); - if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { - LOG_INFO(Service_NFP, "Invalid amiibo"); - return false; - } - device_state = DeviceState::TagFound; deactivate_event->GetReadableEvent().Clear(); activate_event->GetWritableEvent().Signal(); @@ -148,20 +143,28 @@ void NfpDevice::Finalize() { } Result NfpDevice::StartDetection(s32 protocol_) { - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { - npad_device->SetPollingMode(Common::Input::PollingMode::NFC); - device_state = DeviceState::SearchingForTag; - protocol = protocol_; - return ResultSuccess; + if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; } - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return WrongDeviceState; + if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { + LOG_ERROR(Service_NFP, "Nfc not supported"); + return NfcDisabled; + } + + device_state = DeviceState::SearchingForTag; + protocol = protocol_; + return ResultSuccess; } Result NfpDevice::StopDetection() { npad_device->SetPollingMode(Common::Input::PollingMode::Active); + if (device_state == DeviceState::Initialized) { + return ResultSuccess; + } + if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { CloseAmiibo(); return ResultSuccess; @@ -225,6 +228,11 @@ Result NfpDevice::Mount(MountTarget mount_target_) { return WrongDeviceState; } + if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Not an amiibo"); + return NotAnAmiibo; + } + if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); return CorruptedData; @@ -238,6 +246,9 @@ Result NfpDevice::Mount(MountTarget mount_target_) { Result NfpDevice::Unmount() { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } return WrongDeviceState; } @@ -256,6 +267,9 @@ Result NfpDevice::Unmount() { Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } return WrongDeviceState; } @@ -287,12 +301,7 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { // TODO: Validate this data common_info = { - .last_write_date = - { - settings.write_date.GetYear(), - settings.write_date.GetMonth(), - settings.write_date.GetDay(), - }, + .last_write_date = settings.write_date.GetWriteDate(), .write_counter = tag_data.write_counter, .version = 0, .application_area_size = sizeof(ApplicationArea), @@ -303,6 +312,9 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } return WrongDeviceState; } @@ -341,12 +353,7 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { // TODO: Validate this data register_info = { .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), - .creation_date = - { - settings.init_date.GetYear(), - settings.init_date.GetMonth(), - settings.init_date.GetDay(), - }, + .creation_date = settings.init_date.GetWriteDate(), .amiibo_name = GetAmiiboName(settings), .font_region = {}, }; @@ -478,8 +485,7 @@ Result NfpDevice::GetApplicationArea(std::vector& data) const { } if (data.size() > sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; + data.resize(sizeof(ApplicationArea)); } memcpy(data.data(), tag_data.application_area.data(), data.size()); @@ -518,7 +524,7 @@ Result NfpDevice::SetApplicationArea(std::span data) { Common::TinyMT rng{}; std::memcpy(tag_data.application_area.data(), data.data(), data.size()); - // HW seems to fill excess data with garbage + // Fill remaining data with random numbers rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), sizeof(ApplicationArea) - data.size()); @@ -561,12 +567,12 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span dat if (data.size() > sizeof(ApplicationArea)) { LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; + return WrongApplicationAreaSize; } Common::TinyMT rng{}; std::memcpy(tag_data.application_area.data(), data.data(), data.size()); - // HW seems to fill excess data with garbage + // Fill remaining data with random numbers rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), sizeof(ApplicationArea) - data.size()); @@ -612,7 +618,6 @@ u64 NfpDevice::GetHandle() const { } u32 NfpDevice::GetApplicationAreaSize() const { - // Investigate if this value is really constant return sizeof(ApplicationArea); } diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index ac259e2ff7..d8e4cf0945 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h @@ -8,6 +8,8 @@ namespace Service::NFP { constexpr Result DeviceNotFound(ErrorModule::NFP, 64); +constexpr Result InvalidArgument(ErrorModule::NFP, 65); +constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); constexpr Result WrongDeviceState(ErrorModule::NFP, 73); constexpr Result NfcDisabled(ErrorModule::NFP, 80); constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index dd4525b614..867ea2f36e 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -84,6 +84,15 @@ enum class TagType : u32 { Type5, // ISO15693 RW/RO 540 bytes 106kbit/s }; +enum class PackedTagType : u8 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + enum class TagProtocol : u32 { None, TypeA, // ISO14443A @@ -104,6 +113,13 @@ struct TagUuid { }; static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); +struct WriteDate { + u16 year; + u8 month; + u8 day; +}; +static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + struct AmiiboDate { u16 raw_date{}; @@ -121,6 +137,21 @@ struct AmiiboDate { return static_cast(GetValue() & 0x001F); } + WriteDate GetWriteDate() const { + if (!IsValidDate()) { + return { + .year = 2000, + .month = 1, + .day = 1, + }; + } + return { + .year = GetYear(), + .month = GetMonth(), + .day = GetDay(), + }; + } + void SetYear(u16 year) { const u16 year_converted = static_cast((year - 2000) << 9); raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); @@ -133,6 +164,13 @@ struct AmiiboDate { const u16 day_converted = static_cast(day); raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); } + + bool IsValidDate() const { + const bool is_day_valid = GetDay() > 0 && GetDay() < 32; + const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13; + const bool is_year_valid = GetYear() >= 2000; + return is_year_valid && is_month_valid && is_day_valid; + } }; static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); @@ -163,7 +201,7 @@ struct AmiiboModelInfo { AmiiboType amiibo_type; u16_be model_number; AmiiboSeries series; - u8 constant_value; // Must be 02 + PackedTagType tag_type; INSERT_PADDING_BYTES(0x4); // Unknown }; static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); @@ -250,13 +288,6 @@ struct TagInfo { }; static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); -struct WriteDate { - u16 year; - u8 month; - u8 day; -}; -static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); - struct CommonInfo { WriteDate last_write_date; u16 write_counter; diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index c61df94011..4ed53b5344 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -93,6 +93,18 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) { return; } + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + std::vector nfp_devices; const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); @@ -255,6 +267,12 @@ void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { return; } + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + auto device = GetNfpDevice(device_handle); if (!device.has_value()) { @@ -283,6 +301,12 @@ void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { return; } + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + auto device = GetNfpDevice(device_handle); if (!device.has_value()) { @@ -358,6 +382,12 @@ void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { return; } + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + auto device = GetNfpDevice(device_handle); if (!device.has_value()) { From 7969d4d5de2661348c89c5b5d1481624edfce182 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 3 Oct 2022 20:03:25 -0400 Subject: [PATCH 038/245] vk_scheduler: wait for command processing to complete --- src/video_core/renderer_vulkan/vk_scheduler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index a331ff37ee..7a4868e58e 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -139,6 +139,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { Common::SetCurrentThreadName("yuzu:VulkanWorker"); do { std::unique_ptr work; + bool has_submit{false}; { std::unique_lock lock{work_mutex}; if (work_queue.empty()) { @@ -150,9 +151,10 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { } work = std::move(work_queue.front()); work_queue.pop(); + + has_submit = work->HasSubmit(); + work->ExecuteAll(current_cmdbuf); } - const bool has_submit = work->HasSubmit(); - work->ExecuteAll(current_cmdbuf); if (has_submit) { AllocateWorkerCommandBuffer(); } From 3b5a937125b0a6a78530065cf06aca88c2c9db04 Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Wed, 5 Oct 2022 12:39:54 -0700 Subject: [PATCH 039/245] Show error from cpp-httplib when we don't have a response to read (report errors while connecting to API) (#8999) Co-authored-by: Kyle Kienapfel --- src/web_service/web_backend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 378804c08e..12a7e49229 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -111,7 +111,8 @@ struct Client::Impl { httplib::Error error; if (!cli->send(request, response, error)) { - LOG_ERROR(WebService, "{} to {} returned null", method, host + path); + LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path, + httplib::to_string(error)); return WebResult{WebResult::Code::LibError, "Null response", ""}; } From 1a49991676fa3311cb2efdfd4f250a0f63063d5f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 6 Oct 2022 16:45:40 +0200 Subject: [PATCH 040/245] Texture Cache: Add ASTC 10x5 Format. --- src/video_core/renderer_opengl/maxwell_to_gl.h | 2 ++ src/video_core/renderer_vulkan/maxwell_to_vk.cpp | 2 ++ src/video_core/surface.cpp | 3 +++ src/video_core/surface.h | 8 ++++++++ src/video_core/texture_cache/format_lookup_table.cpp | 4 ++++ src/video_core/texture_cache/formatter.h | 4 ++++ 6 files changed, 23 insertions(+) diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 9a72d0d6d5..dfe7f26ca2 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -99,6 +99,8 @@ constexpr std::array FORMAT_TAB {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM + {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM + {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index bdb71dc538..6703b8e688 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -196,6 +196,8 @@ struct FormatTuple { {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM + {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM + {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 079d5f028a..a2bf082941 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -247,6 +247,8 @@ bool IsPixelFormatASTC(PixelFormat format) { case PixelFormat::ASTC_2D_6X6_UNORM: case PixelFormat::ASTC_2D_6X6_SRGB: case PixelFormat::ASTC_2D_10X6_UNORM: + case PixelFormat::ASTC_2D_10X5_UNORM: + case PixelFormat::ASTC_2D_10X5_SRGB: case PixelFormat::ASTC_2D_10X10_UNORM: case PixelFormat::ASTC_2D_10X10_SRGB: case PixelFormat::ASTC_2D_12X12_UNORM: @@ -276,6 +278,7 @@ bool IsPixelFormatSRGB(PixelFormat format) { case PixelFormat::ASTC_2D_5X5_SRGB: case PixelFormat::ASTC_2D_10X8_SRGB: case PixelFormat::ASTC_2D_6X6_SRGB: + case PixelFormat::ASTC_2D_10X5_SRGB: case PixelFormat::ASTC_2D_10X10_SRGB: case PixelFormat::ASTC_2D_12X12_SRGB: case PixelFormat::ASTC_2D_8X6_SRGB: diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 16273f185a..5fd82357ce 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -94,6 +94,8 @@ enum class PixelFormat { ASTC_2D_6X6_UNORM, ASTC_2D_6X6_SRGB, ASTC_2D_10X6_UNORM, + ASTC_2D_10X5_UNORM, + ASTC_2D_10X5_SRGB, ASTC_2D_10X10_UNORM, ASTC_2D_10X10_SRGB, ASTC_2D_12X12_UNORM, @@ -228,6 +230,8 @@ constexpr std::array BLOCK_WIDTH_TABLE = {{ 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB 10, // ASTC_2D_10X6_UNORM + 10, // ASTC_2D_10X5_UNORM + 10, // ASTC_2D_10X5_SRGB 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB 12, // ASTC_2D_12X12_UNORM @@ -331,6 +335,8 @@ constexpr std::array BLOCK_HEIGHT_TABLE = {{ 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB 6, // ASTC_2D_10X6_UNORM + 5, // ASTC_2D_10X5_UNORM + 5, // ASTC_2D_10X5_SRGB 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB 12, // ASTC_2D_12X12_UNORM @@ -434,6 +440,8 @@ constexpr std::array BITS_PER_BLOCK_TABLE = {{ 128, // ASTC_2D_6X6_UNORM 128, // ASTC_2D_6X6_SRGB 128, // ASTC_2D_10X6_UNORM + 128, // ASTC_2D_10X5_UNORM + 128, // ASTC_2D_10X5_SRGB 128, // ASTC_2D_10X10_UNORM 128, // ASTC_2D_10X10_SRGB 128, // ASTC_2D_12X12_UNORM diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 1412aa0761..c71694d2ab 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -208,6 +208,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::ASTC_2D_6X6_SRGB; case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X6_UNORM; + case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR): + return PixelFormat::ASTC_2D_10X5_UNORM; + case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB): + return PixelFormat::ASTC_2D_10X5_SRGB; case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X10_UNORM; case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 95a5726047..6881e4c901 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h @@ -177,6 +177,10 @@ struct fmt::formatter : fmt::formatter Date: Sat, 30 Oct 2021 02:32:07 +0200 Subject: [PATCH 041/245] NvHost: Fix some regressions and correct signaling on timeout. --- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 527531f294..a3c11ad8a4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -105,30 +105,32 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector auto& event = events_interface.events[event_id]; auto& gpu = system.GPU(); - - // This is mostly to take into account unimplemented features. As synced - // gpu is always synced. - if (!gpu.IsAsync()) { - event.event->GetWritableEvent().Signal(); - return NvResult::Success; - } const u32 current_syncpoint_value = event.fence.value; const s32 diff = current_syncpoint_value - params.threshold; - if (diff >= 0) { - event.event->GetWritableEvent().Signal(); - params.value = current_syncpoint_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; - return NvResult::Success; - } - const u32 target_value = current_syncpoint_value - diff; + const u32 target_value = params.value; if (!is_async) { params.value = 0; } + const auto check_failing = [&]() { + if (events_interface.failed[event_id]) { + { + auto lk = system.StallProcesses(); + gpu.WaitFence(params.syncpt_id, target_value); + system.UnstallProcesses(); + } + std::memcpy(output.data(), ¶ms, sizeof(params)); + events_interface.failed[event_id] = false; + return true; + } + return false; + }; + if (params.timeout == 0) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (check_failing()) { + return NvResult::Success; + } return NvResult::Timeout; } @@ -147,15 +149,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; } params.value |= event_id; - event.event->GetWritableEvent().Clear(); - if (events_interface.failed[event_id]) { - { - auto lk = system.StallProcesses(); - gpu.WaitFence(params.syncpt_id, target_value); - system.UnstallProcesses(); - } - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (check_failing()) { return NvResult::Success; } gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); From ac104a24d11c5875e25daee5dd405ee9fe5f3a21 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 30 Oct 2021 11:35:05 +0200 Subject: [PATCH 042/245] NvHost: Try a different approach to blocking. --- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 15 ++++++--------- src/core/hle/service/nvdrv/nvdrv.h | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index a3c11ad8a4..bfe1faf48c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -91,7 +91,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + events_interface.fails[event_id] = 0; return NvResult::Success; } @@ -99,29 +99,26 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { params.value = new_value; std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + events_interface.fails[event_id] = 0; return NvResult::Success; } - auto& event = events_interface.events[event_id]; auto& gpu = system.GPU(); - const u32 current_syncpoint_value = event.fence.value; - const s32 diff = current_syncpoint_value - params.threshold; - const u32 target_value = params.value; + const u32 target_value = syncpoint_manager.GetSyncpointMax(params.syncpt_id); if (!is_async) { params.value = 0; } const auto check_failing = [&]() { - if (events_interface.failed[event_id]) { + if (events_interface.fails[event_id] > 1) { { auto lk = system.StallProcesses(); gpu.WaitFence(params.syncpt_id, target_value); system.UnstallProcesses(); } std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + events_interface.fails[event_id] = 0; return true; } return false; @@ -207,7 +204,7 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v if (events_interface.status[event_id] == EventState::Waiting) { events_interface.LiberateEvent(event_id); } - events_interface.failed[event_id] = true; + events_interface.fails[event_id]++; syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index c929e51062..4c4aa7dab9 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -50,7 +50,7 @@ struct EventInterface { // Tells if an NVEvent is registered or not std::array registered{}; // Tells the NVEvent that it has failed. - std::array failed{}; + std::array fails{}; // When an NVEvent is waiting on GPU interrupt, this is the sync_point // associated with it. std::array assigned_syncpt{}; From 39a5ce4e696716e48bdd1c980abc792827c68184 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 1 Nov 2021 00:51:29 +0100 Subject: [PATCH 043/245] NvHost: Remake Ctrl Implementation. --- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 182 +++++++++++------- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 47 +++-- src/core/hle/service/nvdrv/nvdata.h | 18 +- src/core/hle/service/nvdrv/nvdrv.cpp | 128 ++++++++++-- src/core/hle/service/nvdrv/nvdrv.h | 96 ++++----- .../hle/service/nvdrv/nvdrv_interface.cpp | 13 +- src/video_core/gpu.h | 2 +- 7 files changed, 314 insertions(+), 172 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index bfe1faf48c..f2b015c8f4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -1,11 +1,14 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #include #include #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" @@ -30,9 +33,9 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& case 0x1c: return IocCtrlClearEventWait(input, output); case 0x1d: - return IocCtrlEventWait(input, output, false); - case 0x1e: return IocCtrlEventWait(input, output, true); + case 0x1e: + return IocCtrlEventWait(input, output, false); case 0x1f: return IocCtrlEventRegister(input, output); case 0x20: @@ -71,54 +74,65 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector& input, std::vector } NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector& output, - bool is_async) { + bool is_allocation) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); - if (params.syncpt_id >= MaxSyncPoints) { + bool must_unmark_fail = !is_allocation; + const u32 event_id = params.value.raw; + SCOPE_EXIT({ + std::memcpy(output.data(), ¶ms, sizeof(params)); + if (must_unmark_fail) { + events_interface.fails[event_id] = 0; + } + }); + + const u32 fence_id = static_cast(params.fence.id); + + if (fence_id >= MaxSyncPoints) { return NvResult::BadParameter; } - u32 event_id = params.value & 0x00FF; - - if (event_id >= MaxNvEvents) { - std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::BadParameter; - } - - if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.fails[event_id] = 0; + if (params.fence.value == 0) { + params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id); return NvResult::Success; } - if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); - syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = new_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.fails[event_id] = 0; + if (syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) { + params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id); + return NvResult::Success; + } + + if (const auto new_value = syncpoint_manager.RefreshSyncpoint(fence_id); + syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) { + params.value.raw = new_value; return NvResult::Success; } auto& gpu = system.GPU(); - const u32 target_value = syncpoint_manager.GetSyncpointMax(params.syncpt_id); + const u32 target_value = params.fence.value; - if (!is_async) { - params.value = 0; - } + auto lock = events_interface.Lock(); + + u32 slot = [&]() { + if (is_allocation) { + params.value.raw = 0; + return events_interface.FindFreeEvent(fence_id); + } else { + return params.value.raw; + } + }(); const auto check_failing = [&]() { - if (events_interface.fails[event_id] > 1) { + if (events_interface.fails[slot] > 1) { { auto lk = system.StallProcesses(); - gpu.WaitFence(params.syncpt_id, target_value); + gpu.WaitFence(fence_id, target_value); system.UnstallProcesses(); } - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.fails[event_id] = 0; + params.value.raw = target_value; return true; } return false; @@ -131,47 +145,76 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector return NvResult::Timeout; } - EventState status = events_interface.status[event_id]; - const bool bad_parameter = status == EventState::Busy; - if (bad_parameter) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (slot >= MaxNvEvents) { return NvResult::BadParameter; } - events_interface.SetEventStatus(event_id, EventState::Waiting); - events_interface.assigned_syncpt[event_id] = params.syncpt_id; - events_interface.assigned_value[event_id] = target_value; - if (is_async) { - params.value = params.syncpt_id << 4; - } else { - params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; + + auto* event = events_interface.events[slot]; + + if (!event) { + return NvResult::BadParameter; } - params.value |= event_id; + + if (events_interface.IsBeingUsed(slot)) { + return NvResult::BadParameter; + } + if (check_failing()) { return NvResult::Success; } - gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); - std::memcpy(output.data(), ¶ms, sizeof(params)); + + params.value.raw = 0; + + events_interface.status[slot].store(EventState::Waiting, std::memory_order_release); + events_interface.assigned_syncpt[slot] = fence_id; + events_interface.assigned_value[slot] = target_value; + if (is_allocation) { + params.value.syncpoint_id_for_allocation.Assign(static_cast(fence_id)); + params.value.event_allocated.Assign(1); + } else { + params.value.syncpoint_id.Assign(fence_id); + } + params.value.raw |= slot; + + gpu.RegisterSyncptInterrupt(fence_id, target_value); return NvResult::Timeout; } +NvResult nvhost_ctrl::FreeEvent(u32 slot) { + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; + } + + if (!events_interface.registered[slot]) { + return NvResult::Success; + } + + if (events_interface.IsBeingUsed(slot)) { + return NvResult::Busy; + } + + events_interface.Free(slot); + return NvResult::Success; +} + NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector& input, std::vector& output) { IocCtrlEventRegisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - const u32 event_id = params.user_event_id & 0x00FF; + const u32 event_id = params.user_event_id; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } + + auto lock = events_interface.Lock(); + if (events_interface.registered[event_id]) { - const auto event_state = events_interface.status[event_id]; - if (event_state != EventState::Free) { - LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); - events_interface.UnregisterEvent(event_id); - } else { - return NvResult::BadParameter; + const auto result = FreeEvent(event_id); + if (result != NvResult::Success) { + return result; } } - events_interface.RegisterEvent(event_id); + events_interface.Create(event_id); return NvResult::Success; } @@ -181,32 +224,33 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector& input, std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); - if (event_id >= MaxNvEvents) { - return NvResult::BadParameter; - } - if (!events_interface.registered[event_id]) { - return NvResult::BadParameter; - } - events_interface.UnregisterEvent(event_id); - return NvResult::Success; + + auto lock = events_interface.Lock(); + return FreeEvent(event_id); } NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::vector& output) { - IocCtrlEventSignalParams params{}; + IocCtrlEventClearParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - u32 event_id = params.event_id & 0x00FF; - LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); + u32 event_id = params.event_id.slot; + LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.status[event_id] == EventState::Waiting) { - events_interface.LiberateEvent(event_id); + + auto lock = events_interface.Lock(); + + if (events_interface.status[event_id].exchange( + EventState::Cancelling, std::memory_order_acq_rel) == EventState::Waiting) { + system.GPU().CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id], + events_interface.assigned_value[event_id]); + syncpoint_manager.RefreshSyncpoint(events_interface.assigned_syncpt[event_id]); } events_interface.fails[event_id]++; - - syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); + events_interface.status[event_id].store(EventState::Cancelled, std::memory_order_release); + events_interface.events[event_id]->GetWritableEvent().Clear(); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 4fbb89b155..0471c09b23 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -5,6 +5,7 @@ #include #include +#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -27,6 +28,24 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + union SyncpointEventValue { + u32 raw; + + union { + BitField<0, 4, u32> partial_slot; + BitField<4, 28, u32> syncpoint_id; + }; + + struct { + u16 slot; + union { + BitField<0, 12, u16> syncpoint_id_for_allocation; + BitField<12, 1, u16> event_allocated; + }; + }; + }; + static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); + private: struct IocSyncptReadParams { u32_le id{}; @@ -83,27 +102,18 @@ private: }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); - struct IocCtrlEventSignalParams { - u32_le event_id{}; + struct IocCtrlEventClearParams { + SyncpointEventValue event_id{}; }; - static_assert(sizeof(IocCtrlEventSignalParams) == 4, - "IocCtrlEventSignalParams is incorrect size"); + static_assert(sizeof(IocCtrlEventClearParams) == 4, + "IocCtrlEventClearParams is incorrect size"); struct IocCtrlEventWaitParams { - u32_le syncpt_id{}; - u32_le threshold{}; - s32_le timeout{}; - u32_le value{}; - }; - static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); - - struct IocCtrlEventWaitAsyncParams { - u32_le syncpt_id{}; - u32_le threshold{}; + NvFence fence{}; u32_le timeout{}; - u32_le value{}; + SyncpointEventValue value{}; }; - static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitAsyncParams is incorrect size"); struct IocCtrlEventRegisterParams { @@ -124,11 +134,14 @@ private: static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); NvResult NvOsGetConfigU32(const std::vector& input, std::vector& output); - NvResult IocCtrlEventWait(const std::vector& input, std::vector& output, bool is_async); + NvResult IocCtrlEventWait(const std::vector& input, std::vector& output, + bool is_allocation); NvResult IocCtrlEventRegister(const std::vector& input, std::vector& output); NvResult IocCtrlEventUnregister(const std::vector& input, std::vector& output); NvResult IocCtrlClearEventWait(const std::vector& input, std::vector& output); + NvResult FreeEvent(u32 slot); + EventInterface& events_interface; SyncpointManager& syncpoint_manager; }; diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 1d00394c80..2ee91f9c43 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once @@ -78,11 +80,15 @@ enum class NvResult : u32 { ModuleNotPresent = 0xA000E, }; +// obtained from +// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47 enum class EventState { - Free = 0, - Registered = 1, - Waiting = 2, - Busy = 3, + Available = 0, + Waiting = 1, + Cancelling = 2, + Signalling = 3, + Signalled = 4, + Cancelled = 5, }; union Ioctl { diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 756eb74537..fa259abed2 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -1,6 +1,9 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. +#include #include #include @@ -26,6 +29,73 @@ namespace Service::Nvidia { +std::unique_lock EventInterface::Lock() { + return std::unique_lock(events_mutex); +} + +void EventInterface::Signal(u32 event_id) { + if (status[event_id].exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + events[event_id]->GetWritableEvent().Signal(); + } + status[event_id].store(EventState::Signalled, std::memory_order_release); +} + +void EventInterface::Create(u32 event_id) { + ASSERT(!events[event_id]); + ASSERT(!registered[event_id]); + ASSERT(!IsBeingUsed(event_id)); + events[event_id] = backup[event_id]; + status[event_id] = EventState::Available; + registered[event_id] = true; + const u64 mask = 1ULL << event_id; + fails[event_id] = 0; + events_mask |= mask; + LOG_CRITICAL(Service_NVDRV, "Created Event {}", event_id); +} + +void EventInterface::Free(u32 event_id) { + ASSERT(events[event_id]); + ASSERT(registered[event_id]); + ASSERT(!IsBeingUsed(event_id)); + + backup[event_id]->GetWritableEvent().Clear(); + events[event_id] = nullptr; + status[event_id] = EventState::Available; + registered[event_id] = false; + const u64 mask = ~(1ULL << event_id); + events_mask &= mask; + LOG_CRITICAL(Service_NVDRV, "Freed Event {}", event_id); +} + +u32 EventInterface::FindFreeEvent(u32 syncpoint_id) { + u32 slot{MaxNvEvents}; + u32 free_slot{MaxNvEvents}; + for (u32 i = 0; i < MaxNvEvents; i++) { + if (registered[i]) { + if (!IsBeingUsed(i)) { + slot = i; + if (assigned_syncpt[i] == syncpoint_id) { + return slot; + } + } + } else if (free_slot == MaxNvEvents) { + free_slot = i; + } + } + if (free_slot < MaxNvEvents) { + Create(free_slot); + return free_slot; + } + + if (slot < MaxNvEvents) { + return slot; + } + + LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); + return 0; +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared(system); @@ -38,12 +108,14 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { + : syncpoint_manager{system.GPU()}, events_interface{*this}, service_context{system, "nvdrv"} { + events_interface.events_mask = 0; for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event = - service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); - events_interface.status[i] = EventState::Free; + events_interface.status[i] = EventState::Available; + events_interface.events[i] = nullptr; events_interface.registered[i] = false; + events_interface.backup[i] = + service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); } auto nvmap_dev = std::make_shared(system); devices["/dev/nvhost-as-gpu"] = std::make_shared(system, nvmap_dev); @@ -62,8 +134,12 @@ Module::Module(Core::System& system) } Module::~Module() { + auto lock = events_interface.Lock(); for (u32 i = 0; i < MaxNvEvents; i++) { - service_context.CloseEvent(events_interface.events[i].event); + if (events_interface.registered[i]) { + events_interface.Free(i); + } + service_context.CloseEvent(events_interface.backup[i]); } } @@ -169,21 +245,41 @@ NvResult Module::Close(DeviceFD fd) { } void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - for (u32 i = 0; i < MaxNvEvents; i++) { - if (events_interface.assigned_syncpt[i] == syncpoint_id && + const u32 max = MaxNvEvents - std::countl_zero(events_interface.events_mask); + const u32 min = std::countr_zero(events_interface.events_mask); + for (u32 i = min; i < max; i++) { + if (events_interface.registered[i] && events_interface.assigned_syncpt[i] == syncpoint_id && events_interface.assigned_value[i] == value) { - events_interface.LiberateEvent(i); - events_interface.events[i].event->GetWritableEvent().Signal(); + events_interface.Signal(i); } } } -Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { - return events_interface.events[event_id].event->GetReadableEvent(); -} +Kernel::KEvent* Module::GetEvent(u32 event_id) { + const auto event = Devices::nvhost_ctrl::SyncpointEventValue{.raw = event_id}; -Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { - return events_interface.events[event_id].event->GetWritableEvent(); + const bool allocated = event.event_allocated.Value() != 0; + const u32 slot{allocated ? event.partial_slot.Value() : static_cast(event.slot)}; + if (slot >= MaxNvEvents) { + ASSERT(false); + return nullptr; + } + + const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value() + : event.syncpoint_id.Value()}; + + auto lock = events_interface.Lock(); + + if (events_interface.registered[slot] && + events_interface.assigned_syncpt[slot] == syncpoint_id) { + ASSERT(events_interface.events[slot]); + return events_interface.events[slot]; + } + // Temporary hack. + events_interface.Create(slot); + events_interface.assigned_syncpt[slot] = syncpoint_id; + ASSERT(false); + return events_interface.events[slot]; } } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 4c4aa7dab9..8ce036508a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once @@ -34,19 +36,20 @@ namespace Devices { class nvdevice; } -/// Represents an Nvidia event -struct NvEvent { - Kernel::KEvent* event{}; - NvFence fence{}; -}; +class Module; -struct EventInterface { - // Mask representing currently busy events +class EventInterface { +public: + EventInterface(Module& module_) : module{module_} {} + + // Mask representing registered events u64 events_mask{}; // Each kernel event associated to an NV event - std::array events; + std::array events{}; + // Backup NV event + std::array backup{}; // The status of the current NVEvent - std::array status{}; + std::array, MaxNvEvents> status{}; // Tells if an NVEvent is registered or not std::array registered{}; // Tells the NVEvent that it has failed. @@ -59,50 +62,26 @@ struct EventInterface { std::array assigned_value{}; // Constant to denote an unasigned syncpoint. static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; - std::optional GetFreeEvent() const { - u64 mask = events_mask; - for (u32 i = 0; i < MaxNvEvents; i++) { - const bool is_free = (mask & 0x1) == 0; - if (is_free) { - if (status[i] == EventState::Registered || status[i] == EventState::Free) { - return {i}; - } - } - mask = mask >> 1; - } - return std::nullopt; - } - void SetEventStatus(const u32 event_id, EventState new_status) { - EventState old_status = status[event_id]; - if (old_status == new_status) { - return; - } - status[event_id] = new_status; - if (new_status == EventState::Registered) { - registered[event_id] = true; - } - if (new_status == EventState::Waiting || new_status == EventState::Busy) { - events_mask |= (1ULL << event_id); - } - } - void RegisterEvent(const u32 event_id) { - registered[event_id] = true; - if (status[event_id] == EventState::Free) { - status[event_id] = EventState::Registered; - } - } - void UnregisterEvent(const u32 event_id) { - registered[event_id] = false; - if (status[event_id] == EventState::Registered) { - status[event_id] = EventState::Free; - } - } - void LiberateEvent(const u32 event_id) { - status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; - events_mask &= ~(1ULL << event_id); - assigned_syncpt[event_id] = unassigned_syncpt; - assigned_value[event_id] = 0; + + bool IsBeingUsed(u32 event_id) { + const auto current_status = status[event_id].load(std::memory_order_acquire); + return current_status == EventState::Waiting || current_status == EventState::Cancelling || + current_status == EventState::Signalling; } + + std::unique_lock Lock(); + + void Signal(u32 event_id); + + void Create(u32 event_id); + + void Free(u32 event_id); + + u32 FindFreeEvent(u32 syncpoint_id); + +private: + std::mutex events_mutex; + Module& module; }; class Module final { @@ -139,11 +118,11 @@ public: void SignalSyncpt(const u32 syncpoint_id, const u32 value); - Kernel::KReadableEvent& GetEvent(u32 event_id); - - Kernel::KWritableEvent& GetEventWriteable(u32 event_id); + Kernel::KEvent* GetEvent(u32 event_id); private: + friend class EventInterface; + /// Manages syncpoints on the host SyncpointManager syncpoint_manager; @@ -159,6 +138,9 @@ private: EventInterface events_interface; KernelHelpers::ServiceContext service_context; + + void CreateEvent(u32 event_id); + void FreeEvent(u32 event_id); }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index b5a9803840..07883feb2f 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -5,6 +5,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -164,8 +165,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto fd = rp.Pop(); - const auto event_id = rp.Pop() & 0x00FF; - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); + const auto event_id = rp.Pop(); if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); @@ -180,12 +180,13 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { return; } - if (event_id < MaxNvEvents) { + auto* event = nvdrv->GetEvent(event_id); + + if (event) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(ResultSuccess); - auto& event = nvdrv->GetEvent(event_id); - event.Clear(); - rb.PushCopyObjects(event); + auto& readable_event = event->GetReadableEvent(); + rb.PushCopyObjects(readable_event); rb.PushEnum(NvResult::Success); } else { IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index b939ba315d..42c91954f7 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -201,7 +201,7 @@ public: void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); - [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); + bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); [[nodiscard]] u64 GetTicks() const; From d30b885d713fa2b9393aeb3c515f4d881bf838f2 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 1 Nov 2021 15:02:47 +0100 Subject: [PATCH 044/245] NVDRV: Implement QueryEvent. --- src/core/hle/service/nvdrv/devices/nvdevice.h | 8 ++++ .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 23 +++++++++ .../hle/service/nvdrv/devices/nvhost_ctrl.h | 2 + .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 20 +++++++- .../service/nvdrv/devices/nvhost_ctrl_gpu.h | 14 +++++- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 26 +++++++++- .../hle/service/nvdrv/devices/nvhost_gpu.h | 13 ++++- src/core/hle/service/nvdrv/nvdrv.cpp | 48 +++++++++---------- src/core/hle/service/nvdrv/nvdrv.h | 6 ++- .../hle/service/nvdrv/nvdrv_interface.cpp | 15 ++---- 10 files changed, 134 insertions(+), 41 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 696e8121e5..204b0e7572 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -11,6 +11,10 @@ namespace Core { class System; } +namespace Kernel { +class KEvent; +} + namespace Service::Nvidia::Devices { /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to @@ -64,6 +68,10 @@ public: */ virtual void OnClose(DeviceFD fd) = 0; + virtual Kernel::KEvent* QueryEvent(u32 event_id) { + return nullptr; + } + protected: Core::System& system; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index f2b015c8f4..9b393521aa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -255,4 +255,27 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v return NvResult::Success; } +Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { + const auto event = SyncpointEventValue{.raw = event_id}; + + const bool allocated = event.event_allocated.Value() != 0; + const u32 slot{allocated ? event.partial_slot.Value() : static_cast(event.slot)}; + if (slot >= MaxNvEvents) { + ASSERT(false); + return nullptr; + } + + const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value() + : event.syncpoint_id.Value()}; + + auto lock = events_interface.Lock(); + + if (events_interface.registered[slot] && + events_interface.assigned_syncpt[slot] == syncpoint_id) { + ASSERT(events_interface.events[slot]); + return events_interface.events[slot]; + } + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 0471c09b23..d548a78277 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -28,6 +28,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + union SyncpointEventValue { u32 raw; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 2b3b7efea7..e353408eb9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -7,10 +7,15 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) + : nvdevice{system_}, events_interface{events_interface_} { + error_notifier_event = events_interface.CreateNonCtrlEvent("CtrlGpuErrorNotifier"); + unknown_event = events_interface.CreateNonCtrlEvent("CtrlGpuUknownEvent"); +} nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -286,4 +291,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector& input, std::vector& input, @@ -27,6 +31,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: struct IoctlGpuCharacteristics { u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) @@ -160,6 +166,12 @@ private: NvResult ZBCQueryTable(const std::vector& input, std::vector& output); NvResult FlushL2(const std::vector& input, std::vector& output); NvResult GetGpuTime(const std::vector& input, std::vector& output); + + EventInterface& events_interface; + + // Events + Kernel::KEvent* error_notifier_event; + Kernel::KEvent* unknown_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index b98e63011f..e87fa59924 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/memory.h" #include "video_core/gpu.h" @@ -21,10 +22,16 @@ Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoi } // namespace nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { + EventInterface& events_interface_, SyncpointManager& syncpoint_manager_) + : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, events_interface{events_interface_}, + syncpoint_manager{syncpoint_manager_} { channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); + sm_exception_breakpoint_int_report_event = + events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointInt"); + sm_exception_breakpoint_pause_report_event = + events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointPause"); + error_notifier_event = events_interface.CreateNonCtrlEvent("GpuChannelErrorNotifier"); } nvhost_gpu::~nvhost_gpu() = default; @@ -328,4 +335,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector& input, std::vect return NvResult::Success; } +Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { + switch (event_id) { + case 1: + return sm_exception_breakpoint_int_report_event; + case 2: + return sm_exception_breakpoint_pause_report_event; + case 3: + return error_notifier_event; + default: { + LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + } + } + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 8a9f7775ad..eb4936df0e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -15,7 +15,8 @@ namespace Service::Nvidia { class SyncpointManager; -} +class EventInterface; +} // namespace Service::Nvidia namespace Service::Nvidia::Devices { @@ -23,7 +24,7 @@ class nvmap; class nvhost_gpu final : public nvdevice { public: explicit nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + EventInterface& events_interface_, SyncpointManager& syncpoint_manager_); ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -36,6 +37,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: enum class CtxObjects : u32_le { Ctx2D = 0x902D, @@ -192,8 +195,14 @@ private: NvResult ChannelSetTimeslice(const std::vector& input, std::vector& output); std::shared_ptr nvmap_dev; + EventInterface& events_interface; SyncpointManager& syncpoint_manager; NvFence channel_fence; + + // Events + Kernel::KEvent* sm_exception_breakpoint_int_report_event; + Kernel::KEvent* sm_exception_breakpoint_pause_report_event; + Kernel::KEvent* error_notifier_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index fa259abed2..7c0f89c127 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -96,6 +96,12 @@ u32 EventInterface::FindFreeEvent(u32 syncpoint_id) { return 0; } +Kernel::KEvent* EventInterface::CreateNonCtrlEvent(std::string name) { + Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); + basic_events.push_back(new_event); + return new_event; +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared(system); @@ -119,9 +125,10 @@ Module::Module(Core::System& system) } auto nvmap_dev = std::make_shared(system); devices["/dev/nvhost-as-gpu"] = std::make_shared(system, nvmap_dev); - devices["/dev/nvhost-gpu"] = - std::make_shared(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared(system); + devices["/dev/nvhost-gpu"] = std::make_shared( + system, nvmap_dev, events_interface, syncpoint_manager); + devices["/dev/nvhost-ctrl-gpu"] = + std::make_shared(system, events_interface); devices["/dev/nvmap"] = nvmap_dev; devices["/dev/nvdisp_disp0"] = std::make_shared(system, nvmap_dev); devices["/dev/nvhost-ctrl"] = @@ -255,31 +262,24 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { } } -Kernel::KEvent* Module::GetEvent(u32 event_id) { - const auto event = Devices::nvhost_ctrl::SyncpointEventValue{.raw = event_id}; - - const bool allocated = event.event_allocated.Value() != 0; - const u32 slot{allocated ? event.partial_slot.Value() : static_cast(event.slot)}; - if (slot >= MaxNvEvents) { - ASSERT(false); - return nullptr; +NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; } - const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value() - : event.syncpoint_id.Value()}; + const auto itr = open_files.find(fd); - auto lock = events_interface.Lock(); - - if (events_interface.registered[slot] && - events_interface.assigned_syncpt[slot] == syncpoint_id) { - ASSERT(events_interface.events[slot]); - return events_interface.events[slot]; + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; } - // Temporary hack. - events_interface.Create(slot); - events_interface.assigned_syncpt[slot] = syncpoint_id; - ASSERT(false); - return events_interface.events[slot]; + + event = itr->second->QueryEvent(event_id); + if (!event) { + return NvResult::BadParameter; + } + return NvResult::Success; } } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 8ce036508a..be8813c978 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -6,6 +6,7 @@ #pragma once #include +#include #include #include @@ -79,9 +80,12 @@ public: u32 FindFreeEvent(u32 syncpoint_id); + Kernel::KEvent* CreateNonCtrlEvent(std::string name); + private: std::mutex events_mutex; Module& module; + std::vector basic_events; }; class Module final { @@ -118,7 +122,7 @@ public: void SignalSyncpt(const u32 syncpoint_id, const u32 value); - Kernel::KEvent* GetEvent(u32 event_id); + NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); private: friend class EventInterface; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 07883feb2f..81ee28f319 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -173,25 +173,20 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { return; } - const auto nv_result = nvdrv->VerifyFD(fd); - if (nv_result != NvResult::Success) { - LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); - ServiceError(ctx, nv_result); - return; - } + Kernel::KEvent* event = nullptr; + NvResult result = nvdrv->QueryEvent(fd, event_id, event); - auto* event = nvdrv->GetEvent(event_id); - - if (event) { + if (result == NvResult::Success) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(ResultSuccess); auto& readable_event = event->GetReadableEvent(); rb.PushCopyObjects(readable_event); rb.PushEnum(NvResult::Success); } else { + LOG_ERROR(Service_NVDRV, "Invalid event request!"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(NvResult::BadParameter); + rb.PushEnum(result); } } From a21b8824fb8a0bd7bcb44fd50559f26718f5dbc4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 1 Nov 2021 15:25:06 +0100 Subject: [PATCH 045/245] NVDRV: Cleanup. --- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 4 +- src/core/hle/service/nvdrv/nvdrv.cpp | 53 ++++++++++--------- src/core/hle/service/nvdrv/nvdrv.h | 9 ++-- .../hle/service/nvdrv/nvdrv_interface.cpp | 6 ++- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 9b393521aa..55acd4f78e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -125,8 +125,10 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector } }(); + must_unmark_fail = true; + const auto check_failing = [&]() { - if (events_interface.fails[slot] > 1) { + if (events_interface.fails[slot] > 2) { { auto lk = system.StallProcesses(); gpu.WaitFence(fence_id, target_value); diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 7c0f89c127..df46562403 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -29,6 +29,29 @@ namespace Service::Nvidia { +EventInterface::EventInterface(Module& module_) : module{module_} { + events_mask = 0; + for (u32 i = 0; i < MaxNvEvents; i++) { + status[i] = EventState::Available; + events[i] = nullptr; + registered[i] = false; + } +} + +EventInterface::~EventInterface() { + auto lk = Lock(); + for (u32 i = 0; i < MaxNvEvents; i++) { + if (registered[i]) { + module.service_context.CloseEvent(events[i]); + events[i] = nullptr; + registered[i] = false; + } + } + for (auto* event : basic_events) { + module.service_context.CloseEvent(event); + } +} + std::unique_lock EventInterface::Lock() { return std::unique_lock(events_mutex); } @@ -45,27 +68,25 @@ void EventInterface::Create(u32 event_id) { ASSERT(!events[event_id]); ASSERT(!registered[event_id]); ASSERT(!IsBeingUsed(event_id)); - events[event_id] = backup[event_id]; + events[event_id] = + module.service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", event_id)); status[event_id] = EventState::Available; registered[event_id] = true; const u64 mask = 1ULL << event_id; fails[event_id] = 0; events_mask |= mask; - LOG_CRITICAL(Service_NVDRV, "Created Event {}", event_id); } void EventInterface::Free(u32 event_id) { ASSERT(events[event_id]); ASSERT(registered[event_id]); ASSERT(!IsBeingUsed(event_id)); - - backup[event_id]->GetWritableEvent().Clear(); + module.service_context.CloseEvent(events[event_id]); events[event_id] = nullptr; status[event_id] = EventState::Available; registered[event_id] = false; const u64 mask = ~(1ULL << event_id); events_mask &= mask; - LOG_CRITICAL(Service_NVDRV, "Freed Event {}", event_id); } u32 EventInterface::FindFreeEvent(u32 syncpoint_id) { @@ -114,15 +135,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : syncpoint_manager{system.GPU()}, events_interface{*this}, service_context{system, "nvdrv"} { - events_interface.events_mask = 0; - for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.status[i] = EventState::Available; - events_interface.events[i] = nullptr; - events_interface.registered[i] = false; - events_interface.backup[i] = - service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); - } + : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"}, events_interface{*this} { auto nvmap_dev = std::make_shared(system); devices["/dev/nvhost-as-gpu"] = std::make_shared(system, nvmap_dev); devices["/dev/nvhost-gpu"] = std::make_shared( @@ -140,15 +153,7 @@ Module::Module(Core::System& system) std::make_shared(system, nvmap_dev, syncpoint_manager); } -Module::~Module() { - auto lock = events_interface.Lock(); - for (u32 i = 0; i < MaxNvEvents; i++) { - if (events_interface.registered[i]) { - events_interface.Free(i); - } - service_context.CloseEvent(events_interface.backup[i]); - } -} +Module::~Module() = default; NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { @@ -255,7 +260,7 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { const u32 max = MaxNvEvents - std::countl_zero(events_interface.events_mask); const u32 min = std::countr_zero(events_interface.events_mask); for (u32 i = min; i < max; i++) { - if (events_interface.registered[i] && events_interface.assigned_syncpt[i] == syncpoint_id && + if (events_interface.assigned_syncpt[i] == syncpoint_id && events_interface.assigned_value[i] == value) { events_interface.Signal(i); } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index be8813c978..d24b575399 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -41,14 +41,13 @@ class Module; class EventInterface { public: - EventInterface(Module& module_) : module{module_} {} + EventInterface(Module& module_); + ~EventInterface(); // Mask representing registered events u64 events_mask{}; // Each kernel event associated to an NV event std::array events{}; - // Backup NV event - std::array backup{}; // The status of the current NVEvent std::array, MaxNvEvents> status{}; // Tells if an NVEvent is registered or not @@ -139,10 +138,10 @@ private: /// Mapping of device node names to their implementation. std::unordered_map> devices; - EventInterface events_interface; - KernelHelpers::ServiceContext service_context; + EventInterface events_interface; + void CreateEvent(u32 event_id); void FreeEvent(u32 event_id); }; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 81ee28f319..bd41205b88 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #include #include "common/logging/log.h" From 3cbe352c18f69596d91c4862382d61a3d6515140 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 1 Nov 2021 18:53:32 +0100 Subject: [PATCH 046/245] NVDRV: Refactor and add new NvMap. --- src/common/bit_field.h | 13 +- src/core/CMakeLists.txt | 8 +- src/core/hle/service/nvdrv/core/container.cpp | 41 +++ src/core/hle/service/nvdrv/core/container.h | 38 +++ src/core/hle/service/nvdrv/core/nvmap.cpp | 245 ++++++++++++++++++ src/core/hle/service/nvdrv/core/nvmap.h | 155 +++++++++++ .../nvdrv/{ => core}/syncpoint_manager.cpp | 6 +- .../nvdrv/{ => core}/syncpoint_manager.h | 4 +- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 8 +- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 10 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 9 +- .../hle/service/nvdrv/devices/nvhost_gpu.h | 10 +- .../service/nvdrv/devices/nvhost_nvdec.cpp | 4 +- .../hle/service/nvdrv/devices/nvhost_nvdec.h | 2 +- .../nvdrv/devices/nvhost_nvdec_common.cpp | 8 +- .../nvdrv/devices/nvhost_nvdec_common.h | 9 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 4 +- .../hle/service/nvdrv/devices/nvhost_vic.h | 2 +- src/core/hle/service/nvdrv/nvdrv.cpp | 16 +- src/core/hle/service/nvdrv/nvdrv.h | 11 +- 20 files changed, 558 insertions(+), 45 deletions(-) create mode 100644 src/core/hle/service/nvdrv/core/container.cpp create mode 100644 src/core/hle/service/nvdrv/core/container.h create mode 100644 src/core/hle/service/nvdrv/core/nvmap.cpp create mode 100644 src/core/hle/service/nvdrv/core/nvmap.h rename src/core/hle/service/nvdrv/{ => core}/syncpoint_manager.cpp (88%) rename src/core/hle/service/nvdrv/{ => core}/syncpoint_manager.h (97%) diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 7e1df62b1c..368b7b98c9 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -127,11 +127,14 @@ public: } } - // This constructor and assignment operator might be considered ambiguous: - // Would they initialize the storage or just the bitfield? - // Hence, delete them. Use the Assign method to set bitfield values! - BitField(T val) = delete; - BitField& operator=(T val) = delete; + BitField(T val) { + Assign(val); + } + + BitField& operator=(T val) { + Assign(val); + return *this; + } constexpr BitField() noexcept = default; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8e3fd4505c..3ef19f9c26 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -550,6 +550,12 @@ add_library(core STATIC hle/service/ns/ns.h hle/service/ns/pdm_qry.cpp hle/service/ns/pdm_qry.h + hle/service/nvdrv/core/container.cpp + hle/service/nvdrv/core/container.h + hle/service/nvdrv/core/nvmap.cpp + hle/service/nvdrv/core/nvmap.h + hle/service/nvdrv/core/syncpoint_manager.cpp + hle/service/nvdrv/core/syncpoint_manager.h hle/service/nvdrv/devices/nvdevice.h hle/service/nvdrv/devices/nvdisp_disp0.cpp hle/service/nvdrv/devices/nvdisp_disp0.h @@ -578,8 +584,6 @@ add_library(core STATIC hle/service/nvdrv/nvdrv_interface.h hle/service/nvdrv/nvmemp.cpp hle/service/nvdrv/nvmemp.h - hle/service/nvdrv/syncpoint_manager.cpp - hle/service/nvdrv/syncpoint_manager.h hle/service/nvflinger/binder.h hle/service/nvflinger/buffer_item.h hle/service/nvflinger/buffer_item_consumer.cpp diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp new file mode 100644 index 0000000000..97b5b2c86f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -0,0 +1,41 @@ +// Copyright 2021 yuzu emulator team +// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/gpu.h" + +namespace Service::Nvidia::NvCore { + +struct ContainerImpl { + ContainerImpl(Tegra::GPU& gpu_) : file{}, manager{gpu_} {} + NvMap file; + SyncpointManager manager; +}; + +Container::Container(Tegra::GPU& gpu_) { + impl = std::make_unique(gpu_); +} + +Container::~Container() = default; + +NvMap& Container::GetNvMapFile() { + return impl->file; +} + +const NvMap& Container::GetNvMapFile() const { + return impl->file; +} + +SyncpointManager& Container::GetSyncpointManager() { + return impl->manager; +} + +const SyncpointManager& Container::GetSyncpointManager() const { + return impl->manager; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h new file mode 100644 index 0000000000..91ac2305ab --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.h @@ -0,0 +1,38 @@ +// Copyright 2021 yuzu emulator team +// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Tegra { +class GPU; +} + +namespace Service::Nvidia::NvCore { + +class NvMap; +class SyncpointManager; + +struct ContainerImpl; + +class Container { +public: + Container(Tegra::GPU& gpu_); + ~Container(); + + NvMap& GetNvMapFile(); + + const NvMap& GetNvMapFile() const; + + SyncpointManager& GetSyncpointManager(); + + const SyncpointManager& GetSyncpointManager() const; + +private: + std::unique_ptr impl; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp new file mode 100644 index 0000000000..d3f227f526 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -0,0 +1,245 @@ +// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/memory.h" + +using Core::Memory::YUZU_PAGESIZE; + +namespace Service::Nvidia::NvCore { +NvMap::Handle::Handle(u64 size, Id id) : size(size), aligned_size(size), orig_size(size), id(id) {} + +NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { + std::scoped_lock lock(mutex); + + // Handles cannot be allocated twice + if (allocated) [[unlikely]] + return NvResult::AccessDenied; + + flags = pFlags; + kind = pKind; + align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; + + // This flag is only applicable for handles with an address passed + if (pAddress) + flags.keep_uncached_after_free = 0; + else + LOG_CRITICAL(Service_NVDRV, + "Mapping nvmap handles without a CPU side address is unimplemented!"); + + size = Common::AlignUp(size, YUZU_PAGESIZE); + aligned_size = Common::AlignUp(size, align); + address = pAddress; + + // TODO: pin init + + allocated = true; + + return NvResult::Success; +} + +NvResult NvMap::Handle::Duplicate(bool internal_session) { + // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) + if (!allocated) [[unlikely]] + return NvResult::BadValue; + + std::scoped_lock lock(mutex); + + // If we internally use FromId the duplication tracking of handles won't work accurately due to + // us not implementing per-process handle refs. + if (internal_session) + internal_dupes++; + else + dupes++; + + return NvResult::Success; +} + +NvMap::NvMap() = default; + +void NvMap::AddHandle(std::shared_ptr handleDesc) { + std::scoped_lock lock(handles_lock); + + handles.emplace(handleDesc->id, std::move(handleDesc)); +} + +void NvMap::UnmapHandle(Handle& handleDesc) { + // Remove pending unmap queue entry if needed + if (handleDesc.unmap_queue_entry) { + unmap_queue.erase(*handleDesc.unmap_queue_entry); + handleDesc.unmap_queue_entry.reset(); + } + + // Free and unmap the handle from the SMMU + /* + state.soc->smmu.Unmap(handleDesc.pin_virt_address, static_cast(handleDesc.aligned_size)); + smmuAllocator.Free(handleDesc.pin_virt_address, static_cast(handleDesc.aligned_size)); + handleDesc.pin_virt_address = 0; + */ +} + +bool NvMap::TryRemoveHandle(const Handle& handleDesc) { + // No dupes left, we can remove from handle map + if (handleDesc.dupes == 0 && handleDesc.internal_dupes == 0) { + std::scoped_lock lock(handles_lock); + + auto it{handles.find(handleDesc.id)}; + if (it != handles.end()) + handles.erase(it); + + return true; + } else { + return false; + } +} + +NvResult NvMap::CreateHandle(u64 size, std::shared_ptr& result_out) { + if (!size) [[unlikely]] + return NvResult::BadValue; + + u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; + auto handleDesc{std::make_shared(size, id)}; + AddHandle(handleDesc); + + result_out = handleDesc; + return NvResult::Success; +} + +std::shared_ptr NvMap::GetHandle(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle); + } catch ([[maybe_unused]] std::out_of_range& e) { + return nullptr; + } +} + +u32 NvMap::PinHandle(NvMap::Handle::Id handle) { + UNIMPLEMENTED_MSG("pinning"); + return 0; + /* + auto handleDesc{GetHandle(handle)}; + if (!handleDesc) + [[unlikely]] return 0; + + std::scoped_lock lock(handleDesc->mutex); + if (!handleDesc->pins) { + // If we're in the unmap queue we can just remove ourselves and return since we're already + // mapped + { + // Lock now to prevent our queue entry from being removed for allocation in-between the + // following check and erase + std::scoped_lock queueLock(unmap_queue_lock); + if (handleDesc->unmap_queue_entry) { + unmap_queue.erase(*handleDesc->unmap_queue_entry); + handleDesc->unmap_queue_entry.reset(); + + handleDesc->pins++; + return handleDesc->pin_virt_address; + } + } + + // If not then allocate some space and map it + u32 address{}; + while (!(address = smmuAllocator.Allocate(static_cast(handleDesc->aligned_size)))) { + // Free handles until the allocation succeeds + std::scoped_lock queueLock(unmap_queue_lock); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handleDesc->pin_virt_address) + UnmapHandle(*freeHandleDesc); + } else { + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + } + } + + state.soc->smmu.Map(address, handleDesc->GetPointer(), + static_cast(handleDesc->aligned_size)); + handleDesc->pin_virt_address = address; + } + + handleDesc->pins++; + return handleDesc->pin_virt_address; + */ +} + +void NvMap::UnpinHandle(Handle::Id handle) { + UNIMPLEMENTED_MSG("Unpinning"); + /* + auto handleDesc{GetHandle(handle)}; + if (!handleDesc) + return; + + std::scoped_lock lock(handleDesc->mutex); + if (--handleDesc->pins < 0) { + LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); + } else if (!handleDesc->pins) { + std::scoped_lock queueLock(unmap_queue_lock); + + // Add to the unmap queue allowing this handle's memory to be freed if needed + unmap_queue.push_back(handleDesc); + handleDesc->unmap_queue_entry = std::prev(unmap_queue.end()); + } + */ +} + +std::optional NvMap::FreeHandle(Handle::Id handle, bool internal_session) { + std::weak_ptr hWeak{GetHandle(handle)}; + FreeInfo freeInfo; + + // We use a weak ptr here so we can tell when the handle has been freed and report that back to + // guest + if (auto handleDesc = hWeak.lock()) { + std::scoped_lock lock(handleDesc->mutex); + + if (internal_session) { + if (--handleDesc->internal_dupes < 0) + LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); + } else { + if (--handleDesc->dupes < 0) { + LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); + } else if (handleDesc->dupes == 0) { + // Force unmap the handle + if (handleDesc->pin_virt_address) { + std::scoped_lock queueLock(unmap_queue_lock); + UnmapHandle(*handleDesc); + } + + handleDesc->pins = 0; + } + } + + // Try to remove the shared ptr to the handle from the map, if nothing else is using the + // handle then it will now be freed when `handleDesc` goes out of scope + if (TryRemoveHandle(*handleDesc)) + LOG_ERROR(Service_NVDRV, "Removed nvmap handle: {}", handle); + else + LOG_ERROR(Service_NVDRV, + "Tried to free nvmap handle: {} but didn't as it still has duplicates", + handle); + + freeInfo = { + .address = handleDesc->address, + .size = handleDesc->size, + .was_uncached = handleDesc->flags.map_uncached.Value() != 0, + }; + } else { + return std::nullopt; + } + + // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed + if (!hWeak.expired()) { + LOG_ERROR(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); + freeInfo.address = 0; + } + + return freeInfo; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h new file mode 100644 index 0000000000..e47aa755d6 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -0,0 +1,155 @@ +// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Service::Nvidia::NvCore { +/** + * @brief The nvmap core class holds the global state for nvmap and provides methods to manage + * handles + */ +class NvMap { +public: + /** + * @brief A handle to a contiguous block of memory in an application's address space + */ + struct Handle { + std::mutex mutex; + + u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU + u64 size; //!< Page-aligned size of the memory the handle refers to + u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to + u64 orig_size; //!< Original unaligned size of the memory this handle refers to + + s32 dupes{1}; //!< How many guest references there are to this handle + s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle + + using Id = u32; + Id id; //!< A globally unique identifier for this handle + + s32 pins{}; + u32 pin_virt_address{}; + std::optional>::iterator> unmap_queue_entry{}; + + union Flags { + BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached + BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was + //!< allocated with a fixed address + BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins + } flags{}; + static_assert(sizeof(Flags) == sizeof(u32)); + + u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, + //!< this can also be in the nvdrv tmem + bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC + //!< call + + u8 kind{}; //!< Used for memory compression + bool allocated{}; //!< If the handle has been allocated with `Alloc` + + Handle(u64 size, Id id); + + /** + * @brief Sets up the handle with the given memory config, can allocate memory from the tmem + * if a 0 address is passed + */ + [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); + + /** + * @brief Increases the dupe counter of the handle for the given session + */ + [[nodiscard]] NvResult Duplicate(bool internal_session); + + /** + * @brief Obtains a pointer to the handle's memory and marks the handle it as having been + * mapped + */ + u8* GetPointer() { + if (!address) { + return nullptr; + } + + is_shared_mem_mapped = true; + return reinterpret_cast(address); + } + }; + +private: + std::list> unmap_queue; + std::mutex unmap_queue_lock; //!< Protects access to `unmap_queue` + + std::unordered_map> handles; //!< Main owning map of handles + std::mutex handles_lock; //!< Protects access to `handles` + + static constexpr u32 HandleIdIncrement{ + 4}; //!< Each new handle ID is an increment of 4 from the previous + std::atomic next_handle_id{HandleIdIncrement}; + + void AddHandle(std::shared_ptr handle); + + /** + * @brief Unmaps and frees the SMMU memory region a handle is mapped to + * @note Both `unmap_queue_lock` and `handleDesc.mutex` MUST be locked when calling this + */ + void UnmapHandle(Handle& handleDesc); + + /** + * @brief Removes a handle from the map taking its dupes into account + * @note handleDesc.mutex MUST be locked when calling this + * @return If the handle was removed from the map + */ + bool TryRemoveHandle(const Handle& handleDesc); + +public: + /** + * @brief Encapsulates the result of a FreeHandle operation + */ + struct FreeInfo { + u64 address; //!< Address the handle referred to before deletion + u64 size; //!< Page-aligned handle size + bool was_uncached; //!< If the handle was allocated as uncached + }; + + NvMap(); + + /** + * @brief Creates an unallocated handle of the given size + */ + [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr& result_out); + + std::shared_ptr GetHandle(Handle::Id handle); + + /** + * @brief Maps a handle into the SMMU address space + * @note This operation is refcounted, the number of calls to this must eventually match the + * number of calls to `UnpinHandle` + * @return The SMMU virtual address that the handle has been mapped to + */ + u32 PinHandle(Handle::Id handle); + + /** + * @brief When this has been called an equal number of times to `PinHandle` for the supplied + * handle it will be added to a list of handles to be freed when necessary + */ + void UnpinHandle(Handle::Id handle); + + /** + * @brief Tries to free a handle and remove a single dupe + * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned + * describing the prior state of the handle + */ + std::optional FreeHandle(Handle::Id handle, bool internal_session); +}; +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp similarity index 88% rename from src/core/hle/service/nvdrv/syncpoint_manager.cpp rename to src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index a6fa943e8a..ff6cbb37e5 100644 --- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -2,10 +2,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "video_core/gpu.h" -namespace Service::Nvidia { +namespace Service::Nvidia::NvCore { SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} @@ -35,4 +35,4 @@ u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { return GetSyncpointMax(syncpoint_id); } -} // namespace Service::Nvidia +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h similarity index 97% rename from src/core/hle/service/nvdrv/syncpoint_manager.h rename to src/core/hle/service/nvdrv/core/syncpoint_manager.h index 7f080f76e2..cf7f0b4bee 100644 --- a/src/core/hle/service/nvdrv/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -13,7 +13,7 @@ namespace Tegra { class GPU; } -namespace Service::Nvidia { +namespace Service::Nvidia::NvCore { class SyncpointManager final { public: @@ -81,4 +81,4 @@ private: Tegra::GPU& gpu; }; -} // namespace Service::Nvidia +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 55acd4f78e..5e2155e6c7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -12,15 +12,17 @@ #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "video_core/gpu.h" namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ - syncpoint_manager_} {} + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()} {} nvhost_ctrl::~nvhost_ctrl() = default; NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index d548a78277..9fd46ea5f0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -10,12 +10,17 @@ #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdrv.h" +namespace Service::Nvidia::NvCore { +class Container; +class SyncpointManager; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_ctrl() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -145,7 +150,8 @@ private: NvResult FreeEvent(u32 slot); EventInterface& events_interface; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index e87fa59924..a480bfc47f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -5,9 +5,10 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/memory.h" #include "video_core/gpu.h" @@ -22,10 +23,10 @@ Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoi } // namespace nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - EventInterface& events_interface_, SyncpointManager& syncpoint_manager_) + EventInterface& events_interface_, NvCore::Container& core_) : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, events_interface{events_interface_}, - syncpoint_manager{syncpoint_manager_} { - channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); + core{core_}, syncpoint_manager{core_.GetSyncpointManager()} { + channel_fence.id = syncpoint_manager.AllocateSyncpoint(); channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); sm_exception_breakpoint_int_report_event = events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointInt"); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index eb4936df0e..4f73a7bae3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -14,7 +14,12 @@ #include "video_core/dma_pusher.h" namespace Service::Nvidia { + +namespace NvCore { +class Container; class SyncpointManager; +} // namespace NvCore + class EventInterface; } // namespace Service::Nvidia @@ -24,7 +29,7 @@ class nvmap; class nvhost_gpu final : public nvdevice { public: explicit nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - EventInterface& events_interface_, SyncpointManager& syncpoint_manager_); + EventInterface& events_interface_, NvCore::Container& core); ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -196,7 +201,8 @@ private: std::shared_ptr nvmap_dev; EventInterface& events_interface; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; NvFence channel_fence; // Events diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index a7385fce81..2c9158c7c8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -11,8 +11,8 @@ namespace Service::Nvidia::Devices { nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} + NvCore::Container& core) + : nvhost_nvdec_common{system_, std::move(nvmap_dev_), core} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 29b3e6a360..04da4a9136 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -11,7 +11,7 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvhost_nvdec_common { public: explicit nvhost_nvdec(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_nvdec() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 8b2cd9bf1d..5a9c59f371 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -8,9 +8,10 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/memory.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" @@ -45,8 +46,9 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } // Anonymous namespace nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} + NvCore::Container& core_) + : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, core{core_}, + syncpoint_manager{core.GetSyncpointManager()} {} nvhost_nvdec_common::~nvhost_nvdec_common() = default; NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 12d39946db..cccc94a584 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -9,7 +9,11 @@ #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia { + +namespace NvCore { class SyncpointManager; +class Container; +} // namespace NvCore namespace Devices { class nvmap; @@ -17,7 +21,7 @@ class nvmap; class nvhost_nvdec_common : public nvdevice { public: explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_nvdec_common() override; protected: @@ -114,7 +118,8 @@ protected: s32_le nvmap_fd{}; u32_le submit_timeout{}; std::shared_ptr nvmap_dev; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; std::array device_syncpoints{}; }; }; // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index f58e8bada5..66558c331b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -9,8 +9,8 @@ namespace Service::Nvidia::Devices { nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} + NvCore::Container& core) + : nvhost_nvdec_common{system_, std::move(nvmap_dev_), core} {} nvhost_vic::~nvhost_vic() = default; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index b41b195aee..6f9838b2dc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -10,7 +10,7 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvhost_nvdec_common { public: explicit nvhost_vic(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_vic(); NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index df46562403..824c0e2906 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -11,6 +11,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" @@ -24,8 +25,8 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "video_core/gpu.h" namespace Service::Nvidia { @@ -75,6 +76,7 @@ void EventInterface::Create(u32 event_id) { const u64 mask = 1ULL << event_id; fails[event_id] = 0; events_mask |= mask; + assigned_syncpt[event_id] = 0; } void EventInterface::Free(u32 event_id) { @@ -135,22 +137,22 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"}, events_interface{*this} { + : service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} { auto nvmap_dev = std::make_shared(system); devices["/dev/nvhost-as-gpu"] = std::make_shared(system, nvmap_dev); - devices["/dev/nvhost-gpu"] = std::make_shared( - system, nvmap_dev, events_interface, syncpoint_manager); + devices["/dev/nvhost-gpu"] = + std::make_shared(system, nvmap_dev, events_interface, container); devices["/dev/nvhost-ctrl-gpu"] = std::make_shared(system, events_interface); devices["/dev/nvmap"] = nvmap_dev; devices["/dev/nvdisp_disp0"] = std::make_shared(system, nvmap_dev); devices["/dev/nvhost-ctrl"] = - std::make_shared(system, events_interface, syncpoint_manager); + std::make_shared(system, events_interface, container); devices["/dev/nvhost-nvdec"] = - std::make_shared(system, nvmap_dev, syncpoint_manager); + std::make_shared(system, nvmap_dev, container); devices["/dev/nvhost-nvjpg"] = std::make_shared(system); devices["/dev/nvhost-vic"] = - std::make_shared(system, nvmap_dev, syncpoint_manager); + std::make_shared(system, nvmap_dev, container); } Module::~Module() = default; diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index d24b575399..96adf2ffbc 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -12,8 +12,8 @@ #include "common/common_types.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/ui/fence.h" #include "core/hle/service/service.h" @@ -31,7 +31,10 @@ class NVFlinger; namespace Service::Nvidia { +namespace NvCore { +class Container; class SyncpointManager; +} // namespace NvCore namespace Devices { class nvdevice; @@ -126,9 +129,6 @@ public: private: friend class EventInterface; - /// Manages syncpoints on the host - SyncpointManager syncpoint_manager; - /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; @@ -142,6 +142,9 @@ private: EventInterface events_interface; + /// Manages syncpoints on the host + NvCore::Container container; + void CreateEvent(u32 event_id); void FreeEvent(u32 event_id); }; From de0e8eff429b4374c18e3325ad3747db55bddddd Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 4 Nov 2021 12:51:17 +0100 Subject: [PATCH 047/245] NVDRV: Implement new NvMap --- src/core/hle/service/nvdrv/core/nvmap.cpp | 119 +++++----- src/core/hle/service/nvdrv/core/nvmap.h | 12 +- .../service/nvdrv/devices/nvdisp_disp0.cpp | 14 +- .../hle/service/nvdrv/devices/nvdisp_disp0.h | 12 +- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 54 +++-- .../hle/service/nvdrv/devices/nvhost_as_gpu.h | 12 +- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 2 + .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 9 +- .../hle/service/nvdrv/devices/nvhost_gpu.h | 7 +- .../service/nvdrv/devices/nvhost_nvdec.cpp | 5 +- .../hle/service/nvdrv/devices/nvhost_nvdec.h | 3 +- .../nvdrv/devices/nvhost_nvdec_common.cpp | 26 ++- .../nvdrv/devices/nvhost_nvdec_common.h | 11 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 5 +- .../hle/service/nvdrv/devices/nvhost_vic.h | 3 +- src/core/hle/service/nvdrv/devices/nvmap.cpp | 218 +++++++++--------- src/core/hle/service/nvdrv/devices/nvmap.h | 55 ++--- src/core/hle/service/nvdrv/nvdrv.cpp | 15 +- 18 files changed, 306 insertions(+), 276 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index d3f227f526..281381cc41 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -17,7 +17,7 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) std::scoped_lock lock(mutex); // Handles cannot be allocated twice - if (allocated) [[unlikely]] + if (allocated) return NvResult::AccessDenied; flags = pFlags; @@ -61,33 +61,34 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) { NvMap::NvMap() = default; -void NvMap::AddHandle(std::shared_ptr handleDesc) { +void NvMap::AddHandle(std::shared_ptr handle_description) { std::scoped_lock lock(handles_lock); - handles.emplace(handleDesc->id, std::move(handleDesc)); + handles.emplace(handle_description->id, std::move(handle_description)); } -void NvMap::UnmapHandle(Handle& handleDesc) { +void NvMap::UnmapHandle(Handle& handle_description) { // Remove pending unmap queue entry if needed - if (handleDesc.unmap_queue_entry) { - unmap_queue.erase(*handleDesc.unmap_queue_entry); - handleDesc.unmap_queue_entry.reset(); + if (handle_description.unmap_queue_entry) { + unmap_queue.erase(*handle_description.unmap_queue_entry); + handle_description.unmap_queue_entry.reset(); } // Free and unmap the handle from the SMMU /* - state.soc->smmu.Unmap(handleDesc.pin_virt_address, static_cast(handleDesc.aligned_size)); - smmuAllocator.Free(handleDesc.pin_virt_address, static_cast(handleDesc.aligned_size)); - handleDesc.pin_virt_address = 0; + state.soc->smmu.Unmap(handle_description.pin_virt_address, + static_cast(handle_description.aligned_size)); + smmuAllocator.Free(handle_description.pin_virt_address, + static_cast(handle_description.aligned_size)); handle_description.pin_virt_address = 0; */ } -bool NvMap::TryRemoveHandle(const Handle& handleDesc) { +bool NvMap::TryRemoveHandle(const Handle& handle_description) { // No dupes left, we can remove from handle map - if (handleDesc.dupes == 0 && handleDesc.internal_dupes == 0) { + if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { std::scoped_lock lock(handles_lock); - auto it{handles.find(handleDesc.id)}; + auto it{handles.find(handle_description.id)}; if (it != handles.end()) handles.erase(it); @@ -102,10 +103,10 @@ NvResult NvMap::CreateHandle(u64 size, std::shared_ptr& result_ou return NvResult::BadValue; u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; - auto handleDesc{std::make_shared(size, id)}; - AddHandle(handleDesc); + auto handle_description{std::make_shared(size, id)}; + AddHandle(handle_description); - result_out = handleDesc; + result_out = handle_description; return NvResult::Success; } @@ -118,73 +119,83 @@ std::shared_ptr NvMap::GetHandle(Handle::Id handle) { } } +VAddr NvMap::GetHandleAddress(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle)->address; + } catch ([[maybe_unused]] std::out_of_range& e) { + return 0; + } +} + u32 NvMap::PinHandle(NvMap::Handle::Id handle) { UNIMPLEMENTED_MSG("pinning"); return 0; /* - auto handleDesc{GetHandle(handle)}; - if (!handleDesc) + auto handle_description{GetHandle(handle)}; + if (!handle_description) [[unlikely]] return 0; - std::scoped_lock lock(handleDesc->mutex); - if (!handleDesc->pins) { + std::scoped_lock lock(handle_description->mutex); + if (!handle_description->pins) { // If we're in the unmap queue we can just remove ourselves and return since we're already // mapped { // Lock now to prevent our queue entry from being removed for allocation in-between the // following check and erase std::scoped_lock queueLock(unmap_queue_lock); - if (handleDesc->unmap_queue_entry) { - unmap_queue.erase(*handleDesc->unmap_queue_entry); - handleDesc->unmap_queue_entry.reset(); + if (handle_description->unmap_queue_entry) { + unmap_queue.erase(*handle_description->unmap_queue_entry); + handle_description->unmap_queue_entry.reset(); - handleDesc->pins++; - return handleDesc->pin_virt_address; + handle_description->pins++; + return handle_description->pin_virt_address; } } // If not then allocate some space and map it u32 address{}; - while (!(address = smmuAllocator.Allocate(static_cast(handleDesc->aligned_size)))) { + while (!(address = + smmuAllocator.Allocate(static_cast(handle_description->aligned_size)))) { // Free handles until the allocation succeeds std::scoped_lock queueLock(unmap_queue_lock); if (auto freeHandleDesc{unmap_queue.front()}) { // Handles in the unmap queue are guaranteed not to be pinned so don't bother // checking if they are before unmapping std::scoped_lock freeLock(freeHandleDesc->mutex); - if (handleDesc->pin_virt_address) + if (handle_description->pin_virt_address) UnmapHandle(*freeHandleDesc); } else { LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); } } - state.soc->smmu.Map(address, handleDesc->GetPointer(), - static_cast(handleDesc->aligned_size)); - handleDesc->pin_virt_address = address; + state.soc->smmu.Map(address, handle_description->GetPointer(), + static_cast(handle_description->aligned_size)); + handle_description->pin_virt_address = address; } - handleDesc->pins++; - return handleDesc->pin_virt_address; + handle_description->pins++; + return handle_description->pin_virt_address; */ } void NvMap::UnpinHandle(Handle::Id handle) { UNIMPLEMENTED_MSG("Unpinning"); /* - auto handleDesc{GetHandle(handle)}; - if (!handleDesc) + auto handle_description{GetHandle(handle)}; + if (!handle_description) return; - std::scoped_lock lock(handleDesc->mutex); - if (--handleDesc->pins < 0) { + std::scoped_lock lock(handle_description->mutex); + if (--handle_description->pins < 0) { LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); - } else if (!handleDesc->pins) { + } else if (!handle_description->pins) { std::scoped_lock queueLock(unmap_queue_lock); // Add to the unmap queue allowing this handle's memory to be freed if needed - unmap_queue.push_back(handleDesc); - handleDesc->unmap_queue_entry = std::prev(unmap_queue.end()); + unmap_queue.push_back(handle_description); + handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); } */ } @@ -195,39 +206,39 @@ std::optional NvMap::FreeHandle(Handle::Id handle, bool interna // We use a weak ptr here so we can tell when the handle has been freed and report that back to // guest - if (auto handleDesc = hWeak.lock()) { - std::scoped_lock lock(handleDesc->mutex); + if (auto handle_description = hWeak.lock()) { + std::scoped_lock lock(handle_description->mutex); if (internal_session) { - if (--handleDesc->internal_dupes < 0) + if (--handle_description->internal_dupes < 0) LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); } else { - if (--handleDesc->dupes < 0) { + if (--handle_description->dupes < 0) { LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); - } else if (handleDesc->dupes == 0) { + } else if (handle_description->dupes == 0) { // Force unmap the handle - if (handleDesc->pin_virt_address) { + if (handle_description->pin_virt_address) { std::scoped_lock queueLock(unmap_queue_lock); - UnmapHandle(*handleDesc); + UnmapHandle(*handle_description); } - handleDesc->pins = 0; + handle_description->pins = 0; } } // Try to remove the shared ptr to the handle from the map, if nothing else is using the - // handle then it will now be freed when `handleDesc` goes out of scope - if (TryRemoveHandle(*handleDesc)) - LOG_ERROR(Service_NVDRV, "Removed nvmap handle: {}", handle); + // handle then it will now be freed when `handle_description` goes out of scope + if (TryRemoveHandle(*handle_description)) + LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); else - LOG_ERROR(Service_NVDRV, + LOG_DEBUG(Service_NVDRV, "Tried to free nvmap handle: {} but didn't as it still has duplicates", handle); freeInfo = { - .address = handleDesc->address, - .size = handleDesc->size, - .was_uncached = handleDesc->flags.map_uncached.Value() != 0, + .address = handle_description->address, + .size = handle_description->size, + .was_uncached = handle_description->flags.map_uncached.Value() != 0, }; } else { return std::nullopt; diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index e47aa755d6..994c70e6fa 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -59,6 +59,8 @@ public: u8 kind{}; //!< Used for memory compression bool allocated{}; //!< If the handle has been allocated with `Alloc` + u64 dma_map_addr{}; //! remove me after implementing pinning. + Handle(u64 size, Id id); /** @@ -101,16 +103,16 @@ private: /** * @brief Unmaps and frees the SMMU memory region a handle is mapped to - * @note Both `unmap_queue_lock` and `handleDesc.mutex` MUST be locked when calling this + * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this */ - void UnmapHandle(Handle& handleDesc); + void UnmapHandle(Handle& handle_description); /** * @brief Removes a handle from the map taking its dupes into account - * @note handleDesc.mutex MUST be locked when calling this + * @note handle_description.mutex MUST be locked when calling this * @return If the handle was removed from the map */ - bool TryRemoveHandle(const Handle& handleDesc); + bool TryRemoveHandle(const Handle& handle_description); public: /** @@ -131,6 +133,8 @@ public: std::shared_ptr GetHandle(Handle::Id handle); + VAddr GetHandleAddress(Handle::Id handle); + /** * @brief Maps a handle into the SMMU address space * @note This operation is refcounted, the number of calls to this must eventually match the diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 6047119142..b1c0e9eb2d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -5,15 +5,16 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/perf_stats.h" #include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) + : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} nvdisp_disp0::~nvdisp_disp0() = default; NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -40,7 +41,7 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, const Common::Rectangle& crop_rect) { - const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); + const VAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); @@ -54,4 +55,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form system.GetPerfStats().BeginSystemFrame(); } +Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 67b105e020..1ca9b2e74a 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -11,13 +11,18 @@ #include "core/hle/service/nvflinger/buffer_transform_flags.h" #include "core/hle/service/nvflinger/pixel_format.h" +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(Core::System& system_, std::shared_ptr nvmap_dev_); + explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); ~nvdisp_disp0() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -35,8 +40,11 @@ public: u32 stride, android::BufferTransformFlags transform, const Common::Rectangle& crop_rect); + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: - std::shared_ptr nvmap_dev; + NvCore::Container& container; + NvCore::NvMap& nvmap; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9867a648d8..9283d6aec9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -7,15 +7,16 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Service::Nvidia::Devices { -nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, NvCore::Container& core) + : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -143,7 +144,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", entry.offset, entry.nvmap_handle, entry.pages); - const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; + const auto object{nvmap.GetHandle(entry.nvmap_handle)}; if (!object) { LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); result = NvResult::InvalidState; @@ -153,7 +154,8 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out const auto offset{static_cast(entry.offset) << 0x10}; const auto size{static_cast(entry.pages) << 0x10}; const auto map_offset{static_cast(entry.map_offset) << 0x10}; - const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; + const auto addr{ + system.GPU().MemoryManager().Map(object->address + map_offset, offset, size)}; if (!addr) { LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); @@ -176,24 +178,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorGetObject(params.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - - // The real nvservices doesn't make a distinction between handles and ids, and - // object can only have one handle and it will be the same as its id. Assert that this is the - // case to prevent unexpected behavior. - ASSERT(object->id == params.nvmap_handle); auto& gpu = system.GPU(); - - u64 page_size{params.page_size}; - if (!page_size) { - page_size = object->align; - } - if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { const auto cpu_addr{static_cast(buffer_map->CpuAddr() + params.buffer_offset)}; @@ -220,10 +205,24 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorstatus == nvmap::Object::Status::Allocated); + const auto object{nvmap.GetHandle(params.nvmap_handle)}; + if (!object) { + LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); + std::memcpy(output.data(), ¶ms, output.size()); + return NvResult::InvalidState; + } - const auto physical_address{object->addr + params.buffer_offset}; + // The real nvservices doesn't make a distinction between handles and ids, and + // object can only have one handle and it will be the same as its id. Assert that this is the + // case to prevent unexpected behavior. + ASSERT(object->id == params.nvmap_handle); + + u64 page_size{params.page_size}; + if (!page_size) { + page_size = object->align; + } + + const auto physical_address{object->address + params.buffer_offset}; u64 size{params.mapping_size}; if (!size) { size = object->size; @@ -363,4 +362,9 @@ std::optional nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { return std::nullopt; } +Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 555843a6f4..67d2f1e877 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -13,6 +13,11 @@ #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; @@ -29,7 +34,7 @@ DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr nvmap_dev_); + explicit nvhost_as_gpu(Core::System& system_, NvCore::Container& core); ~nvhost_as_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -42,6 +47,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: class BufferMap final { public: @@ -180,7 +187,8 @@ private: void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); std::optional RemoveBufferMap(GPUVAddr gpu_addr); - std::shared_ptr nvmap_dev; + NvCore::Container& container; + NvCore::NvMap& nvmap; // This is expected to be ordered, therefore we must use a map, not unordered_map std::map buffer_mappings; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 5e2155e6c7..51c40f6202 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -279,6 +279,8 @@ Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { ASSERT(events_interface.events[slot]); return events_interface.events[slot]; } + // Is this possible in hardware? + ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); return nullptr; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index a480bfc47f..e7921ade23 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -22,10 +23,10 @@ Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoi } } // namespace -nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - EventInterface& events_interface_, NvCore::Container& core_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, events_interface{events_interface_}, - core{core_}, syncpoint_manager{core_.GetSyncpointManager()} { +nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} { channel_fence.id = syncpoint_manager.AllocateSyncpoint(); channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); sm_exception_breakpoint_int_report_event = diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 4f73a7bae3..440c0c42d5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -17,6 +17,7 @@ namespace Service::Nvidia { namespace NvCore { class Container; +class NvMap; class SyncpointManager; } // namespace NvCore @@ -28,8 +29,8 @@ namespace Service::Nvidia::Devices { class nvmap; class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - EventInterface& events_interface_, NvCore::Container& core); + explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core); ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -199,10 +200,10 @@ private: NvResult ChannelSetTimeout(const std::vector& input, std::vector& output); NvResult ChannelSetTimeslice(const std::vector& input, std::vector& output); - std::shared_ptr nvmap_dev; EventInterface& events_interface; NvCore::Container& core; NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; NvFence channel_fence; // Events diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 2c9158c7c8..aa1a00832a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -10,9 +10,8 @@ namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr nvmap_dev_, - NvCore::Container& core) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), core} {} +nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core) + : nvhost_nvdec_common{system_, core} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 04da4a9136..fef4b32167 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -10,8 +10,7 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvhost_nvdec_common { public: - explicit nvhost_nvdec(Core::System& system_, std::shared_ptr nvmap_dev_, - NvCore::Container& core); + explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); ~nvhost_nvdec() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 5a9c59f371..e76c9e5eda 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -9,9 +9,9 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/memory.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" @@ -45,10 +45,9 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } } // Anonymous namespace -nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr nvmap_dev_, - NvCore::Container& core_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, core{core_}, - syncpoint_manager{core.GetSyncpointManager()} {} +nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_) + : nvdevice{system_}, core{core_}, + syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} {} nvhost_nvdec_common::~nvhost_nvdec_common() = default; NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { @@ -90,10 +89,10 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector& input, } } for (const auto& cmd_buffer : command_buffers) { - const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); + const auto object = nvmap.GetHandle(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), + system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), cmdlist.size() * sizeof(u32)); gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); } @@ -125,6 +124,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector& input, std::ve NvResult nvhost_nvdec_common::GetWaitbase(const std::vector& input, std::vector& output) { IoctlGetWaitbase params{}; + LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); @@ -141,7 +141,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto auto& gpu = system.GPU(); for (auto& cmd_buffer : cmd_buffer_handles) { - auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; + auto object{nvmap.GetHandle(cmd_buffer.map_handle)}; if (!object) { LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); std::memcpy(output.data(), ¶ms, output.size()); @@ -150,7 +150,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto if (object->dma_map_addr == 0) { // NVDEC and VIC memory is in the 32-bit address space // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space - const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size); + const GPUVAddr low_addr = + gpu.MemoryManager().MapAllocate32(object->address, object->size); object->dma_map_addr = static_cast(low_addr); // Ensure that the dma_map_addr is indeed in the lower 32-bit address space. ASSERT(object->dma_map_addr == low_addr); @@ -158,7 +159,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto if (!object->dma_map_addr) { LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); } else { - cmd_buffer.map_address = object->dma_map_addr; + cmd_buffer.map_address = static_cast(object->dma_map_addr); } } std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); @@ -184,4 +185,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector& input, return NvResult::Success; } +Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index cccc94a584..74231d5c5c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -11,17 +11,16 @@ namespace Service::Nvidia { namespace NvCore { -class SyncpointManager; class Container; +class NvMap; +class SyncpointManager; } // namespace NvCore namespace Devices { -class nvmap; class nvhost_nvdec_common : public nvdevice { public: - explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr nvmap_dev_, - NvCore::Container& core); + explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core); ~nvhost_nvdec_common() override; protected: @@ -114,12 +113,14 @@ protected: NvResult UnmapBuffer(const std::vector& input, std::vector& output); NvResult SetSubmitTimeout(const std::vector& input, std::vector& output); + Kernel::KEvent* QueryEvent(u32 event_id) override; + std::unordered_map fd_to_id{}; s32_le nvmap_fd{}; u32_le submit_timeout{}; - std::shared_ptr nvmap_dev; NvCore::Container& core; NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; std::array device_syncpoints{}; }; }; // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 66558c331b..358e89aa8e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -8,9 +8,8 @@ #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr nvmap_dev_, - NvCore::Container& core) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), core} {} +nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core) + : nvhost_nvdec_common{system_, core} {} nvhost_vic::~nvhost_vic() = default; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 6f9838b2dc..252b1e6f2a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -9,8 +9,7 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvhost_nvdec_common { public: - explicit nvhost_vic(Core::System& system_, std::shared_ptr nvmap_dev_, - NvCore::Container& core); + explicit nvhost_vic(Core::System& system_, NvCore::Container& core); ~nvhost_vic(); NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index d8518149d7..2aee68f5cb 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -2,19 +2,24 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/memory.h" + +using Core::Memory::YUZU_PAGESIZE; namespace Service::Nvidia::Devices { -nvmap::nvmap(Core::System& system_) : nvdevice{system_} { - // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to - // represent this. - CreateObject(0); -} +nvmap::nvmap(Core::System& system_, NvCore::Container& container_) + : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {} nvmap::~nvmap() = default; @@ -63,38 +68,32 @@ void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {} VAddr nvmap::GetObjectAddress(u32 handle) const { - auto object = GetObject(handle); - ASSERT(object); - ASSERT(object->status == Object::Status::Allocated); - return object->addr; + auto obj = file.GetHandle(handle); + if (obj) { + return obj->address; + } + return 0; } -u32 nvmap::CreateObject(u32 size) { - // Create a new nvmap object and obtain a handle to it. - auto object = std::make_shared(); - object->id = next_id++; - object->size = size; - object->status = Object::Status::Created; - object->refcount = 1; - - const u32 handle = next_handle++; - - handles.insert_or_assign(handle, std::move(object)); - - return handle; +std::shared_ptr nvmap::GetObject(u32 handle) const { + return file.GetHandle(handle); } NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); + LOG_WARNING(Service_NVDRV, "called, size=0x{:08X}", params.size); - if (!params.size) { - LOG_ERROR(Service_NVDRV, "Size is 0"); - return NvResult::BadValue; + std::shared_ptr handle_description{}; + auto result = + file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Failed to create Object"); + return result; } - - params.handle = CreateObject(params.size); + handle_description->orig_size = params.size; // Orig size is the unaligned size + params.handle = handle_description->id; + LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; @@ -103,42 +102,42 @@ NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) NvResult nvmap::IocAlloc(const std::vector& input, std::vector& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); + LOG_WARNING(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is 0"); + LOG_CRITICAL(Service_NVDRV, "Handle is 0"); return NvResult::BadValue; } if ((params.align - 1) & params.align) { - LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); + LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); return NvResult::BadValue; } - const u32 min_alignment = 0x1000; - if (params.align < min_alignment) { - params.align = min_alignment; + // Force page size alignment at a minimum + if (params.align < YUZU_PAGESIZE) { + params.align = YUZU_PAGESIZE; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return NvResult::BadValue; } - if (object->status == Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); + if (handle_description->allocated) { + LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); return NvResult::InsufficientMemory; } - object->flags = params.flags; - object->align = params.align; - object->kind = params.kind; - object->addr = params.addr; - object->status = Object::Status::Allocated; - + const auto result = + handle_description->Alloc(params.flags, params.align, params.kind, params.address); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); + return result; + } std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::Success; + return result; } NvResult nvmap::IocGetId(const std::vector& input, std::vector& output) { @@ -147,19 +146,20 @@ NvResult nvmap::IocGetId(const std::vector& input, std::vector& output) LOG_WARNING(Service_NVDRV, "called"); + // See the comment in FromId for extra info on this function if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is zero"); + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Error!"); + return NvResult::AccessDenied; // This will always return EPERM irrespective of if the + // handle exists or not } - params.id = object->id; - + params.id = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -168,26 +168,29 @@ NvResult nvmap::IocFromId(const std::vector& input, std::vector& output) IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "called, id:{}"); - auto itr = std::find_if(handles.begin(), handles.end(), - [&](const auto& entry) { return entry.second->id == params.id; }); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + // Handles and IDs are always the same value in nvmap however IDs can be used globally given the + // right permissions. + // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and + // so this function just does simple validation and passes through the handle id. + if (!params.id) { + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - auto& object = itr->second; - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.id)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - itr->second->refcount++; - - // Return the existing handle instead of creating a new one. - params.handle = itr->first; - + auto result = handle_description->Duplicate(false); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Error!"); + return result; + } + params.handle = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -198,35 +201,43 @@ NvResult nvmap::IocParam(const std::vector& input, std::vector& output) IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); + LOG_WARNING(Service_NVDRV, "called type={}", params.param); - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - switch (static_cast(params.param)) { - case ParamTypes::Size: - params.result = object->size; + switch (params.param) { + case HandleParameterType::Size: + params.result = static_cast(handle_description->orig_size); break; - case ParamTypes::Alignment: - params.result = object->align; + case HandleParameterType::Alignment: + params.result = static_cast(handle_description->align); break; - case ParamTypes::Heap: - // TODO(Subv): Seems to be a hardcoded value? - params.result = 0x40000000; + case HandleParameterType::Base: + params.result = static_cast(-22); // posix EINVAL break; - case ParamTypes::Kind: - params.result = object->kind; + case HandleParameterType::Heap: + if (handle_description->allocated) + params.result = 0x40000000; + else + params.result = 0x40000000; + break; + case HandleParameterType::Kind: + params.result = handle_description->kind; + break; + case HandleParameterType::IsSharedMemMapped: + params.result = handle_description->is_shared_mem_mapped; break; default: - UNIMPLEMENTED(); + return NvResult::BadValue; } std::memcpy(output.data(), ¶ms, sizeof(params)); @@ -234,46 +245,25 @@ NvResult nvmap::IocParam(const std::vector& input, std::vector& output) } NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { - // TODO(Subv): These flags are unconfirmed. - enum FreeFlags { - Freed = 0, - NotFreedYet = 1, - }; - IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "called"); - auto itr = handles.find(params.handle); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; - } - if (!itr->second->refcount) { - LOG_ERROR( - Service_NVDRV, - "There is no references to this object. The object is already freed. handle={:08X}", - params.handle); - return NvResult::BadValue; + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Handle null freed?"); + return NvResult::Success; } - itr->second->refcount--; - - params.size = itr->second->size; - - if (itr->second->refcount == 0) { - params.flags = Freed; - // The address of the nvmap is written to the output if we're finally freeing it, otherwise - // 0 is written. - params.address = itr->second->addr; + if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + params.address = freeInfo->address; + params.size = static_cast(freeInfo->size); + params.flags = NvCore::NvMap::Handle::Flags{.map_uncached = freeInfo->was_uncached}; } else { - params.flags = NotFreedYet; - params.address = 0; + // This is possible when there's internel dups or other duplicates. + LOG_CRITICAL(Service_NVDRV, "Not freed"); } - handles.erase(params.handle); - std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index d5360d6e58..c22eb57a40 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -9,15 +9,23 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia::NvCore { +class Container; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - explicit nvmap(Core::System& system_); + explicit nvmap(Core::System& system_, NvCore::Container& container); ~nvmap() override; + nvmap(nvmap const&) = delete; + nvmap& operator=(nvmap const&) = delete; + NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector& input, @@ -31,27 +39,16 @@ public: /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - /// Represents an nvmap object. - struct Object { - enum class Status { Created, Allocated }; - u32 id; - u32 size; - u32 flags; - u32 align; - u8 kind; - VAddr addr; - Status status; - u32 refcount; - u32 dma_map_addr; - }; + std::shared_ptr GetObject(u32 handle) const; - std::shared_ptr GetObject(u32 handle) const { - auto itr = handles.find(handle); - if (itr != handles.end()) { - return itr->second; - } - return {}; - } + enum class HandleParameterType : u32_le { + Size = 1, + Alignment = 2, + Base = 3, + Heap = 4, + Kind = 5, + IsSharedMemMapped = 6 + }; private: /// Id to use for the next handle that is created. @@ -60,9 +57,6 @@ private: /// Id to use for the next object that is created. u32 next_id = 0; - /// Mapping of currently allocated handles to the objects they represent. - std::unordered_map> handles; - struct IocCreateParams { // Input u32_le size{}; @@ -83,11 +77,11 @@ private: // Input u32_le handle{}; u32_le heap_mask{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; u32_le align{}; u8 kind{}; INSERT_PADDING_BYTES(7); - u64_le addr{}; + u64_le address{}; }; static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); @@ -96,14 +90,14 @@ private: INSERT_PADDING_BYTES(4); u64_le address{}; u32_le size{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; }; static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); struct IocParamParams { // Input u32_le handle{}; - u32_le param{}; + HandleParameterType param{}; // Output u32_le result{}; }; @@ -117,14 +111,15 @@ private: }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); - u32 CreateObject(u32 size); - NvResult IocCreate(const std::vector& input, std::vector& output); NvResult IocAlloc(const std::vector& input, std::vector& output); NvResult IocGetId(const std::vector& input, std::vector& output); NvResult IocFromId(const std::vector& input, std::vector& output); NvResult IocParam(const std::vector& input, std::vector& output); NvResult IocFree(const std::vector& input, std::vector& output); + + NvCore::Container& container; + NvCore::NvMap& file; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 824c0e2906..f4914d5399 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -138,21 +138,18 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger Module::Module(Core::System& system) : service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} { - auto nvmap_dev = std::make_shared(system); - devices["/dev/nvhost-as-gpu"] = std::make_shared(system, nvmap_dev); + devices["/dev/nvhost-as-gpu"] = std::make_shared(system, container); devices["/dev/nvhost-gpu"] = - std::make_shared(system, nvmap_dev, events_interface, container); + std::make_shared(system, events_interface, container); devices["/dev/nvhost-ctrl-gpu"] = std::make_shared(system, events_interface); - devices["/dev/nvmap"] = nvmap_dev; - devices["/dev/nvdisp_disp0"] = std::make_shared(system, nvmap_dev); + devices["/dev/nvmap"] = std::make_shared(system, container); + devices["/dev/nvdisp_disp0"] = std::make_shared(system, container); devices["/dev/nvhost-ctrl"] = std::make_shared(system, events_interface, container); - devices["/dev/nvhost-nvdec"] = - std::make_shared(system, nvmap_dev, container); + devices["/dev/nvhost-nvdec"] = std::make_shared(system, container); devices["/dev/nvhost-nvjpg"] = std::make_shared(system); - devices["/dev/nvhost-vic"] = - std::make_shared(system, nvmap_dev, container); + devices["/dev/nvhost-vic"] = std::make_shared(system, container); } Module::~Module() = default; From af35dbcf633d35450b333eb33334b3dd1bc050a1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 5 Nov 2021 01:44:11 +0100 Subject: [PATCH 048/245] NVDRV: Fix Open/Close and make sure each device is correctly created. --- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 178 ++++++++++++----- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 42 ++++ .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 9 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 12 +- .../service/nvdrv/devices/nvhost_nvdec.cpp | 2 + .../hle/service/nvdrv/devices/nvhost_nvdec.h | 2 +- .../nvdrv/devices/nvhost_nvdec_common.cpp | 2 + .../nvdrv/devices/nvhost_nvdec_common.h | 2 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 3 + .../hle/service/nvdrv/devices/nvhost_vic.h | 2 +- src/core/hle/service/nvdrv/nvdrv.cpp | 182 +++++++----------- src/core/hle/service/nvdrv/nvdrv.h | 56 ++---- src/core/hle/service/nvflinger/nvflinger.cpp | 7 +- src/core/hle/service/nvflinger/nvflinger.h | 1 + 14 files changed, 296 insertions(+), 204 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 51c40f6202..122c1d5e12 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -3,9 +3,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 // or any later version Refer to the license.txt file included. +#include #include #include +#include #include "common/assert.h" #include "common/logging/log.h" #include "common/scope_exit.h" @@ -22,8 +24,19 @@ namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, NvCore::Container& core_) : nvdevice{system_}, events_interface{events_interface_}, core{core_}, - syncpoint_manager{core_.GetSyncpointManager()} {} -nvhost_ctrl::~nvhost_ctrl() = default; + syncpoint_manager{core_.GetSyncpointManager()} { + events_interface.RegisterForSignal(this); +} + +nvhost_ctrl::~nvhost_ctrl() { + events_interface.UnregisterForSignal(this); + for (auto& event : events) { + if (!event.registered) { + continue; + } + events_interface.FreeEvent(event.kevent); + } +} NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { @@ -87,7 +100,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector SCOPE_EXIT({ std::memcpy(output.data(), ¶ms, sizeof(params)); if (must_unmark_fail) { - events_interface.fails[event_id] = 0; + events[event_id].fails = 0; } }); @@ -116,12 +129,12 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector auto& gpu = system.GPU(); const u32 target_value = params.fence.value; - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); u32 slot = [&]() { if (is_allocation) { params.value.raw = 0; - return events_interface.FindFreeEvent(fence_id); + return FindFreeNvEvent(fence_id); } else { return params.value.raw; } @@ -130,7 +143,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector must_unmark_fail = true; const auto check_failing = [&]() { - if (events_interface.fails[slot] > 2) { + if (events[slot].fails > 2) { { auto lk = system.StallProcesses(); gpu.WaitFence(fence_id, target_value); @@ -142,6 +155,10 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector return false; }; + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (params.timeout == 0) { if (check_failing()) { return NvResult::Success; @@ -149,17 +166,13 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector return NvResult::Timeout; } - if (slot >= MaxNvEvents) { + auto& event = events[slot]; + + if (!event.registered) { return NvResult::BadParameter; } - auto* event = events_interface.events[slot]; - - if (!event) { - return NvResult::BadParameter; - } - - if (events_interface.IsBeingUsed(slot)) { + if (event.IsBeingUsed()) { return NvResult::BadParameter; } @@ -169,9 +182,9 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector params.value.raw = 0; - events_interface.status[slot].store(EventState::Waiting, std::memory_order_release); - events_interface.assigned_syncpt[slot] = fence_id; - events_interface.assigned_value[slot] = target_value; + event.status.store(EventState::Waiting, std::memory_order_release); + event.assigned_syncpt = fence_id; + event.assigned_value = target_value; if (is_allocation) { params.value.syncpoint_id_for_allocation.Assign(static_cast(fence_id)); params.value.event_allocated.Assign(1); @@ -189,15 +202,17 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) { return NvResult::BadParameter; } - if (!events_interface.registered[slot]) { + auto& event = events[slot]; + + if (!event.registered) { return NvResult::Success; } - if (events_interface.IsBeingUsed(slot)) { + if (event.IsBeingUsed()) { return NvResult::Busy; } - events_interface.Free(slot); + FreeNvEvent(slot); return NvResult::Success; } @@ -210,15 +225,15 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector& input, std::ve return NvResult::BadParameter; } - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); - if (events_interface.registered[event_id]) { + if (events[event_id].registered) { const auto result = FreeEvent(event_id); if (result != NvResult::Success) { return result; } } - events_interface.Create(event_id); + CreateNvEvent(event_id); return NvResult::Success; } @@ -229,7 +244,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector& input, const u32 event_id = params.user_event_id & 0x00FF; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); return FreeEvent(event_id); } @@ -244,44 +259,121 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v return NvResult::BadParameter; } - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); - if (events_interface.status[event_id].exchange( - EventState::Cancelling, std::memory_order_acq_rel) == EventState::Waiting) { - system.GPU().CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id], - events_interface.assigned_value[event_id]); - syncpoint_manager.RefreshSyncpoint(events_interface.assigned_syncpt[event_id]); + auto& event = events[event_id]; + if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == + EventState::Waiting) { + system.GPU().CancelSyncptInterrupt(event.assigned_syncpt, event.assigned_value); + syncpoint_manager.RefreshSyncpoint(event.assigned_syncpt); } - events_interface.fails[event_id]++; - events_interface.status[event_id].store(EventState::Cancelled, std::memory_order_release); - events_interface.events[event_id]->GetWritableEvent().Clear(); + event.fails++; + event.status.store(EventState::Cancelled, std::memory_order_release); + event.kevent->GetWritableEvent().Clear(); return NvResult::Success; } Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { - const auto event = SyncpointEventValue{.raw = event_id}; + const auto desired_event = SyncpointEventValue{.raw = event_id}; - const bool allocated = event.event_allocated.Value() != 0; - const u32 slot{allocated ? event.partial_slot.Value() : static_cast(event.slot)}; + const bool allocated = desired_event.event_allocated.Value() != 0; + const u32 slot{allocated ? desired_event.partial_slot.Value() + : static_cast(desired_event.slot)}; if (slot >= MaxNvEvents) { ASSERT(false); return nullptr; } - const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value() - : event.syncpoint_id.Value()}; + const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value() + : desired_event.syncpoint_id.Value()}; - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); - if (events_interface.registered[slot] && - events_interface.assigned_syncpt[slot] == syncpoint_id) { - ASSERT(events_interface.events[slot]); - return events_interface.events[slot]; + auto& event = events[slot]; + if (event.registered && event.assigned_syncpt == syncpoint_id) { + ASSERT(event.kevent); + return event.kevent; } // Is this possible in hardware? ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); return nullptr; } +std::unique_lock nvhost_ctrl::NvEventsLock() { + return std::unique_lock(events_mutex); +} + +void nvhost_ctrl::CreateNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(!event.kevent); + ASSERT(!event.registered); + ASSERT(!event.IsBeingUsed()); + event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id)); + event.status = EventState::Available; + event.registered = true; + const u64 mask = 1ULL << event_id; + event.fails = 0; + events_mask |= mask; + event.assigned_syncpt = 0; +} + +void nvhost_ctrl::FreeNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(event.kevent); + ASSERT(event.registered); + ASSERT(!event.IsBeingUsed()); + events_interface.FreeEvent(event.kevent); + event.kevent = nullptr; + event.status = EventState::Available; + event.registered = false; + const u64 mask = ~(1ULL << event_id); + events_mask &= mask; +} + +u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { + u32 slot{MaxNvEvents}; + u32 free_slot{MaxNvEvents}; + for (u32 i = 0; i < MaxNvEvents; i++) { + auto& event = events[i]; + if (event.registered) { + if (!event.IsBeingUsed()) { + slot = i; + if (event.assigned_syncpt == syncpoint_id) { + return slot; + } + } + } else if (free_slot == MaxNvEvents) { + free_slot = i; + } + } + if (free_slot < MaxNvEvents) { + CreateNvEvent(free_slot); + return free_slot; + } + + if (slot < MaxNvEvents) { + return slot; + } + + LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); + return 0; +} + +void nvhost_ctrl::SignalNvEvent(u32 syncpoint_id, u32 value) { + const u32 max = MaxNvEvents - std::countl_zero(events_mask); + const u32 min = std::countr_zero(events_mask); + for (u32 i = min; i < max; i++) { + auto& event = events[i]; + if (event.assigned_syncpt != syncpoint_id || event.assigned_value != value) { + continue; + } + if (event.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + event.kevent->GetWritableEvent().Signal(); + } + event.status.store(EventState::Signalled, std::memory_order_release); + } +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 9fd46ea5f0..f2fc5d0472 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -53,7 +53,49 @@ public: }; static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); + void SignalNvEvent(u32 syncpoint_id, u32 value); + private: + struct InternalEvent { + // Mask representing registered events + + // Each kernel event associated to an NV event + Kernel::KEvent* kevent{}; + // The status of the current NVEvent + std::atomic status{}; + + // Tells the NVEvent that it has failed. + u32 fails{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + u32 assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + u32 assigned_value{}; + + // Tells if an NVEvent is registered or not + bool registered{}; + + bool IsBeingUsed() { + const auto current_status = status.load(std::memory_order_acquire); + return current_status == EventState::Waiting || + current_status == EventState::Cancelling || + current_status == EventState::Signalling; + } + }; + + std::unique_lock NvEventsLock(); + + void CreateNvEvent(u32 event_id); + + void FreeNvEvent(u32 event_id); + + u32 FindFreeNvEvent(u32 syncpoint_id); + + std::array events{}; + std::mutex events_mutex; + u64 events_mask{}; + struct IocSyncptReadParams { u32_le id{}; u32_le value{}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index e353408eb9..ced57dfe66 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -13,10 +13,13 @@ namespace Service::Nvidia::Devices { nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) : nvdevice{system_}, events_interface{events_interface_} { - error_notifier_event = events_interface.CreateNonCtrlEvent("CtrlGpuErrorNotifier"); - unknown_event = events_interface.CreateNonCtrlEvent("CtrlGpuUknownEvent"); + error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); + unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); +} +nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { + events_interface.FreeEvent(error_notifier_event); + events_interface.FreeEvent(unknown_event); } -nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index e7921ade23..cb54ee5a41 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -30,13 +30,17 @@ nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, channel_fence.id = syncpoint_manager.AllocateSyncpoint(); channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); sm_exception_breakpoint_int_report_event = - events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointInt"); + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); sm_exception_breakpoint_pause_report_event = - events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointPause"); - error_notifier_event = events_interface.CreateNonCtrlEvent("GpuChannelErrorNotifier"); + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause"); + error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier"); } -nvhost_gpu::~nvhost_gpu() = default; +nvhost_gpu::~nvhost_gpu() { + events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); + events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); + events_interface.FreeEvent(error_notifier_event); +} NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index aa1a00832a..00947ea199 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -10,6 +10,8 @@ namespace Service::Nvidia::Devices { +u32 nvhost_nvdec::next_id{}; + nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core) : nvhost_nvdec_common{system_, core} {} nvhost_nvdec::~nvhost_nvdec() = default; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index fef4b32167..3261ce1d40 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -24,7 +24,7 @@ public: void OnClose(DeviceFD fd) override; private: - u32 next_id{}; + static u32 next_id; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index e76c9e5eda..77e6a1cd64 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -45,6 +45,8 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } } // Anonymous namespace +std::unordered_map nvhost_nvdec_common::fd_to_id{}; + nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_) : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} {} diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 74231d5c5c..53029af6ad 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -115,7 +115,7 @@ protected: Kernel::KEvent* QueryEvent(u32 event_id) override; - std::unordered_map fd_to_id{}; + static std::unordered_map fd_to_id; s32_le nvmap_fd{}; u32_le submit_timeout{}; NvCore::Container& core; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 358e89aa8e..c89ff6b27d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -8,6 +8,9 @@ #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { + +u32 nvhost_vic::next_id{}; + nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core) : nvhost_nvdec_common{system_, core} {} diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 252b1e6f2a..59e23b41ef 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -23,6 +23,6 @@ public: void OnClose(DeviceFD fd) override; private: - u32 next_id{}; + static u32 next_id; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index f4914d5399..ff8c7c13cc 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -3,7 +3,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 // or any later version Refer to the license.txt file included. -#include #include #include @@ -30,101 +29,39 @@ namespace Service::Nvidia { -EventInterface::EventInterface(Module& module_) : module{module_} { - events_mask = 0; - for (u32 i = 0; i < MaxNvEvents; i++) { - status[i] = EventState::Available; - events[i] = nullptr; - registered[i] = false; +EventInterface::EventInterface(Module& module_) : module{module_} {} + +EventInterface::~EventInterface() = default; + +void EventInterface::RegisterForSignal(Devices::nvhost_ctrl* device) { + std::unique_lock lk(guard); + on_signal.push_back(device); +} + +void EventInterface::UnregisterForSignal(Devices::nvhost_ctrl* device) { + std::unique_lock lk(guard); + auto it = std::find(on_signal.begin(), on_signal.end(), device); + if (it != on_signal.end()) { + on_signal.erase(it); } } -EventInterface::~EventInterface() { - auto lk = Lock(); - for (u32 i = 0; i < MaxNvEvents; i++) { - if (registered[i]) { - module.service_context.CloseEvent(events[i]); - events[i] = nullptr; - registered[i] = false; - } - } - for (auto* event : basic_events) { - module.service_context.CloseEvent(event); +void EventInterface::Signal(u32 syncpoint_id, u32 value) { + std::unique_lock lk(guard); + for (auto* device : on_signal) { + device->SignalNvEvent(syncpoint_id, value); } } -std::unique_lock EventInterface::Lock() { - return std::unique_lock(events_mutex); -} - -void EventInterface::Signal(u32 event_id) { - if (status[event_id].exchange(EventState::Signalling, std::memory_order_acq_rel) == - EventState::Waiting) { - events[event_id]->GetWritableEvent().Signal(); - } - status[event_id].store(EventState::Signalled, std::memory_order_release); -} - -void EventInterface::Create(u32 event_id) { - ASSERT(!events[event_id]); - ASSERT(!registered[event_id]); - ASSERT(!IsBeingUsed(event_id)); - events[event_id] = - module.service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", event_id)); - status[event_id] = EventState::Available; - registered[event_id] = true; - const u64 mask = 1ULL << event_id; - fails[event_id] = 0; - events_mask |= mask; - assigned_syncpt[event_id] = 0; -} - -void EventInterface::Free(u32 event_id) { - ASSERT(events[event_id]); - ASSERT(registered[event_id]); - ASSERT(!IsBeingUsed(event_id)); - module.service_context.CloseEvent(events[event_id]); - events[event_id] = nullptr; - status[event_id] = EventState::Available; - registered[event_id] = false; - const u64 mask = ~(1ULL << event_id); - events_mask &= mask; -} - -u32 EventInterface::FindFreeEvent(u32 syncpoint_id) { - u32 slot{MaxNvEvents}; - u32 free_slot{MaxNvEvents}; - for (u32 i = 0; i < MaxNvEvents; i++) { - if (registered[i]) { - if (!IsBeingUsed(i)) { - slot = i; - if (assigned_syncpt[i] == syncpoint_id) { - return slot; - } - } - } else if (free_slot == MaxNvEvents) { - free_slot = i; - } - } - if (free_slot < MaxNvEvents) { - Create(free_slot); - return free_slot; - } - - if (slot < MaxNvEvents) { - return slot; - } - - LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); - return 0; -} - -Kernel::KEvent* EventInterface::CreateNonCtrlEvent(std::string name) { +Kernel::KEvent* EventInterface::CreateEvent(std::string name) { Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); - basic_events.push_back(new_event); return new_event; } +void EventInterface::FreeEvent(Kernel::KEvent* event) { + module.service_context.CloseEvent(event); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared(system); @@ -138,18 +75,50 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger Module::Module(Core::System& system) : service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} { - devices["/dev/nvhost-as-gpu"] = std::make_shared(system, container); - devices["/dev/nvhost-gpu"] = - std::make_shared(system, events_interface, container); - devices["/dev/nvhost-ctrl-gpu"] = - std::make_shared(system, events_interface); - devices["/dev/nvmap"] = std::make_shared(system, container); - devices["/dev/nvdisp_disp0"] = std::make_shared(system, container); - devices["/dev/nvhost-ctrl"] = - std::make_shared(system, events_interface, container); - devices["/dev/nvhost-nvdec"] = std::make_shared(system, container); - devices["/dev/nvhost-nvjpg"] = std::make_shared(system); - devices["/dev/nvhost-vic"] = std::make_shared(system, container); + builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, events_interface); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = std::make_shared(system); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; } Module::~Module() = default; @@ -169,18 +138,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const { } DeviceFD Module::Open(const std::string& device_name) { - if (devices.find(device_name) == devices.end()) { + auto it = builders.find(device_name); + if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); return INVALID_NVDRV_FD; } - auto device = devices[device_name]; const DeviceFD fd = next_fd++; + auto& builder = it->second; + auto device = builder(fd)->second; device->OnOpen(fd); - open_files[fd] = std::move(device); - return fd; } @@ -256,14 +225,7 @@ NvResult Module::Close(DeviceFD fd) { } void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - const u32 max = MaxNvEvents - std::countl_zero(events_interface.events_mask); - const u32 min = std::countr_zero(events_interface.events_mask); - for (u32 i = min; i < max; i++) { - if (events_interface.assigned_syncpt[i] == syncpoint_id && - events_interface.assigned_value[i] == value) { - events_interface.Signal(i); - } - } + events_interface.Signal(syncpoint_id, value); } NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 96adf2ffbc..3983794bb7 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -5,6 +5,7 @@ #pragma once +#include #include #include #include @@ -38,7 +39,8 @@ class SyncpointManager; namespace Devices { class nvdevice; -} +class nvhost_ctrl; +} // namespace Devices class Module; @@ -47,47 +49,19 @@ public: EventInterface(Module& module_); ~EventInterface(); - // Mask representing registered events - u64 events_mask{}; - // Each kernel event associated to an NV event - std::array events{}; - // The status of the current NVEvent - std::array, MaxNvEvents> status{}; - // Tells if an NVEvent is registered or not - std::array registered{}; - // Tells the NVEvent that it has failed. - std::array fails{}; - // When an NVEvent is waiting on GPU interrupt, this is the sync_point - // associated with it. - std::array assigned_syncpt{}; - // This is the value of the GPU interrupt for which the NVEvent is waiting - // for. - std::array assigned_value{}; - // Constant to denote an unasigned syncpoint. - static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; + void RegisterForSignal(Devices::nvhost_ctrl*); + void UnregisterForSignal(Devices::nvhost_ctrl*); - bool IsBeingUsed(u32 event_id) { - const auto current_status = status[event_id].load(std::memory_order_acquire); - return current_status == EventState::Waiting || current_status == EventState::Cancelling || - current_status == EventState::Signalling; - } + void Signal(u32 syncpoint_id, u32 value); - std::unique_lock Lock(); + Kernel::KEvent* CreateEvent(std::string name); - void Signal(u32 event_id); - - void Create(u32 event_id); - - void Free(u32 event_id); - - u32 FindFreeEvent(u32 syncpoint_id); - - Kernel::KEvent* CreateNonCtrlEvent(std::string name); + void FreeEvent(Kernel::KEvent* event); private: - std::mutex events_mutex; Module& module; - std::vector basic_events; + std::mutex guard; + std::list on_signal; }; class Module final { @@ -97,9 +71,9 @@ public: /// Returns a pointer to one of the available devices, identified by its name. template - std::shared_ptr GetDevice(const std::string& name) { - auto itr = devices.find(name); - if (itr == devices.end()) + std::shared_ptr GetDevice(DeviceFD fd) { + auto itr = open_files.find(fd); + if (itr == open_files.end()) return nullptr; return std::static_pointer_cast(itr->second); } @@ -132,8 +106,9 @@ private: /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; + using FilesContainerType = std::unordered_map>; /// Mapping of file descriptors to the devices they reference. - std::unordered_map> open_files; + FilesContainerType open_files; /// Mapping of device node names to their implementation. std::unordered_map> devices; @@ -147,6 +122,7 @@ private: void CreateEvent(u32 event_id); void FreeEvent(u32 event_id); + std::unordered_map> builders; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 4246e5e259..8c3013f833 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -105,10 +105,15 @@ NVFlinger::~NVFlinger() { display.GetLayer(layer).Core().NotifyShutdown(); } } + + if (nvdrv) { + nvdrv->Close(disp_fd); + } } void NVFlinger::SetNVDrvInstance(std::shared_ptr instance) { nvdrv = std::move(instance); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); } std::optional NVFlinger::OpenDisplay(std::string_view name) { @@ -276,7 +281,7 @@ void NVFlinger::Compose() { // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice("/dev/nvdisp_disp0"); + auto nvdisp = nvdrv->GetDevice(disp_fd); ASSERT(nvdisp); Common::Rectangle crop_rect{ diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 3bbe5d92b9..b62615de27 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -116,6 +116,7 @@ private: void SplitVSync(std::stop_token stop_token); std::shared_ptr nvdrv; + s32 disp_fd; std::list displays; From 68d9504a046f40ce4ada54b0eba7228e659970de Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 5 Nov 2021 02:11:46 +0100 Subject: [PATCH 049/245] NVMAP: Fix the Free return parameters. --- src/core/hle/service/nvdrv/core/nvmap.cpp | 4 ++- src/core/hle/service/nvdrv/core/nvmap.h | 1 + src/core/hle/service/nvdrv/devices/nvmap.cpp | 28 ++++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 281381cc41..1126daeb5e 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -11,7 +11,9 @@ using Core::Memory::YUZU_PAGESIZE; namespace Service::Nvidia::NvCore { -NvMap::Handle::Handle(u64 size, Id id) : size(size), aligned_size(size), orig_size(size), id(id) {} +NvMap::Handle::Handle(u64 size, Id id) : size(size), aligned_size(size), orig_size(size), id(id) { + flags.raw = 0; +} NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { std::scoped_lock lock(mutex); diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index 994c70e6fa..5e6c73589a 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -44,6 +44,7 @@ public: std::optional>::iterator> unmap_queue_entry{}; union Flags { + u32 raw; BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was //!< allocated with a fixed address diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 2aee68f5cb..57f58055d3 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -82,7 +82,7 @@ std::shared_ptr nvmap::GetObject(u32 handle) const { NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called, size=0x{:08X}", params.size); + LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); std::shared_ptr handle_description{}; auto result = @@ -102,7 +102,7 @@ NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) NvResult nvmap::IocAlloc(const std::vector& input, std::vector& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called, addr={:X}", params.address); + LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { LOG_CRITICAL(Service_NVDRV, "Handle is 0"); @@ -144,7 +144,7 @@ NvResult nvmap::IocGetId(const std::vector& input, std::vector& output) IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); // See the comment in FromId for extra info on this function if (!params.handle) { @@ -168,26 +168,26 @@ NvResult nvmap::IocFromId(const std::vector& input, std::vector& output) IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called, id:{}"); + LOG_DEBUG(Service_NVDRV, "called, id:{}"); // Handles and IDs are always the same value in nvmap however IDs can be used globally given the // right permissions. // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and // so this function just does simple validation and passes through the handle id. if (!params.id) { - LOG_CRITICAL(Service_NVDRV, "Error!"); + LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!"); return NvResult::BadValue; } auto handle_description{file.GetHandle(params.id)}; if (!handle_description) { - LOG_CRITICAL(Service_NVDRV, "Error!"); + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); return NvResult::BadValue; } auto result = handle_description->Duplicate(false); if (result != NvResult::Success) { - LOG_CRITICAL(Service_NVDRV, "Error!"); + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); return result; } params.handle = handle_description->id; @@ -201,16 +201,16 @@ NvResult nvmap::IocParam(const std::vector& input, std::vector& output) IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called type={}", params.param); + LOG_DEBUG(Service_NVDRV, "called type={}", params.param); if (!params.handle) { - LOG_CRITICAL(Service_NVDRV, "Error!"); + LOG_CRITICAL(Service_NVDRV, "Invalid handle!"); return NvResult::BadValue; } auto handle_description{file.GetHandle(params.handle)}; if (!handle_description) { - LOG_CRITICAL(Service_NVDRV, "Error!"); + LOG_CRITICAL(Service_NVDRV, "Not registered handle!"); return NvResult::BadValue; } @@ -228,7 +228,7 @@ NvResult nvmap::IocParam(const std::vector& input, std::vector& output) if (handle_description->allocated) params.result = 0x40000000; else - params.result = 0x40000000; + params.result = 0; break; case HandleParameterType::Kind: params.result = handle_description->kind; @@ -248,7 +248,7 @@ NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); if (!params.handle) { LOG_CRITICAL(Service_NVDRV, "Handle null freed?"); @@ -258,10 +258,10 @@ NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { if (auto freeInfo{file.FreeHandle(params.handle, false)}) { params.address = freeInfo->address; params.size = static_cast(freeInfo->size); - params.flags = NvCore::NvMap::Handle::Flags{.map_uncached = freeInfo->was_uncached}; + params.flags.raw = 0; + params.flags.map_uncached = freeInfo->was_uncached; } else { // This is possible when there's internel dups or other duplicates. - LOG_CRITICAL(Service_NVDRV, "Not freed"); } std::memcpy(output.data(), ¶ms, sizeof(params)); From ad038609c877ad54dde7b9592f0584deb56a27c5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 5 Nov 2021 02:57:14 +0100 Subject: [PATCH 050/245] NVDRV: Fix clearing when destroying. --- src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 13 +++++++------ src/core/hle/service/nvdrv/nvdrv.cpp | 7 ++----- src/core/hle/service/nvdrv/nvdrv.h | 3 --- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 122c1d5e12..abde2a6d35 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -24,12 +24,9 @@ namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, NvCore::Container& core_) : nvdevice{system_}, events_interface{events_interface_}, core{core_}, - syncpoint_manager{core_.GetSyncpointManager()} { - events_interface.RegisterForSignal(this); -} + syncpoint_manager{core_.GetSyncpointManager()} {} nvhost_ctrl::~nvhost_ctrl() { - events_interface.UnregisterForSignal(this); for (auto& event : events) { if (!event.registered) { continue; @@ -77,8 +74,12 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& return NvResult::NotImplemented; } -void nvhost_ctrl::OnOpen(DeviceFD fd) {} -void nvhost_ctrl::OnClose(DeviceFD fd) {} +void nvhost_ctrl::OnOpen(DeviceFD fd) { + events_interface.RegisterForSignal(this); +} +void nvhost_ctrl::OnClose(DeviceFD fd) { + events_interface.UnregisterForSignal(this); +} NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector& input, std::vector& output) { IocGetConfigParams params{}; diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index ff8c7c13cc..208de0b756 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -29,7 +29,7 @@ namespace Service::Nvidia { -EventInterface::EventInterface(Module& module_) : module{module_} {} +EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} EventInterface::~EventInterface() = default; @@ -40,10 +40,7 @@ void EventInterface::RegisterForSignal(Devices::nvhost_ctrl* device) { void EventInterface::UnregisterForSignal(Devices::nvhost_ctrl* device) { std::unique_lock lk(guard); - auto it = std::find(on_signal.begin(), on_signal.end(), device); - if (it != on_signal.end()) { - on_signal.erase(it); - } + on_signal.remove(device); } void EventInterface::Signal(u32 syncpoint_id, u32 value) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 3983794bb7..1fe98cf32c 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -110,9 +110,6 @@ private: /// Mapping of file descriptors to the devices they reference. FilesContainerType open_files; - /// Mapping of device node names to their implementation. - std::unordered_map> devices; - KernelHelpers::ServiceContext service_context; EventInterface events_interface; From c77b8df12efab9f36f007ff3b309d43588a71260 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 5 Nov 2021 03:10:20 +0100 Subject: [PATCH 051/245] NVASGPU: Fix Remap. --- src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9283d6aec9..b1c683511d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -144,6 +144,14 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", entry.offset, entry.nvmap_handle, entry.pages); + if (entry.nvmap_handle == 0) { + // If nvmap handle is null, we should unmap instead. + const auto offset{static_cast(entry.offset) << 0x10}; + const auto size{static_cast(entry.pages) << 0x10}; + system.GPU().MemoryManager().Unmap(offset, size); + continue; + } + const auto object{nvmap.GetHandle(entry.nvmap_handle)}; if (!object) { LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); From 139ea93512aeead8a4aee3910a3de86eb109a838 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 5 Nov 2021 15:52:31 +0100 Subject: [PATCH 052/245] VideoCore: implement channels on gpu caches. --- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 34 +- .../hle/service/nvdrv/devices/nvhost_as_gpu.h | 14 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 34 +- .../hle/service/nvdrv/devices/nvhost_gpu.h | 9 + src/core/hle/service/nvdrv/devices/nvmap.cpp | 2 +- src/core/hle/service/nvdrv/nvdrv.cpp | 2 +- src/video_core/CMakeLists.txt | 8 + src/video_core/buffer_cache/buffer_cache.h | 103 ++-- src/video_core/control/channel_state.cpp | 44 ++ src/video_core/control/channel_state.h | 69 +++ .../control/channel_state_cache.cpp | 5 + src/video_core/control/channel_state_cache.h | 68 +++ .../control/channel_state_cache.inc | 64 +++ src/video_core/control/scheduler.cpp | 31 ++ src/video_core/control/scheduler.h | 38 ++ src/video_core/dma_pusher.cpp | 23 +- src/video_core/dma_pusher.h | 13 +- src/video_core/engines/puller.cpp | 297 +++++++++++ src/video_core/engines/puller.h | 179 +++++++ src/video_core/fence_manager.h | 28 +- src/video_core/gpu.cpp | 482 ++++-------------- src/video_core/gpu.h | 55 +- src/video_core/gpu_thread.cpp | 14 +- src/video_core/gpu_thread.h | 12 +- src/video_core/memory_manager.cpp | 5 - src/video_core/query_cache.h | 18 +- src/video_core/rasterizer_interface.h | 9 + .../renderer_opengl/gl_fence_manager.cpp | 4 +- .../renderer_opengl/gl_fence_manager.h | 4 +- .../renderer_opengl/gl_query_cache.cpp | 5 +- .../renderer_opengl/gl_query_cache.h | 3 +- .../renderer_opengl/gl_rasterizer.cpp | 14 +- .../renderer_opengl/gl_shader_cache.cpp | 39 +- .../renderer_opengl/gl_shader_cache.h | 9 +- .../renderer_vulkan/renderer_vulkan.cpp | 17 +- .../renderer_vulkan/vk_fence_manager.cpp | 4 +- .../renderer_vulkan/vk_fence_manager.h | 4 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 28 +- .../renderer_vulkan/vk_pipeline_cache.h | 6 +- .../renderer_vulkan/vk_query_cache.cpp | 7 +- .../renderer_vulkan/vk_query_cache.h | 5 +- .../renderer_vulkan/vk_rasterizer.cpp | 87 +++- .../renderer_vulkan/vk_rasterizer.h | 20 +- .../renderer_vulkan/vk_state_tracker.cpp | 13 +- .../renderer_vulkan/vk_state_tracker.h | 22 +- src/video_core/shader_cache.cpp | 33 +- src/video_core/shader_cache.h | 15 +- src/video_core/texture_cache/image_base.h | 3 + src/video_core/texture_cache/texture_cache.h | 209 +++++--- .../texture_cache/texture_cache_base.h | 75 ++- 50 files changed, 1469 insertions(+), 817 deletions(-) create mode 100644 src/video_core/control/channel_state.cpp create mode 100644 src/video_core/control/channel_state.h create mode 100644 src/video_core/control/channel_state_cache.cpp create mode 100644 src/video_core/control/channel_state_cache.h create mode 100644 src/video_core/control/channel_state_cache.inc create mode 100644 src/video_core/control/scheduler.cpp create mode 100644 src/video_core/control/scheduler.h create mode 100644 src/video_core/engines/puller.cpp create mode 100644 src/video_core/engines/puller.h diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index b1c683511d..9946ce6246 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -10,13 +10,17 @@ #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/control/channel_state.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Service::Nvidia::Devices { -nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, NvCore::Container& core) - : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) + : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, + gmmu{std::make_shared(system)} {} nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -102,9 +106,9 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector& input, std::vector< const auto size{static_cast(params.pages) * static_cast(params.page_size)}; if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { - params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); + params.offset = *(gmmu->AllocateFixed(params.offset, size)); } else { - params.offset = system.GPU().MemoryManager().Allocate(size, params.align); + params.offset = gmmu->Allocate(size, params.align); } auto result = NvResult::Success; @@ -124,8 +128,7 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector& input, std::vector& LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); - system.GPU().MemoryManager().Unmap(params.offset, - static_cast(params.pages) * params.page_size); + gmmu->Unmap(params.offset, static_cast(params.pages) * params.page_size); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -148,7 +151,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out // If nvmap handle is null, we should unmap instead. const auto offset{static_cast(entry.offset) << 0x10}; const auto size{static_cast(entry.pages) << 0x10}; - system.GPU().MemoryManager().Unmap(offset, size); + gmmu->Unmap(offset, size); continue; } @@ -162,8 +165,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out const auto offset{static_cast(entry.offset) << 0x10}; const auto size{static_cast(entry.pages) << 0x10}; const auto map_offset{static_cast(entry.map_offset) << 0x10}; - const auto addr{ - system.GPU().MemoryManager().Map(object->address + map_offset, offset, size)}; + const auto addr{gmmu->Map(object->address + map_offset, offset, size)}; if (!addr) { LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); @@ -186,13 +188,12 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector(buffer_map->CpuAddr() + params.buffer_offset)}; const auto gpu_addr{static_cast(params.offset + params.buffer_offset)}; - if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { + if (!gmmu->Map(cpu_addr, gpu_addr, params.mapping_size)) { LOG_CRITICAL(Service_NVDRV, "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " "mapping_size = {}, offset={}", @@ -238,9 +239,9 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorMapAllocate(physical_address, size, page_size); } else { - params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); + params.offset = gmmu->Map(physical_address, params.offset, size); } auto result = NvResult::Success; @@ -262,7 +263,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vectorUnmap(params.offset, *size); } else { LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); } @@ -274,9 +275,10 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vector& input, std::vector& output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); + LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); - channel = params.fd; + auto gpu_channel_device = module.GetDevice(params.fd); + gpu_channel_device->channel_state->memory_manager = gmmu; return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 67d2f1e877..4ecae3caf6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -13,6 +13,14 @@ #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Tegra { +class MemoryManager; +} // namespace Tegra + +namespace Service::Nvidia { +class Module; +} + namespace Service::Nvidia::NvCore { class Container; class NvMap; @@ -34,7 +42,7 @@ DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(Core::System& system_, NvCore::Container& core); + explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); ~nvhost_as_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -187,9 +195,13 @@ private: void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); std::optional RemoveBufferMap(GPUVAddr gpu_addr); + Module& module; + NvCore::Container& container; NvCore::NvMap& nvmap; + std::shared_ptr gmmu; + // This is expected to be ordered, therefore we must use a map, not unordered_map std::map buffer_mappings; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index cb54ee5a41..38d45cb79a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -11,12 +11,14 @@ #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/memory.h" +#include "video_core/control/channel_state.h" +#include "video_core/engines/puller.h" #include "video_core/gpu.h" namespace Service::Nvidia::Devices { namespace { -Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { - Tegra::GPU::FenceAction result{}; +Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) { + Tegra::Engines::Puller::FenceAction result{}; result.op.Assign(op); result.syncpoint_id.Assign(syncpoint_id); return {result.raw}; @@ -26,7 +28,8 @@ Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoi nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, NvCore::Container& core_) : nvdevice{system_}, events_interface{events_interface_}, core{core_}, - syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} { + syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, + channel_state{system.GPU().AllocateChannel()} { channel_fence.id = syncpoint_manager.AllocateSyncpoint(); channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); sm_exception_breakpoint_int_report_event = @@ -180,6 +183,12 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector& input, std::vectorinitiated) { + LOG_CRITICAL(Service_NVDRV, "Already allocated!"); + return NvResult::AlreadyAllocated; + } + + system.GPU().InitChannel(*channel_state); channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); params.fence_out = channel_fence; @@ -206,7 +215,7 @@ static std::vector BuildWaitCommandList(NvFence fence) { {fence.value}, Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, Tegra::SubmissionMode::Increasing), - BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id), }; } @@ -220,7 +229,8 @@ static std::vector BuildIncrementCommandList(NvFence fence for (u32 count = 0; count < add_increment; ++count) { result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, Tegra::SubmissionMode::Increasing)); - result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); + result.emplace_back( + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); } return result; @@ -247,11 +257,13 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector auto& gpu = system.GPU(); + const auto bind_id = channel_state->bind_id; + params.fence_out.id = channel_fence.id; if (params.flags.add_wait.Value() && !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { - gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); } if (params.flags.add_increment.Value() || params.flags.increment.Value()) { @@ -262,15 +274,15 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); } - gpu.PushGPUEntries(std::move(entries)); + gpu.PushGPUEntries(bind_id, std::move(entries)); if (params.flags.add_increment.Value()) { if (params.flags.suppress_wfi) { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildIncrementCommandList( + params.fence_out, params.AddIncrementValue())}); } else { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildIncrementWithWfiCommandList( + params.fence_out, params.AddIncrementValue())}); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 440c0c42d5..3a65ed06db 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -13,6 +13,12 @@ #include "core/hle/service/nvdrv/nvdata.h" #include "video_core/dma_pusher.h" +namespace Tegra { +namespace Control { +struct ChannelState; +} +} // namespace Tegra + namespace Service::Nvidia { namespace NvCore { @@ -26,6 +32,7 @@ class EventInterface; namespace Service::Nvidia::Devices { +class nvhost_as_gpu; class nvmap; class nvhost_gpu final : public nvdevice { public: @@ -46,6 +53,7 @@ public: Kernel::KEvent* QueryEvent(u32 event_id) override; private: + friend class nvhost_as_gpu; enum class CtxObjects : u32_le { Ctx2D = 0x902D, Ctx3D = 0xB197, @@ -204,6 +212,7 @@ private: NvCore::Container& core; NvCore::SyncpointManager& syncpoint_manager; NvCore::NvMap& nvmap; + std::shared_ptr channel_state; NvFence channel_fence; // Events diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 57f58055d3..279997e813 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -168,7 +168,7 @@ NvResult nvmap::IocFromId(const std::vector& input, std::vector& output) IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "called, id:{}"); + LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); // Handles and IDs are always the same value in nvmap however IDs can be used globally given the // right permissions. diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 208de0b756..b39a4c6db5 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -74,7 +74,7 @@ Module::Module(Core::System& system) : service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} { builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { std::shared_ptr device = - std::make_shared(system, container); + std::make_shared(system, *this, container); return open_files.emplace(fd, device).first; }; builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 5b38083516..e216c51a21 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -35,6 +35,12 @@ add_library(video_core STATIC command_classes/vic.h compatible_formats.cpp compatible_formats.h + control/channel_state.cpp + control/channel_state.h + control/channel_state_cache.cpp + control/channel_state_cache.h + control/scheduler.cpp + control/scheduler.h delayed_destruction_ring.h dirty_flags.cpp dirty_flags.h @@ -54,6 +60,8 @@ add_library(video_core STATIC engines/maxwell_3d.h engines/maxwell_dma.cpp engines/maxwell_dma.h + engines/puller.cpp + engines/puller.h framebuffer_config.h macro/macro.cpp macro/macro.h diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index f015dae566..6b6764d721 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include "common/settings.h" #include "core/memory.h" #include "video_core/buffer_cache/buffer_base.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" @@ -56,7 +56,7 @@ using UniformBufferSizes = std::array; template -class BufferCache { +class BufferCache : public VideoCommon::ChannelSetupCaches { // Page size for caching purposes. // This is unrelated to the CPU page size and it can be changed as it seems optimal. @@ -116,10 +116,7 @@ public: static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast(4_KiB); explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - Runtime& runtime_); + Core::Memory::Memory& cpu_memory_, Runtime& runtime_); void TickFrame(); @@ -367,9 +364,6 @@ private: void ClearDownload(IntervalType subtract_interval); VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; Core::Memory::Memory& cpu_memory; SlotVector slot_buffers; @@ -444,12 +438,8 @@ private: template BufferCache

::BufferCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - Runtime& runtime_) - : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_}, - kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} { + Core::Memory::Memory& cpu_memory_, Runtime& runtime_) + : runtime{runtime_}, rasterizer{rasterizer_}, cpu_memory{cpu_memory_} { // Ensure the first slot is used for the null buffer void(slot_buffers.insert(runtime, NullBufferParams{})); common_ranges.clear(); @@ -552,8 +542,8 @@ void BufferCache

::ClearDownload(IntervalType subtract_interval) { template bool BufferCache

::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { - const std::optional cpu_src_address = gpu_memory.GpuToCpuAddress(src_address); - const std::optional cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address); + const std::optional cpu_src_address = gpu_memory->GpuToCpuAddress(src_address); + const std::optional cpu_dest_address = gpu_memory->GpuToCpuAddress(dest_address); if (!cpu_src_address || !cpu_dest_address) { return false; } @@ -611,7 +601,7 @@ bool BufferCache

::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am template bool BufferCache

::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { - const std::optional cpu_dst_address = gpu_memory.GpuToCpuAddress(dst_address); + const std::optional cpu_dst_address = gpu_memory->GpuToCpuAddress(dst_address); if (!cpu_dst_address) { return false; } @@ -635,7 +625,7 @@ bool BufferCache

::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { template void BufferCache

::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) { - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); const Binding binding{ .cpu_addr = *cpu_addr, .size = size, @@ -673,7 +663,7 @@ void BufferCache

::BindHostGeometryBuffers(bool is_indexed) { if (is_indexed) { BindHostIndexBuffer(); } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { runtime.BindQuadArrayIndexBuffer(regs.vertex_buffer.first, regs.vertex_buffer.count); } @@ -733,7 +723,7 @@ void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, enabled_storage_buffers[stage] |= 1U << ssbo_index; written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index; - const auto& cbufs = maxwell3d.state.shader_stages[stage]; + const auto& cbufs = maxwell3d->state.shader_stages[stage]; const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr); } @@ -770,7 +760,7 @@ void BufferCache

::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, enabled_compute_storage_buffers |= 1U << ssbo_index; written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index; - const auto& launch_desc = kepler_compute.launch_description; + const auto& launch_desc = kepler_compute->launch_description; ASSERT(((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) != 0); const auto& cbufs = launch_desc.const_buffer_config; @@ -991,19 +981,19 @@ void BufferCache

::BindHostIndexBuffer() { const u32 size = index_buffer.size; SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const u32 new_offset = offset + maxwell3d.regs.index_array.first * - maxwell3d.regs.index_array.FormatSizeInBytes(); + const u32 new_offset = offset + maxwell3d->regs.index_array.first * + maxwell3d->regs.index_array.FormatSizeInBytes(); runtime.BindIndexBuffer(buffer, new_offset, size); } else { - runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, - maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, - buffer, offset, size); + runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_array.format, + maxwell3d->regs.index_array.first, + maxwell3d->regs.index_array.count, buffer, offset, size); } } template void BufferCache

::BindHostVertexBuffers() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { const Binding& binding = vertex_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; @@ -1014,7 +1004,7 @@ void BufferCache

::BindHostVertexBuffers() { } flags[Dirty::VertexBuffer0 + index] = false; - const u32 stride = maxwell3d.regs.vertex_array[index].stride; + const u32 stride = maxwell3d->regs.vertex_array[index].stride; const u32 offset = buffer.Offset(binding.cpu_addr); runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride); } @@ -1154,7 +1144,7 @@ void BufferCache

::BindHostGraphicsTextureBuffers(size_t stage) { template void BufferCache

::BindHostTransformFeedbackBuffers() { - if (maxwell3d.regs.tfb_enabled == 0) { + if (maxwell3d->regs.tfb_enabled == 0) { return; } for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { @@ -1262,8 +1252,8 @@ template void BufferCache

::UpdateIndexBuffer() { // We have to check for the dirty flags and index count // The index count is currently changed without updating the dirty flags - const auto& index_array = maxwell3d.regs.index_array; - auto& flags = maxwell3d.dirty.flags; + const auto& index_array = maxwell3d->regs.index_array; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { return; } @@ -1272,7 +1262,7 @@ void BufferCache

::UpdateIndexBuffer() { const GPUVAddr gpu_addr_begin = index_array.StartAddress(); const GPUVAddr gpu_addr_end = index_array.EndAddress(); - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes(); const u32 size = std::min(address_size, draw_size); @@ -1289,8 +1279,8 @@ void BufferCache

::UpdateIndexBuffer() { template void BufferCache

::UpdateVertexBuffers() { - auto& flags = maxwell3d.dirty.flags; - if (!maxwell3d.dirty.flags[Dirty::VertexBuffers]) { + auto& flags = maxwell3d->dirty.flags; + if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) { return; } flags[Dirty::VertexBuffers] = false; @@ -1302,28 +1292,15 @@ void BufferCache

::UpdateVertexBuffers() { template void BufferCache

::UpdateVertexBuffer(u32 index) { - if (!maxwell3d.dirty.flags[Dirty::VertexBuffer0 + index]) { + if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) { return; } - const auto& array = maxwell3d.regs.vertex_array[index]; - const auto& limit = maxwell3d.regs.vertex_array_limit[index]; + const auto& array = maxwell3d->regs.vertex_array[index]; + const auto& limit = maxwell3d->regs.vertex_array_limit[index]; const GPUVAddr gpu_addr_begin = array.StartAddress(); const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); - u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); - if (address_size >= 64_MiB) { - // Reported vertex buffer size is very large, cap to mapped buffer size - GPUVAddr submapped_addr_end = gpu_addr_begin; - - const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)}; - if (ranges.size() > 0) { - const auto& [addr, size] = *ranges.begin(); - submapped_addr_end = addr + size; - } - - address_size = - std::min(address_size, static_cast(submapped_addr_end - gpu_addr_begin)); - } + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); + const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); const u32 size = address_size; // TODO: Analyze stride and number of vertices if (array.enable == 0 || size == 0 || !cpu_addr) { vertex_buffers[index] = NULL_BINDING; @@ -1382,7 +1359,7 @@ void BufferCache

::UpdateTextureBuffers(size_t stage) { template void BufferCache

::UpdateTransformFeedbackBuffers() { - if (maxwell3d.regs.tfb_enabled == 0) { + if (maxwell3d->regs.tfb_enabled == 0) { return; } for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { @@ -1392,10 +1369,10 @@ void BufferCache

::UpdateTransformFeedbackBuffers() { template void BufferCache

::UpdateTransformFeedbackBuffer(u32 index) { - const auto& binding = maxwell3d.regs.tfb_bindings[index]; + const auto& binding = maxwell3d->regs.tfb_bindings[index]; const GPUVAddr gpu_addr = binding.Address() + binding.buffer_offset; const u32 size = binding.buffer_size; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (binding.buffer_enable == 0 || size == 0 || !cpu_addr) { transform_feedback_buffers[index] = NULL_BINDING; return; @@ -1414,10 +1391,10 @@ void BufferCache

::UpdateComputeUniformBuffers() { ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) { Binding& binding = compute_uniform_buffers[index]; binding = NULL_BINDING; - const auto& launch_desc = kepler_compute.launch_description; + const auto& launch_desc = kepler_compute->launch_description; if (((launch_desc.const_buffer_enable_mask >> index) & 1) != 0) { const auto& cbuf = launch_desc.const_buffer_config[index]; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(cbuf.Address()); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(cbuf.Address()); if (cpu_addr) { binding.cpu_addr = *cpu_addr; binding.size = cbuf.size; @@ -1831,7 +1808,7 @@ void BufferCache

::NotifyBufferDeletion() { dirty_uniform_buffers.fill(~u32{0}); uniform_buffer_binding_sizes.fill({}); } - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; flags[Dirty::IndexBuffer] = true; flags[Dirty::VertexBuffers] = true; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { @@ -1842,9 +1819,9 @@ void BufferCache

::NotifyBufferDeletion() { template typename BufferCache

::Binding BufferCache

::StorageBufferBinding(GPUVAddr ssbo_addr) const { - const GPUVAddr gpu_addr = gpu_memory.Read(ssbo_addr); - const u32 size = gpu_memory.Read(ssbo_addr + 8); - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const GPUVAddr gpu_addr = gpu_memory->Read(ssbo_addr); + const u32 size = gpu_memory->Read(ssbo_addr + 8); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr || size == 0) { return NULL_BINDING; } @@ -1859,7 +1836,7 @@ typename BufferCache

::Binding BufferCache

::StorageBufferBinding(GPUVAddr s template typename BufferCache

::TextureBufferBinding BufferCache

::GetTextureBufferBinding( GPUVAddr gpu_addr, u32 size, PixelFormat format) { - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); TextureBufferBinding binding; if (!cpu_addr || size == 0) { binding.cpu_addr = 0; diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp new file mode 100644 index 0000000000..67803fe94c --- /dev/null +++ b/src/video_core/control/channel_state.cpp @@ -0,0 +1,44 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "video_core/control/channel_state.h" +#include "video_core/dma_pusher.h" +#include "video_core/engines/fermi_2d.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/engines/kepler_memory.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/engines/puller.h" +#include "video_core/memory_manager.h" + +namespace Tegra::Control { + +ChannelState::ChannelState(s32 bind_id_) { + bind_id = bind_id_; + initiated = false; +} + +void ChannelState::Init(Core::System& system, GPU& gpu) { + ASSERT(memory_manager); + dma_pusher = std::make_unique(system, gpu, *memory_manager, *this); + maxwell_3d = std::make_unique(system, *memory_manager); + fermi_2d = std::make_unique(); + kepler_compute = std::make_unique(system, *memory_manager); + maxwell_dma = std::make_unique(system, *memory_manager); + kepler_memory = std::make_unique(system, *memory_manager); + initiated = true; +} + +void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { + dma_pusher->BindRasterizer(rasterizer); + memory_manager->BindRasterizer(rasterizer); + maxwell_3d->BindRasterizer(rasterizer); + fermi_2d->BindRasterizer(rasterizer); + kepler_memory->BindRasterizer(rasterizer); + kepler_compute->BindRasterizer(rasterizer); + maxwell_dma->BindRasterizer(rasterizer); +} + +} // namespace Tegra::Control diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h new file mode 100644 index 0000000000..82808a6b82 --- /dev/null +++ b/src/video_core/control/channel_state.h @@ -0,0 +1,69 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Core { +class System; +} + +namespace VideoCore { +class RasterizerInterface; +} + +namespace Tegra { + +class GPU; + +namespace Engines { +class Puller; +class Fermi2D; +class Maxwell3D; +class MaxwellDMA; +class KeplerCompute; +class KeplerMemory; +} // namespace Engines + +class MemoryManager; +class DmaPusher; + +namespace Control { + +struct ChannelState { + ChannelState(s32 bind_id); + ChannelState(const ChannelState& state) = delete; + ChannelState& operator=(const ChannelState&) = delete; + ChannelState(ChannelState&& other) noexcept = default; + ChannelState& operator=(ChannelState&& other) noexcept = default; + + void Init(Core::System& system, GPU& gpu); + + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + + s32 bind_id = -1; + /// 3D engine + std::unique_ptr maxwell_3d; + /// 2D engine + std::unique_ptr fermi_2d; + /// Compute engine + std::unique_ptr kepler_compute; + /// DMA engine + std::unique_ptr maxwell_dma; + /// Inline memory engine + std::unique_ptr kepler_memory; + + std::shared_ptr memory_manager; + + std::unique_ptr dma_pusher; + + bool initiated{}; +}; + +} // namespace Control + +} // namespace Tegra diff --git a/src/video_core/control/channel_state_cache.cpp b/src/video_core/control/channel_state_cache.cpp new file mode 100644 index 0000000000..f72a97b2f5 --- /dev/null +++ b/src/video_core/control/channel_state_cache.cpp @@ -0,0 +1,5 @@ +#include "video_core/control/channel_state_cache.inc" + +namespace VideoCommon { +template class VideoCommon::ChannelSetupCaches; +} diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h new file mode 100644 index 0000000000..c8298c0030 --- /dev/null +++ b/src/video_core/control/channel_state_cache.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +#include "common/common_types.h" + +namespace Tegra { + +namespace Engines { +class Maxwell3D; +class KeplerCompute; +} // namespace Engines + +class MemoryManager; + +namespace Control { +struct ChannelState; +} + +} // namespace Tegra + +namespace VideoCommon { + +class ChannelInfo { +public: + ChannelInfo() = delete; + ChannelInfo(Tegra::Control::ChannelState& state); + ChannelInfo(const ChannelInfo& state) = delete; + ChannelInfo& operator=(const ChannelInfo&) = delete; + ChannelInfo(ChannelInfo&& other) = default; + ChannelInfo& operator=(ChannelInfo&& other) = default; + + Tegra::Engines::Maxwell3D& maxwell3d; + Tegra::Engines::KeplerCompute& kepler_compute; + Tegra::MemoryManager& gpu_memory; +}; + +template +class ChannelSetupCaches { +public: + /// Operations for seting the channel of execution. + + /// Create channel state. + void CreateChannel(Tegra::Control::ChannelState& channel); + + /// Bind a channel for execution. + void BindToChannel(s32 id); + + /// Erase channel's state. + void EraseChannel(s32 id); + +protected: + static constexpr size_t UNSET_CHANNEL{std::numeric_limits::max()}; + + std::deque

channel_storage; + std::deque free_channel_ids; + std::unordered_map channel_map; + + P* channel_state; + size_t current_channel_id{UNSET_CHANNEL}; + Tegra::Engines::Maxwell3D* maxwell3d; + Tegra::Engines::KeplerCompute* kepler_compute; + Tegra::MemoryManager* gpu_memory; +}; + +} // namespace VideoCommon diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc new file mode 100644 index 0000000000..3eb73af9f4 --- /dev/null +++ b/src/video_core/control/channel_state_cache.inc @@ -0,0 +1,64 @@ +#include "video_core/control/channel_state.h" +#include "video_core/control/channel_state_cache.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/memory_manager.h" + +namespace VideoCommon { + +ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state) + : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, + gpu_memory{*channel_state.memory_manager} {} + +template +void ChannelSetupCaches

::CreateChannel(struct Tegra::Control::ChannelState& channel) { + ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0); + auto new_id = [this, &channel]() { + if (!free_channel_ids.empty()) { + auto id = free_channel_ids.front(); + free_channel_ids.pop_front(); + new (&channel_storage[id]) ChannelInfo(channel); + return id; + } + channel_storage.emplace_back(channel); + return channel_storage.size() - 1; + }(); + channel_map.emplace(channel.bind_id, new_id); + if (current_channel_id != UNSET_CHANNEL) { + channel_state = &channel_storage[current_channel_id]; + } +} + +/// Bind a channel for execution. +template +void ChannelSetupCaches

::BindToChannel(s32 id) { + auto it = channel_map.find(id); + ASSERT(it != channel_map.end() && id >= 0); + current_channel_id = it->second; + channel_state = &channel_storage[current_channel_id]; + maxwell3d = &channel_state->maxwell3d; + kepler_compute = &channel_state->kepler_compute; + gpu_memory = &channel_state->gpu_memory; +} + +/// Erase channel's channel_state. +template +void ChannelSetupCaches

::EraseChannel(s32 id) { + const auto it = channel_map.find(id); + ASSERT(it != channel_map.end() && id >= 0); + const auto this_id = it->second; + free_channel_ids.push_back(this_id); + channel_map.erase(it); + if (this_id == current_channel_id) { + current_channel_id = UNSET_CHANNEL; + channel_state = nullptr; + maxwell3d = nullptr; + kepler_compute = nullptr; + gpu_memory = nullptr; + } else if (current_channel_id != UNSET_CHANNEL) { + channel_state = &channel_storage[current_channel_id]; + } +} + + +} // namespace VideoCommon diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp new file mode 100644 index 0000000000..e1abcb188b --- /dev/null +++ b/src/video_core/control/scheduler.cpp @@ -0,0 +1,31 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "video_core/control/channel_state.h" +#include "video_core/control/scheduler.h" +#include "video_core/gpu.h" + +namespace Tegra::Control { +Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {} + +Scheduler::~Scheduler() = default; + +void Scheduler::Push(s32 channel, CommandList&& entries) { + std::unique_lock lk(scheduling_guard); + auto it = channels.find(channel); + auto channel_state = it->second; + gpu.BindChannel(channel_state->bind_id); + channel_state->dma_pusher->Push(std::move(entries)); + channel_state->dma_pusher->DispatchCalls(); +} + +void Scheduler::DeclareChannel(std::shared_ptr new_channel) { + s32 channel = new_channel->bind_id; + std::unique_lock lk(scheduling_guard); + channels.emplace(channel, new_channel); +} + +} // namespace Tegra::Control diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h new file mode 100644 index 0000000000..802e9caffb --- /dev/null +++ b/src/video_core/control/scheduler.h @@ -0,0 +1,38 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "video_core/dma_pusher.h" + +namespace Tegra { + +class GPU; + +namespace Control { + +struct ChannelState; + +class Scheduler { +public: + Scheduler(GPU& gpu_); + ~Scheduler(); + + void Push(s32 channel, CommandList&& entries); + + void DeclareChannel(std::shared_ptr new_channel); + +private: + std::unordered_map> channels; + std::mutex scheduling_guard; + GPU& gpu; +}; + +} // namespace Control + +} // namespace Tegra diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 29b8582abd..b01f04d0c9 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -12,7 +12,10 @@ namespace Tegra { -DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_) : gpu{gpu_}, system{system_} {} +DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, + Control::ChannelState& channel_state_) + : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, + *this, channel_state_} {} DmaPusher::~DmaPusher() = default; @@ -76,11 +79,11 @@ bool DmaPusher::Step() { // Push buffer non-empty, read a word command_headers.resize(command_list_header.size); if (Settings::IsGPULevelHigh()) { - gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + memory_manager.ReadBlock(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); } else { - gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); } } for (std::size_t index = 0; index < command_headers.size();) { @@ -154,7 +157,7 @@ void DmaPusher::SetState(const CommandHeader& command_header) { void DmaPusher::CallMethod(u32 argument) const { if (dma_state.method < non_puller_methods) { - gpu.CallMethod(GPU::MethodCall{ + puller.CallPullerMethod(Engines::Puller::MethodCall{ dma_state.method, argument, dma_state.subchannel, @@ -168,12 +171,16 @@ void DmaPusher::CallMethod(u32 argument) const { void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { if (dma_state.method < non_puller_methods) { - gpu.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, - dma_state.method_count); + puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, + dma_state.method_count); } else { subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, num_methods, dma_state.method_count); } } +void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { + puller.BindRasterizer(rasterizer); +} + } // namespace Tegra diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 872fd146ac..fd7c936c40 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -10,6 +10,7 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "video_core/engines/engine_interface.h" +#include "video_core/engines/puller.h" namespace Core { class System; @@ -17,7 +18,12 @@ class System; namespace Tegra { +namespace Control { +struct ChannelState; +} + class GPU; +class MemoryManager; enum class SubmissionMode : u32 { IncreasingOld = 0, @@ -102,7 +108,8 @@ struct CommandList final { */ class DmaPusher final { public: - explicit DmaPusher(Core::System& system_, GPU& gpu_); + explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, + Control::ChannelState& channel_state_); ~DmaPusher(); void Push(CommandList&& entries) { @@ -115,6 +122,8 @@ public: subchannels[subchannel_id] = engine; } + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + private: static constexpr u32 non_puller_methods = 0x40; static constexpr u32 max_subchannels = 8; @@ -148,6 +157,8 @@ private: GPU& gpu; Core::System& system; + MemoryManager& memory_manager; + mutable Engines::Puller puller; }; } // namespace Tegra diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp new file mode 100644 index 0000000000..37f2ced18f --- /dev/null +++ b/src/video_core/engines/puller.cpp @@ -0,0 +1,297 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "video_core/control/channel_state.h" +#include "video_core/dma_pusher.h" +#include "video_core/engines/fermi_2d.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/engines/kepler_memory.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/engines/puller.h" +#include "video_core/gpu.h" +#include "video_core/memory_manager.h" +#include "video_core/rasterizer_interface.h" + +namespace Tegra::Engines { + +Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_, + Control::ChannelState& channel_state_) + : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{ + channel_state_} {} + +Puller::~Puller() = default; + +void Puller::ProcessBindMethod(const MethodCall& method_call) { + // Bind the current subchannel to the desired engine id. + LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, + method_call.argument); + const auto engine_id = static_cast(method_call.argument); + bound_engines[method_call.subchannel] = static_cast(engine_id); + switch (engine_id) { + case EngineID::FERMI_TWOD_A: + dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); + break; + case EngineID::MAXWELL_B: + dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel); + break; + case EngineID::KEPLER_COMPUTE_B: + dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel); + break; + case EngineID::MAXWELL_DMA_COPY_A: + dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); + } +} + +void Puller::ProcessFenceActionMethod() { + switch (regs.fence_action.op) { + case Puller::FenceOperation::Acquire: + // UNIMPLEMENTED_MSG("Channel Scheduling pending."); + // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); + break; + case Puller::FenceOperation::Increment: + rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); + } +} + +void Puller::ProcessWaitForInterruptMethod() { + // TODO(bunnei) ImplementMe + LOG_WARNING(HW_GPU, "(STUBBED) called"); +} + +void Puller::ProcessSemaphoreTriggerMethod() { + const auto semaphoreOperationMask = 0xF; + const auto op = + static_cast(regs.semaphore_trigger & semaphoreOperationMask); + if (op == GpuSemaphoreOperation::WriteLong) { + struct Block { + u32 sequence; + u32 zeros = 0; + u64 timestamp; + }; + + Block block{}; + block.sequence = regs.semaphore_sequence; + // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of + // CoreTiming + block.timestamp = gpu.GetTicks(); + memory_manager.WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, sizeof(block)); + } else { + const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; + if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || + (op == GpuSemaphoreOperation::AcquireGequal && + static_cast(word - regs.semaphore_sequence) > 0) || + (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) { + // Nothing to do in this case + } else { + regs.acquire_source = true; + regs.acquire_value = regs.semaphore_sequence; + if (op == GpuSemaphoreOperation::AcquireEqual) { + regs.acquire_active = true; + regs.acquire_mode = false; + } else if (op == GpuSemaphoreOperation::AcquireGequal) { + regs.acquire_active = true; + regs.acquire_mode = true; + } else if (op == GpuSemaphoreOperation::AcquireMask) { + // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with + // semaphore_sequence, gives a non-0 result + LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented"); + } else { + LOG_ERROR(HW_GPU, "Invalid semaphore operation"); + } + } + } +} + +void Puller::ProcessSemaphoreRelease() { + memory_manager.Write(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release); +} + +void Puller::ProcessSemaphoreAcquire() { + const u32 word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); + const auto value = regs.semaphore_acquire; + if (word != value) { + regs.acquire_active = true; + regs.acquire_value = value; + // TODO(kemathe73) figure out how to do the acquire_timeout + regs.acquire_mode = false; + regs.acquire_source = false; + } +} + +/// Calls a GPU puller method. +void Puller::CallPullerMethod(const MethodCall& method_call) { + regs.reg_array[method_call.method] = method_call.argument; + const auto method = static_cast(method_call.method); + + switch (method) { + case BufferMethods::BindObject: { + ProcessBindMethod(method_call); + break; + } + case BufferMethods::Nop: + case BufferMethods::SemaphoreAddressHigh: + case BufferMethods::SemaphoreAddressLow: + case BufferMethods::SemaphoreSequence: + case BufferMethods::UnkCacheFlush: + case BufferMethods::WrcacheFlush: + case BufferMethods::FenceValue: + break; + case BufferMethods::RefCnt: + rasterizer->SignalReference(); + break; + case BufferMethods::FenceAction: + ProcessFenceActionMethod(); + break; + case BufferMethods::WaitForInterrupt: + ProcessWaitForInterruptMethod(); + break; + case BufferMethods::SemaphoreTrigger: { + ProcessSemaphoreTriggerMethod(); + break; + } + case BufferMethods::NotifyIntr: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented"); + break; + } + case BufferMethods::Unk28: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented"); + break; + } + case BufferMethods::SemaphoreAcquire: { + ProcessSemaphoreAcquire(); + break; + } + case BufferMethods::SemaphoreRelease: { + ProcessSemaphoreRelease(); + break; + } + case BufferMethods::Yield: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented"); + break; + } + default: + LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method); + break; + } +} + +/// Calls a GPU engine method. +void Puller::CallEngineMethod(const MethodCall& method_call) { + const EngineID engine = bound_engines[method_call.subchannel]; + + switch (engine) { + case EngineID::FERMI_TWOD_A: + channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::MAXWELL_B: + channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::KEPLER_COMPUTE_B: + channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::MAXWELL_DMA_COPY_A: + channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine"); + } +} + +/// Calls a GPU engine multivalue method. +void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + const EngineID engine = bound_engines[subchannel]; + + switch (engine) { + case EngineID::FERMI_TWOD_A: + channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::MAXWELL_B: + channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::KEPLER_COMPUTE_B: + channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::MAXWELL_DMA_COPY_A: + channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine"); + } +} + +/// Calls a GPU method. +void Puller::CallMethod(const MethodCall& method_call) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, + method_call.subchannel); + + ASSERT(method_call.subchannel < bound_engines.size()); + + if (ExecuteMethodOnEngine(method_call.method)) { + CallEngineMethod(method_call); + } else { + CallPullerMethod(method_call); + } +} + +/// Calls a GPU multivalue method. +void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); + + ASSERT(subchannel < bound_engines.size()); + + if (ExecuteMethodOnEngine(method)) { + CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); + } else { + for (std::size_t i = 0; i < amount; i++) { + CallPullerMethod(MethodCall{ + method, + base_start[i], + subchannel, + methods_pending - static_cast(i), + }); + } + } +} + +void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + rasterizer = rasterizer_; +} + +/// Determines where the method should be executed. +[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) { + const auto buffer_method = static_cast(method); + return buffer_method >= BufferMethods::NonPullerMethods; +} + +} // namespace Tegra::Engines diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h new file mode 100644 index 0000000000..d948ec790d --- /dev/null +++ b/src/video_core/engines/puller.h @@ -0,0 +1,179 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "video_core/engines/engine_interface.h" + +namespace Core { +class System; +} + +namespace Tegra { +class MemoryManager; +class DmaPusher; + +enum class EngineID { + FERMI_TWOD_A = 0x902D, // 2D Engine + MAXWELL_B = 0xB197, // 3D Engine + KEPLER_COMPUTE_B = 0xB1C0, + KEPLER_INLINE_TO_MEMORY_B = 0xA140, + MAXWELL_DMA_COPY_A = 0xB0B5, +}; + +namespace Control { +struct ChannelState; +} +} // namespace Tegra + +namespace VideoCore { +class RasterizerInterface; +} + +namespace Tegra::Engines { + +class Puller final { +public: + struct MethodCall { + u32 method{}; + u32 argument{}; + u32 subchannel{}; + u32 method_count{}; + + explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0) + : method(method_), argument(argument_), subchannel(subchannel_), + method_count(method_count_) {} + + [[nodiscard]] bool IsLastCall() const { + return method_count <= 1; + } + }; + + enum class FenceOperation : u32 { + Acquire = 0, + Increment = 1, + }; + + union FenceAction { + u32 raw; + BitField<0, 1, FenceOperation> op; + BitField<8, 24, u32> syncpoint_id; + }; + + explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher, + Control::ChannelState& channel_state); + ~Puller(); + + void CallMethod(const MethodCall& method_call); + + void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending); + + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + + void CallPullerMethod(const MethodCall& method_call); + + void CallEngineMethod(const MethodCall& method_call); + + void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending); + +private: + Tegra::GPU& gpu; + + MemoryManager& memory_manager; + DmaPusher& dma_pusher; + Control::ChannelState& channel_state; + VideoCore::RasterizerInterface* rasterizer = nullptr; + + static constexpr std::size_t NUM_REGS = 0x800; + struct Regs { + static constexpr size_t NUM_REGS = 0x40; + + union { + struct { + INSERT_PADDING_WORDS_NOINIT(0x4); + struct { + u32 address_high; + u32 address_low; + + [[nodiscard]] GPUVAddr SemaphoreAddress() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + } semaphore_address; + + u32 semaphore_sequence; + u32 semaphore_trigger; + INSERT_PADDING_WORDS_NOINIT(0xC); + + // The pusher and the puller share the reference counter, the pusher only has read + // access + u32 reference_count; + INSERT_PADDING_WORDS_NOINIT(0x5); + + u32 semaphore_acquire; + u32 semaphore_release; + u32 fence_value; + FenceAction fence_action; + INSERT_PADDING_WORDS_NOINIT(0xE2); + + // Puller state + u32 acquire_mode; + u32 acquire_source; + u32 acquire_active; + u32 acquire_timeout; + u32 acquire_value; + }; + std::array reg_array; + }; + } regs{}; + + void ProcessBindMethod(const MethodCall& method_call); + void ProcessFenceActionMethod(); + void ProcessSemaphoreAcquire(); + void ProcessSemaphoreRelease(); + void ProcessSemaphoreTriggerMethod(); + void ProcessWaitForInterruptMethod(); + [[nodiscard]] bool ExecuteMethodOnEngine(u32 method); + + /// Mapping of command subchannels to their bound engine ids + std::array bound_engines{}; + + enum class GpuSemaphoreOperation { + AcquireEqual = 0x1, + WriteLong = 0x2, + AcquireGequal = 0x4, + AcquireMask = 0x8, + }; + +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(Regs, field_name) == position * 4, \ + "Field " #field_name " has invalid position") + + ASSERT_REG_POSITION(semaphore_address, 0x4); + ASSERT_REG_POSITION(semaphore_sequence, 0x6); + ASSERT_REG_POSITION(semaphore_trigger, 0x7); + ASSERT_REG_POSITION(reference_count, 0x14); + ASSERT_REG_POSITION(semaphore_acquire, 0x1A); + ASSERT_REG_POSITION(semaphore_release, 0x1B); + ASSERT_REG_POSITION(fence_value, 0x1C); + ASSERT_REG_POSITION(fence_action, 0x1D); + + ASSERT_REG_POSITION(acquire_mode, 0x100); + ASSERT_REG_POSITION(acquire_source, 0x101); + ASSERT_REG_POSITION(acquire_active, 0x102); + ASSERT_REG_POSITION(acquire_timeout, 0x103); + ASSERT_REG_POSITION(acquire_value, 0x104); + +#undef ASSERT_REG_POSITION +}; + +} // namespace Tegra::Engines diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 1e9832ddd4..d658e038d9 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -4,12 +4,13 @@ #pragma once #include +#include +#include #include #include "common/common_types.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace VideoCommon { @@ -19,10 +20,10 @@ public: explicit FenceBase(u32 payload_, bool is_stubbed_) : address{}, payload{payload_}, is_semaphore{false}, is_stubbed{is_stubbed_} {} - explicit FenceBase(GPUVAddr address_, u32 payload_, bool is_stubbed_) + explicit FenceBase(u8* address_, u32 payload_, bool is_stubbed_) : address{address_}, payload{payload_}, is_semaphore{true}, is_stubbed{is_stubbed_} {} - GPUVAddr GetAddress() const { + u8* GetAddress() const { return address; } @@ -35,7 +36,7 @@ public: } private: - GPUVAddr address; + u8* address; u32 payload; bool is_semaphore; @@ -57,7 +58,7 @@ public: buffer_cache.AccumulateFlushes(); } - void SignalSemaphore(GPUVAddr addr, u32 value) { + void SignalSemaphore(u8* addr, u32 value) { TryReleasePendingFences(); const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); @@ -91,8 +92,9 @@ public: } PopAsyncFlushes(); if (current_fence->IsSemaphore()) { - gpu_memory.template Write(current_fence->GetAddress(), - current_fence->GetPayload()); + char* address = reinterpret_cast(current_fence->GetAddress()); + auto payload = current_fence->GetPayload(); + std::memcpy(address, &payload, sizeof(payload)); } else { gpu.IncrementSyncPoint(current_fence->GetPayload()); } @@ -104,8 +106,8 @@ protected: explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, TQueryCache& query_cache_) - : rasterizer{rasterizer_}, gpu{gpu_}, gpu_memory{gpu.MemoryManager()}, - texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} + : rasterizer{rasterizer_}, gpu{gpu_}, texture_cache{texture_cache_}, + buffer_cache{buffer_cache_}, query_cache{query_cache_} {} virtual ~FenceManager() = default; @@ -113,7 +115,7 @@ protected: /// true virtual TFence CreateFence(u32 value, bool is_stubbed) = 0; /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true - virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0; + virtual TFence CreateFence(u8* addr, u32 value, bool is_stubbed) = 0; /// Queues a fence into the backend if the fence isn't stubbed. virtual void QueueFence(TFence& fence) = 0; /// Notifies that the backend fence has been signaled/reached in host GPU. @@ -123,7 +125,6 @@ protected: VideoCore::RasterizerInterface& rasterizer; Tegra::GPU& gpu; - Tegra::MemoryManager& gpu_memory; TTextureCache& texture_cache; TTBufferCache& buffer_cache; TQueryCache& query_cache; @@ -137,8 +138,9 @@ private: } PopAsyncFlushes(); if (current_fence->IsSemaphore()) { - gpu_memory.template Write(current_fence->GetAddress(), - current_fence->GetPayload()); + char* address = reinterpret_cast(current_fence->GetAddress()); + const auto payload = current_fence->GetPayload(); + std::memcpy(address, &payload, sizeof(payload)); } else { gpu.IncrementSyncPoint(current_fence->GetPayload()); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 33431f2a0f..80a1c69e00 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -18,6 +18,8 @@ #include "core/hle/service/nvdrv/nvdata.h" #include "core/perf_stats.h" #include "video_core/cdma_pusher.h" +#include "video_core/control/channel_state.h" +#include "video_core/control/scheduler.h" #include "video_core/dma_pusher.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/kepler_compute.h" @@ -36,65 +38,58 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); struct GPU::Impl { explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) - : gpu{gpu_}, system{system_}, memory_manager{std::make_unique( - system)}, - dma_pusher{std::make_unique(system, gpu)}, use_nvdec{use_nvdec_}, - maxwell_3d{std::make_unique(system, *memory_manager)}, - fermi_2d{std::make_unique()}, - kepler_compute{std::make_unique(system, *memory_manager)}, - maxwell_dma{std::make_unique(system, *memory_manager)}, - kepler_memory{std::make_unique(system, *memory_manager)}, + : gpu{gpu_}, system{system_}, use_nvdec{use_nvdec_}, shader_notify{std::make_unique()}, is_async{is_async_}, - gpu_thread{system_, is_async_} {} + gpu_thread{system_, is_async_}, scheduler{std::make_unique(gpu)} {} ~Impl() = default; + std::shared_ptr CreateChannel(s32 channel_id) { + auto channel_state = std::make_shared(channel_id); + channels.emplace(channel_id, channel_state); + scheduler->DeclareChannel(channel_state); + return channel_state; + } + + void BindChannel(s32 channel_id) { + if (bound_channel == channel_id) { + return; + } + auto it = channels.find(channel_id); + ASSERT(it != channels.end()); + bound_channel = channel_id; + current_channel = it->second.get(); + + rasterizer->BindChannel(*current_channel); + } + + std::shared_ptr AllocateChannel() { + return CreateChannel(new_channel_id++); + } + + void InitChannel(Control::ChannelState& to_init) { + to_init.Init(system, gpu); + to_init.BindRasterizer(rasterizer); + rasterizer->InitializeChannel(to_init); + } + + void ReleaseChannel(Control::ChannelState& to_release) { + UNIMPLEMENTED(); + } + + void CreateHost1xChannel() { + if (host1x_channel) { + return; + } + host1x_channel = CreateChannel(0); + host1x_channel->memory_manager = std::make_shared(system); + InitChannel(*host1x_channel); + } + /// Binds a renderer to the GPU. void BindRenderer(std::unique_ptr renderer_) { renderer = std::move(renderer_); rasterizer = renderer->ReadRasterizer(); - - memory_manager->BindRasterizer(rasterizer); - maxwell_3d->BindRasterizer(rasterizer); - fermi_2d->BindRasterizer(rasterizer); - kepler_compute->BindRasterizer(rasterizer); - kepler_memory->BindRasterizer(rasterizer); - maxwell_dma->BindRasterizer(rasterizer); - } - - /// Calls a GPU method. - void CallMethod(const GPU::MethodCall& method_call) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, - method_call.subchannel); - - ASSERT(method_call.subchannel < bound_engines.size()); - - if (ExecuteMethodOnEngine(method_call.method)) { - CallEngineMethod(method_call); - } else { - CallPullerMethod(method_call); - } - } - - /// Calls a GPU multivalue method. - void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); - - ASSERT(subchannel < bound_engines.size()); - - if (ExecuteMethodOnEngine(method)) { - CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); - } else { - for (std::size_t i = 0; i < amount; i++) { - CallPullerMethod(GPU::MethodCall{ - method, - base_start[i], - subchannel, - methods_pending - static_cast(i), - }); - } - } } /// Flush all current written commands into the host GPU for execution. @@ -146,42 +141,44 @@ struct GPU::Impl { /// Returns a reference to the Maxwell3D GPU engine. [[nodiscard]] Engines::Maxwell3D& Maxwell3D() { - return *maxwell_3d; + ASSERT(current_channel); + return *current_channel->maxwell_3d; } /// Returns a const reference to the Maxwell3D GPU engine. [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const { - return *maxwell_3d; + ASSERT(current_channel); + return *current_channel->maxwell_3d; } /// Returns a reference to the KeplerCompute GPU engine. [[nodiscard]] Engines::KeplerCompute& KeplerCompute() { - return *kepler_compute; + ASSERT(current_channel); + return *current_channel->kepler_compute; } /// Returns a reference to the KeplerCompute GPU engine. [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const { - return *kepler_compute; + ASSERT(current_channel); + return *current_channel->kepler_compute; } /// Returns a reference to the GPU memory manager. [[nodiscard]] Tegra::MemoryManager& MemoryManager() { - return *memory_manager; - } - - /// Returns a const reference to the GPU memory manager. - [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const { - return *memory_manager; + CreateHost1xChannel(); + return *host1x_channel->memory_manager; } /// Returns a reference to the GPU DMA pusher. [[nodiscard]] Tegra::DmaPusher& DmaPusher() { - return *dma_pusher; + ASSERT(current_channel); + return *current_channel->dma_pusher; } /// Returns a const reference to the GPU DMA pusher. [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const { - return *dma_pusher; + ASSERT(current_channel); + return *current_channel->dma_pusher; } /// Returns a reference to the underlying renderer. @@ -306,7 +303,7 @@ struct GPU::Impl { /// This can be used to launch any necessary threads and register any necessary /// core timing events. void Start() { - gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher); + gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); cpu_context = renderer->GetRenderWindow().CreateSharedContext(); cpu_context->MakeCurrent(); } @@ -328,8 +325,8 @@ struct GPU::Impl { } /// Push GPU command entries to be processed - void PushGPUEntries(Tegra::CommandList&& entries) { - gpu_thread.SubmitList(std::move(entries)); + void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) { + gpu_thread.SubmitList(channel, std::move(entries)); } /// Push GPU command buffer entries to be processed @@ -381,303 +378,16 @@ struct GPU::Impl { interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); } - void ProcessBindMethod(const GPU::MethodCall& method_call) { - // Bind the current subchannel to the desired engine id. - LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, - method_call.argument); - const auto engine_id = static_cast(method_call.argument); - bound_engines[method_call.subchannel] = static_cast(engine_id); - switch (engine_id) { - case EngineID::FERMI_TWOD_A: - dma_pusher->BindSubchannel(fermi_2d.get(), method_call.subchannel); - break; - case EngineID::MAXWELL_B: - dma_pusher->BindSubchannel(maxwell_3d.get(), method_call.subchannel); - break; - case EngineID::KEPLER_COMPUTE_B: - dma_pusher->BindSubchannel(kepler_compute.get(), method_call.subchannel); - break; - case EngineID::MAXWELL_DMA_COPY_A: - dma_pusher->BindSubchannel(maxwell_dma.get(), method_call.subchannel); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - dma_pusher->BindSubchannel(kepler_memory.get(), method_call.subchannel); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); - } - } - - void ProcessFenceActionMethod() { - switch (regs.fence_action.op) { - case GPU::FenceOperation::Acquire: - WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); - break; - case GPU::FenceOperation::Increment: - IncrementSyncPoint(regs.fence_action.syncpoint_id); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); - } - } - - void ProcessWaitForInterruptMethod() { - // TODO(bunnei) ImplementMe - LOG_WARNING(HW_GPU, "(STUBBED) called"); - } - - void ProcessSemaphoreTriggerMethod() { - const auto semaphoreOperationMask = 0xF; - const auto op = - static_cast(regs.semaphore_trigger & semaphoreOperationMask); - if (op == GpuSemaphoreOperation::WriteLong) { - struct Block { - u32 sequence; - u32 zeros = 0; - u64 timestamp; - }; - - Block block{}; - block.sequence = regs.semaphore_sequence; - // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of - // CoreTiming - block.timestamp = GetTicks(); - memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, - sizeof(block)); - } else { - const u32 word{memory_manager->Read(regs.semaphore_address.SemaphoreAddress())}; - if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || - (op == GpuSemaphoreOperation::AcquireGequal && - static_cast(word - regs.semaphore_sequence) > 0) || - (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) { - // Nothing to do in this case - } else { - regs.acquire_source = true; - regs.acquire_value = regs.semaphore_sequence; - if (op == GpuSemaphoreOperation::AcquireEqual) { - regs.acquire_active = true; - regs.acquire_mode = false; - } else if (op == GpuSemaphoreOperation::AcquireGequal) { - regs.acquire_active = true; - regs.acquire_mode = true; - } else if (op == GpuSemaphoreOperation::AcquireMask) { - // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with - // semaphore_sequence, gives a non-0 result - LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented"); - } else { - LOG_ERROR(HW_GPU, "Invalid semaphore operation"); - } - } - } - } - - void ProcessSemaphoreRelease() { - memory_manager->Write(regs.semaphore_address.SemaphoreAddress(), - regs.semaphore_release); - } - - void ProcessSemaphoreAcquire() { - const u32 word = memory_manager->Read(regs.semaphore_address.SemaphoreAddress()); - const auto value = regs.semaphore_acquire; - if (word != value) { - regs.acquire_active = true; - regs.acquire_value = value; - // TODO(kemathe73) figure out how to do the acquire_timeout - regs.acquire_mode = false; - regs.acquire_source = false; - } - } - - /// Calls a GPU puller method. - void CallPullerMethod(const GPU::MethodCall& method_call) { - regs.reg_array[method_call.method] = method_call.argument; - const auto method = static_cast(method_call.method); - - switch (method) { - case BufferMethods::BindObject: { - ProcessBindMethod(method_call); - break; - } - case BufferMethods::Nop: - case BufferMethods::SemaphoreAddressHigh: - case BufferMethods::SemaphoreAddressLow: - case BufferMethods::SemaphoreSequence: - break; - case BufferMethods::UnkCacheFlush: - rasterizer->SyncGuestHost(); - break; - case BufferMethods::WrcacheFlush: - rasterizer->SignalReference(); - break; - case BufferMethods::FenceValue: - break; - case BufferMethods::RefCnt: - rasterizer->SignalReference(); - break; - case BufferMethods::FenceAction: - ProcessFenceActionMethod(); - break; - case BufferMethods::WaitForInterrupt: - rasterizer->WaitForIdle(); - break; - case BufferMethods::SemaphoreTrigger: { - ProcessSemaphoreTriggerMethod(); - break; - } - case BufferMethods::NotifyIntr: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented"); - break; - } - case BufferMethods::Unk28: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented"); - break; - } - case BufferMethods::SemaphoreAcquire: { - ProcessSemaphoreAcquire(); - break; - } - case BufferMethods::SemaphoreRelease: { - ProcessSemaphoreRelease(); - break; - } - case BufferMethods::Yield: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented"); - break; - } - default: - LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method); - break; - } - } - - /// Calls a GPU engine method. - void CallEngineMethod(const GPU::MethodCall& method_call) { - const EngineID engine = bound_engines[method_call.subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::KEPLER_COMPUTE_B: - kepler_compute->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } - } - - /// Calls a GPU engine multivalue method. - void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - const EngineID engine = bound_engines[subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::KEPLER_COMPUTE_B: - kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } - } - - /// Determines where the method should be executed. - [[nodiscard]] bool ExecuteMethodOnEngine(u32 method) { - const auto buffer_method = static_cast(method); - return buffer_method >= BufferMethods::NonPullerMethods; - } - - struct Regs { - static constexpr size_t NUM_REGS = 0x40; - - union { - struct { - INSERT_PADDING_WORDS_NOINIT(0x4); - struct { - u32 address_high; - u32 address_low; - - [[nodiscard]] GPUVAddr SemaphoreAddress() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } semaphore_address; - - u32 semaphore_sequence; - u32 semaphore_trigger; - INSERT_PADDING_WORDS_NOINIT(0xC); - - // The pusher and the puller share the reference counter, the pusher only has read - // access - u32 reference_count; - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 semaphore_acquire; - u32 semaphore_release; - u32 fence_value; - GPU::FenceAction fence_action; - INSERT_PADDING_WORDS_NOINIT(0xE2); - - // Puller state - u32 acquire_mode; - u32 acquire_source; - u32 acquire_active; - u32 acquire_timeout; - u32 acquire_value; - }; - std::array reg_array; - }; - } regs{}; - GPU& gpu; Core::System& system; - std::unique_ptr memory_manager; - std::unique_ptr dma_pusher; + std::map> cdma_pushers; std::unique_ptr renderer; VideoCore::RasterizerInterface* rasterizer = nullptr; const bool use_nvdec; - /// Mapping of command subchannels to their bound engine ids - std::array bound_engines{}; - /// 3D engine - std::unique_ptr maxwell_3d; - /// 2D engine - std::unique_ptr fermi_2d; - /// Compute engine - std::unique_ptr kepler_compute; - /// DMA engine - std::unique_ptr maxwell_dma; - /// Inline memory engine - std::unique_ptr kepler_memory; + std::shared_ptr host1x_channel; + s32 new_channel_id{1}; /// Shader build notifier std::unique_ptr shader_notify; /// When true, we are about to shut down emulation session, so terminate outstanding tasks @@ -710,33 +420,10 @@ struct GPU::Impl { VideoCommon::GPUThread::ThreadManager gpu_thread; std::unique_ptr cpu_context; -#define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Regs, field_name) == position * 4, \ - "Field " #field_name " has invalid position") - - ASSERT_REG_POSITION(semaphore_address, 0x4); - ASSERT_REG_POSITION(semaphore_sequence, 0x6); - ASSERT_REG_POSITION(semaphore_trigger, 0x7); - ASSERT_REG_POSITION(reference_count, 0x14); - ASSERT_REG_POSITION(semaphore_acquire, 0x1A); - ASSERT_REG_POSITION(semaphore_release, 0x1B); - ASSERT_REG_POSITION(fence_value, 0x1C); - ASSERT_REG_POSITION(fence_action, 0x1D); - - ASSERT_REG_POSITION(acquire_mode, 0x100); - ASSERT_REG_POSITION(acquire_source, 0x101); - ASSERT_REG_POSITION(acquire_active, 0x102); - ASSERT_REG_POSITION(acquire_timeout, 0x103); - ASSERT_REG_POSITION(acquire_value, 0x104); - -#undef ASSERT_REG_POSITION - - enum class GpuSemaphoreOperation { - AcquireEqual = 0x1, - WriteLong = 0x2, - AcquireGequal = 0x4, - AcquireMask = 0x8, - }; + std::unique_ptr scheduler; + std::unordered_map> channels; + Tegra::Control::ChannelState* current_channel; + s32 bound_channel{-1}; }; GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) @@ -744,19 +431,26 @@ GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) GPU::~GPU() = default; +std::shared_ptr GPU::AllocateChannel() { + return impl->AllocateChannel(); +} + +void GPU::InitChannel(Control::ChannelState& to_init) { + impl->InitChannel(to_init); +} + +void GPU::BindChannel(s32 channel_id) { + impl->BindChannel(channel_id); +} + +void GPU::ReleaseChannel(Control::ChannelState& to_release) { + impl->ReleaseChannel(to_release); +} + void GPU::BindRenderer(std::unique_ptr renderer) { impl->BindRenderer(std::move(renderer)); } -void GPU::CallMethod(const MethodCall& method_call) { - impl->CallMethod(method_call); -} - -void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - impl->CallMultiMethod(method, subchannel, base_start, amount, methods_pending); -} - void GPU::FlushCommands() { impl->FlushCommands(); } @@ -881,8 +575,8 @@ void GPU::ReleaseContext() { impl->ReleaseContext(); } -void GPU::PushGPUEntries(Tegra::CommandList&& entries) { - impl->PushGPUEntries(std::move(entries)); +void GPU::PushGPUEntries(s32 channel, Tegra::CommandList&& entries) { + impl->PushGPUEntries(channel, std::move(entries)); } void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) { diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 42c91954f7..74d55e074e 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -89,57 +89,20 @@ class Maxwell3D; class KeplerCompute; } // namespace Engines -enum class EngineID { - FERMI_TWOD_A = 0x902D, // 2D Engine - MAXWELL_B = 0xB197, // 3D Engine - KEPLER_COMPUTE_B = 0xB1C0, - KEPLER_INLINE_TO_MEMORY_B = 0xA140, - MAXWELL_DMA_COPY_A = 0xB0B5, -}; +namespace Control { +struct ChannelState; +} class MemoryManager; class GPU final { public: - struct MethodCall { - u32 method{}; - u32 argument{}; - u32 subchannel{}; - u32 method_count{}; - - explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0) - : method(method_), argument(argument_), subchannel(subchannel_), - method_count(method_count_) {} - - [[nodiscard]] bool IsLastCall() const { - return method_count <= 1; - } - }; - - enum class FenceOperation : u32 { - Acquire = 0, - Increment = 1, - }; - - union FenceAction { - u32 raw; - BitField<0, 1, FenceOperation> op; - BitField<8, 24, u32> syncpoint_id; - }; - explicit GPU(Core::System& system, bool is_async, bool use_nvdec); ~GPU(); /// Binds a renderer to the GPU. void BindRenderer(std::unique_ptr renderer); - /// Calls a GPU method. - void CallMethod(const MethodCall& method_call); - - /// Calls a GPU multivalue method. - void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending); - /// Flush all current written commands into the host GPU for execution. void FlushCommands(); /// Synchronizes CPU writes with Host GPU memory. @@ -147,6 +110,14 @@ public: /// Signal the ending of command list. void OnCommandListEnd(); + std::shared_ptr AllocateChannel(); + + void InitChannel(Control::ChannelState& to_init); + + void BindChannel(s32 channel_id); + + void ReleaseChannel(Control::ChannelState& to_release); + /// Request a host GPU memory flush from the CPU. [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); @@ -226,7 +197,7 @@ public: void ReleaseContext(); /// Push GPU command entries to be processed - void PushGPUEntries(Tegra::CommandList&& entries); + void PushGPUEntries(s32 channel, Tegra::CommandList&& entries); /// Push GPU command buffer entries to be processed void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries); @@ -248,7 +219,7 @@ public: private: struct Impl; - std::unique_ptr impl; + mutable std::unique_ptr impl; }; } // namespace Tegra diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index f0e48cfbd0..9844cde43a 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -8,6 +8,7 @@ #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" +#include "video_core/control/scheduler.h" #include "video_core/dma_pusher.h" #include "video_core/gpu.h" #include "video_core/gpu_thread.h" @@ -18,7 +19,7 @@ namespace VideoCommon::GPUThread { /// Runs the GPU thread static void RunThread(std::stop_token stop_token, Core::System& system, VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, - Tegra::DmaPusher& dma_pusher, SynchState& state) { + Tegra::Control::Scheduler& scheduler, SynchState& state) { std::string name = "GPU"; MicroProfileOnThreadCreate(name.c_str()); SCOPE_EXIT({ MicroProfileOnThreadExit(); }); @@ -36,8 +37,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system, break; } if (auto* submit_list = std::get_if(&next.data)) { - dma_pusher.Push(std::move(submit_list->entries)); - dma_pusher.DispatchCalls(); + scheduler.Push(submit_list->channel, std::move(submit_list->entries)); } else if (const auto* data = std::get_if(&next.data)) { renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); } else if (std::holds_alternative(next.data)) { @@ -68,14 +68,14 @@ ThreadManager::~ThreadManager() = default; void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, - Tegra::DmaPusher& dma_pusher) { + Tegra::Control::Scheduler& scheduler) { rasterizer = renderer.ReadRasterizer(); thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), - std::ref(dma_pusher), std::ref(state)); + std::ref(scheduler), std::ref(state)); } -void ThreadManager::SubmitList(Tegra::CommandList&& entries) { - PushCommand(SubmitListCommand(std::move(entries))); +void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { + PushCommand(SubmitListCommand(channel, std::move(entries))); } void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 2f8210cb9a..c5078a2b37 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -15,7 +15,9 @@ namespace Tegra { struct FramebufferConfig; -class DmaPusher; +namespace Control { +class Scheduler; +} } // namespace Tegra namespace Core { @@ -34,8 +36,10 @@ namespace VideoCommon::GPUThread { /// Command to signal to the GPU thread that a command list is ready for processing struct SubmitListCommand final { - explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {} + explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_) + : channel{channel_}, entries{std::move(entries_)} {} + s32 channel; Tegra::CommandList entries; }; @@ -112,10 +116,10 @@ public: /// Creates and starts the GPU thread. void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, - Tegra::DmaPusher& dma_pusher); + Tegra::Control::Scheduler& scheduler); /// Push GPU command entries to be processed - void SubmitList(Tegra::CommandList&& entries); + void SubmitList(s32 channel, Tegra::CommandList&& entries); /// Swap buffers (render frame) void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index bf9eb735d9..a3efd365e3 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -133,11 +133,6 @@ void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::s // TryLockPage(page_entry, size); auto& current_page = page_table[PageEntryIndex(gpu_addr)]; - if ((!current_page.IsValid() && page_entry.IsValid()) || - current_page.ToAddress() != page_entry.ToAddress()) { - rasterizer->ModifyGPUMemory(gpu_addr, size); - } - current_page = page_entry; } diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 889b606b38..eb68ea6388 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -17,6 +17,7 @@ #include "common/assert.h" #include "common/settings.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" @@ -90,13 +91,10 @@ private: }; template -class QueryCacheBase { +class QueryCacheBase : public VideoCommon::ChannelSetupCaches { public: - explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::MemoryManager& gpu_memory_) - : rasterizer{rasterizer_}, maxwell3d{maxwell3d_}, - gpu_memory{gpu_memory_}, streams{{CounterStream{static_cast(*this), + explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_) + : rasterizer{rasterizer_}, streams{{CounterStream{static_cast(*this), VideoCore::QueryType::SamplesPassed}}} {} void InvalidateRegion(VAddr addr, std::size_t size) { @@ -117,13 +115,13 @@ public: */ void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional timestamp) { std::unique_lock lock{mutex}; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); CachedQuery* query = TryGet(*cpu_addr); if (!query) { ASSERT_OR_EXECUTE(cpu_addr, return;); - u8* const host_ptr = gpu_memory.GetPointer(gpu_addr); + u8* const host_ptr = gpu_memory->GetPointer(gpu_addr); query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); } @@ -137,7 +135,7 @@ public: /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. void UpdateCounters() { std::unique_lock lock{mutex}; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); } @@ -264,8 +262,6 @@ private: static constexpr unsigned YUZU_PAGEBITS = 12; VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::MemoryManager& gpu_memory; std::recursive_mutex mutex; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index a04a764815..8dacb26268 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -16,6 +16,9 @@ class MemoryManager; namespace Engines { class AccelerateDMAInterface; } +namespace Control { +struct ChannelState; +} } // namespace Tegra namespace VideoCore { @@ -137,5 +140,11 @@ public: /// Initialize disk cached resources for the game being emulated virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const DiskResourceLoadCallback& callback) {} + + virtual void InitializeChannel(Tegra::Control::ChannelState& channel) {} + + virtual void BindChannel(Tegra::Control::ChannelState& channel) {} + + virtual void ReleaseChannel(s32 channel_id) {} }; } // namespace VideoCore diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp index 6e82c2e28d..c76446b605 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.cpp +++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp @@ -12,7 +12,7 @@ namespace OpenGL { GLInnerFence::GLInnerFence(u32 payload_, bool is_stubbed_) : FenceBase{payload_, is_stubbed_} {} -GLInnerFence::GLInnerFence(GPUVAddr address_, u32 payload_, bool is_stubbed_) +GLInnerFence::GLInnerFence(u8* address_, u32 payload_, bool is_stubbed_) : FenceBase{address_, payload_, is_stubbed_} {} GLInnerFence::~GLInnerFence() = default; @@ -52,7 +52,7 @@ Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) { return std::make_shared(value, is_stubbed); } -Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { +Fence FenceManagerOpenGL::CreateFence(u8* addr, u32 value, bool is_stubbed) { return std::make_shared(addr, value, is_stubbed); } diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h index 14ff00db21..fced8d0029 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.h +++ b/src/video_core/renderer_opengl/gl_fence_manager.h @@ -17,7 +17,7 @@ namespace OpenGL { class GLInnerFence : public VideoCommon::FenceBase { public: explicit GLInnerFence(u32 payload_, bool is_stubbed_); - explicit GLInnerFence(GPUVAddr address_, u32 payload_, bool is_stubbed_); + explicit GLInnerFence(u8* address_, u32 payload_, bool is_stubbed_); ~GLInnerFence(); void Queue(); @@ -41,7 +41,7 @@ public: protected: Fence CreateFence(u32 value, bool is_stubbed) override; - Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override; + Fence CreateFence(u8* addr, u32 value, bool is_stubbed) override; void QueueFence(Fence& fence) override; bool IsFenceSignaled(Fence& fence) const override; void WaitFence(Fence& fence) override; diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp index ed40f5791d..5070db4417 100644 --- a/src/video_core/renderer_opengl/gl_query_cache.cpp +++ b/src/video_core/renderer_opengl/gl_query_cache.cpp @@ -26,9 +26,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) { } // Anonymous namespace -QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::MemoryManager& gpu_memory_) - : QueryCacheBase(rasterizer_, maxwell3d_, gpu_memory_), gl_rasterizer{rasterizer_} {} +QueryCache::QueryCache(RasterizerOpenGL& rasterizer_) + : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {} QueryCache::~QueryCache() = default; diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h index 8a49f1ef0d..14ce59990b 100644 --- a/src/video_core/renderer_opengl/gl_query_cache.h +++ b/src/video_core/renderer_opengl/gl_query_cache.h @@ -28,8 +28,7 @@ using CounterStream = VideoCommon::CounterStreamBase; class QueryCache final : public VideoCommon::QueryCacheBase { public: - explicit QueryCache(RasterizerOpenGL& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::MemoryManager& gpu_memory_); + explicit QueryCache(RasterizerOpenGL& rasterizer_); ~QueryCache(); OGLQuery AllocateQuery(VideoCore::QueryType type); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a0d048b0be..e8d61bd417 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -60,12 +60,11 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_), screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_), texture_cache_runtime(device, program_manager, state_tracker), - texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), - buffer_cache_runtime(device), - buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), - shader_cache(*this, emu_window_, maxwell3d, kepler_compute, gpu_memory, device, texture_cache, - buffer_cache, program_manager, state_tracker, gpu.ShaderNotify()), - query_cache(*this, maxwell3d, gpu_memory), accelerate_dma(buffer_cache), + texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device), + buffer_cache(*this, cpu_memory_, buffer_cache_runtime), + shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, + state_tracker, gpu.ShaderNotify()), + query_cache(*this), accelerate_dma(buffer_cache), fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} RasterizerOpenGL::~RasterizerOpenGL() = default; @@ -392,7 +391,8 @@ void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { gpu_memory.Write(addr, value); return; } - fence_manager.SignalSemaphore(addr, value); + auto paddr = gpu_memory.GetPointer(addr); + fence_manager.SignalSemaphore(paddr, value); } void RasterizerOpenGL::SignalSyncPoint(u32 value) { diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 0b8d8ec927..494581d0d3 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -151,16 +151,13 @@ void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs } // Anonymous namespace ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, const Device& device_, - TextureCache& texture_cache_, BufferCache& buffer_cache_, - ProgramManager& program_manager_, StateTracker& state_tracker_, - VideoCore::ShaderNotify& shader_notify_) - : VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_}, - emu_window{emu_window_}, device{device_}, texture_cache{texture_cache_}, - buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{state_tracker_}, - shader_notify{shader_notify_}, use_asynchronous_shaders{device.UseAsynchronousShaders()}, + const Device& device_, TextureCache& texture_cache_, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, VideoCore::ShaderNotify& shader_notify_) + : VideoCommon::ShaderCache{rasterizer_}, emu_window{emu_window_}, device{device_}, + texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, + state_tracker{state_tracker_}, shader_notify{shader_notify_}, + use_asynchronous_shaders{device.UseAsynchronousShaders()}, profile{ .supported_spirv = 0x00010000, @@ -310,7 +307,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { current_pipeline = nullptr; return nullptr; } - const auto& regs{maxwell3d.regs}; + const auto& regs{maxwell3d->regs}; graphics_key.raw = 0; graphics_key.early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 @@ -351,13 +348,13 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n } // If something is using depth, we can assume that games are not rendering anything which // will be used one time. - if (maxwell3d.regs.zeta_enable) { + if (maxwell3d->regs.zeta_enable) { return nullptr; } // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async - if (maxwell3d.regs.index_array.count <= 6 || maxwell3d.regs.vertex_buffer.count <= 6) { + if (maxwell3d->regs.index_array.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) { return pipeline; } return nullptr; @@ -368,7 +365,7 @@ ComputePipeline* ShaderCache::CurrentComputePipeline() { if (!shader) { return nullptr; } - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const ComputePipelineKey key{ .unique_hash = shader->unique_hash, .shared_memory_size = qmd.shared_alloc, @@ -481,8 +478,8 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( } auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; return std::make_unique( - device, texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker, - thread_worker, &shader_notify, sources, sources_spirv, infos, key); + device, texture_cache, buffer_cache, *gpu_memory, *maxwell3d, program_manager, + state_tracker, thread_worker, &shader_notify, sources, sources_spirv, infos, key); } catch (Shader::Exception& exception) { LOG_ERROR(Render_OpenGL, "{}", exception.what()); @@ -491,9 +488,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( std::unique_ptr ShaderCache::CreateComputePipeline( const ComputePipelineKey& key, const VideoCommon::ShaderInfo* shader) { - const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; - const auto& qmd{kepler_compute.launch_description}; - ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; + const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()}; + const auto& qmd{kepler_compute->launch_description}; + ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; env.SetCachedSize(shader->size_bytes); main_pools.ReleaseContents(); @@ -536,8 +533,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline( break; } - return std::make_unique(device, texture_cache, buffer_cache, gpu_memory, - kepler_compute, program_manager, program.info, code, + return std::make_unique(device, texture_cache, buffer_cache, *gpu_memory, + *kepler_compute, program_manager, program.info, code, code_spirv); } catch (Shader::Exception& exception) { LOG_ERROR(Render_OpenGL, "{}", exception.what()); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index a14269dea6..89f181fe30 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -30,12 +30,9 @@ using ShaderWorker = Common::StatefulThreadWorker; class ShaderCache : public VideoCommon::ShaderCache { public: explicit ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, const Device& device_, - TextureCache& texture_cache_, BufferCache& buffer_cache_, - ProgramManager& program_manager_, StateTracker& state_tracker_, - VideoCore::ShaderNotify& shader_notify_); + const Device& device_, TextureCache& texture_cache_, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, VideoCore::ShaderNotify& shader_notify_); ~ShaderCache(); void LoadDiskResources(u64 title_id, std::stop_token stop_loading, diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7c78d02996..68c2bc34c4 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -95,20 +95,25 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) try - : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), - cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), + : RendererBase(emu_window, std::move(context_)), + telemetry_session(telemetry_session_), + cpu_memory(cpu_memory_), + gpu(gpu_), + library(OpenLibrary()), instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug.GetValue())), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), - device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), - state_tracker(gpu), scheduler(device, state_tracker), + device(CreateDevice(instance, dld, *surface)), + memory_allocator(device, false), + state_tracker(gpu), + scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, screen_info), - rasterizer(render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, device, - memory_allocator, state_tracker, scheduler) { + rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, + state_tracker, scheduler) { Report(); } catch (const vk::Exception& exception) { LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp index c249b34d4c..301cbbabe8 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp @@ -14,7 +14,7 @@ namespace Vulkan { InnerFence::InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_) : FenceBase{payload_, is_stubbed_}, scheduler{scheduler_} {} -InnerFence::InnerFence(Scheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_) +InnerFence::InnerFence(Scheduler& scheduler_, u8* address_, u32 payload_, bool is_stubbed_) : FenceBase{address_, payload_, is_stubbed_}, scheduler{scheduler_} {} InnerFence::~InnerFence() = default; @@ -52,7 +52,7 @@ Fence FenceManager::CreateFence(u32 value, bool is_stubbed) { return std::make_shared(scheduler, value, is_stubbed); } -Fence FenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { +Fence FenceManager::CreateFence(u8* addr, u32 value, bool is_stubbed) { return std::make_shared(scheduler, addr, value, is_stubbed); } diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h index 7c0bbd80a2..ea9e88052d 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.h +++ b/src/video_core/renderer_vulkan/vk_fence_manager.h @@ -26,7 +26,7 @@ class Scheduler; class InnerFence : public VideoCommon::FenceBase { public: explicit InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_); - explicit InnerFence(Scheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_); + explicit InnerFence(Scheduler& scheduler_, u8* address_, u32 payload_, bool is_stubbed_); ~InnerFence(); void Queue(); @@ -51,7 +51,7 @@ public: protected: Fence CreateFence(u32 value, bool is_stubbed) override; - Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override; + Fence CreateFence(u8* addr, u32 value, bool is_stubbed) override; void QueueFence(Fence& fence) override; bool IsFenceSignaled(Fence& fence) const override; void WaitFence(Fence& fence) override; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index accbfc8e11..b1e0b96c49 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -259,17 +259,15 @@ bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) c return std::memcmp(&rhs, this, Size()) == 0; } -PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, const Device& device_, +PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, UpdateDescriptorQueue& update_descriptor_queue_, RenderPassCache& render_pass_cache_, BufferCache& buffer_cache_, TextureCache& texture_cache_, VideoCore::ShaderNotify& shader_notify_) - : VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_}, - device{device_}, scheduler{scheduler_}, descriptor_pool{descriptor_pool_}, - update_descriptor_queue{update_descriptor_queue_}, render_pass_cache{render_pass_cache_}, - buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, + : VideoCommon::ShaderCache{rasterizer_}, device{device_}, scheduler{scheduler_}, + descriptor_pool{descriptor_pool_}, update_descriptor_queue{update_descriptor_queue_}, + render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, + texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), serialization_thread(1, "VkPipelineSerialization") { @@ -337,7 +335,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { current_pipeline = nullptr; return nullptr; } - graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported(), + graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(), device.IsExtVertexInputDynamicStateSupported()); if (current_pipeline) { @@ -357,7 +355,7 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() { if (!shader) { return nullptr; } - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const ComputePipelineCacheKey key{ .unique_hash = shader->unique_hash, .shared_memory_size = qmd.shared_alloc, @@ -486,13 +484,13 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const } // If something is using depth, we can assume that games are not rendering anything which // will be used one time. - if (maxwell3d.regs.zeta_enable) { + if (maxwell3d->regs.zeta_enable) { return nullptr; } // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async - if (maxwell3d.regs.index_array.count <= 6 || maxwell3d.regs.vertex_buffer.count <= 6) { + if (maxwell3d->regs.index_array.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) { return pipeline; } return nullptr; @@ -558,7 +556,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique( - maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, + *maxwell3d, *gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, std::move(modules), infos); @@ -592,9 +590,9 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { std::unique_ptr PipelineCache::CreateComputePipeline( const ComputePipelineCacheKey& key, const ShaderInfo* shader) { - const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; - const auto& qmd{kepler_compute.launch_description}; - ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; + const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()}; + const auto& qmd{kepler_compute->launch_description}; + ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; env.SetCachedSize(shader->size_bytes); main_pools.ReleaseContents(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 127957dbf5..61f9e9366a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -100,10 +100,8 @@ struct ShaderPools { class PipelineCache : public VideoCommon::ShaderCache { public: - explicit PipelineCache(RasterizerVulkan& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, - Tegra::Engines::KeplerCompute& kepler_compute, - Tegra::MemoryManager& gpu_memory, const Device& device, - Scheduler& scheduler, DescriptorPool& descriptor_pool, + explicit PipelineCache(RasterizerVulkan& rasterizer, const Device& device, Scheduler& scheduler, + DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, RenderPassCache& render_pass_cache, BufferCache& buffer_cache, TextureCache& texture_cache, VideoCore::ShaderNotify& shader_notify_); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 2b859c6b8c..393bbdf378 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -65,10 +65,9 @@ void QueryPool::Reserve(std::pair query) { usage[pool_index * GROW_STEP + static_cast(query.second)] = false; } -QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - const Device& device_, Scheduler& scheduler_) - : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_}, +QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, + Scheduler& scheduler_) + : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_}, query_pools{ QueryPool{device_, scheduler_, QueryType::SamplesPassed}, } {} diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h index b0d86c4f80..26762ee097 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.h +++ b/src/video_core/renderer_vulkan/vk_query_cache.h @@ -52,9 +52,8 @@ private: class QueryCache final : public VideoCommon::QueryCacheBase { public: - explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - const Device& device_, Scheduler& scheduler_); + explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, + Scheduler& scheduler_); ~QueryCache(); std::pair AllocateQuery(VideoCore::QueryType type); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7e40c2df1a..5d9ff0589d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -11,6 +11,7 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" +#include "video_core/control/channel_state.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/blit_image.h" @@ -148,14 +149,11 @@ DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instan } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_, const Device& device_, MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, Scheduler& scheduler_) - : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, - gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, - screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_}, - state_tracker{state_tracker_}, scheduler{scheduler_}, + : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, screen_info{screen_info_}, device{device_}, + memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), update_descriptor_queue(device, scheduler), blit_image(device, scheduler, state_tracker, descriptor_pool), @@ -165,14 +163,13 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra memory_allocator, staging_pool, blit_image, astc_decoder_pass, render_pass_cache}, - texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), + texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, update_descriptor_queue, descriptor_pool), - buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), - pipeline_cache(*this, maxwell3d, kepler_compute, gpu_memory, device, scheduler, - descriptor_pool, update_descriptor_queue, render_pass_cache, buffer_cache, - texture_cache, gpu.ShaderNotify()), - query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, accelerate_dma{buffer_cache}, + buffer_cache(*this, cpu_memory_, buffer_cache_runtime), + pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue, + render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()), + query_cache{*this, device, scheduler}, accelerate_dma{buffer_cache}, fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); @@ -199,8 +196,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { UpdateDynamicStates(); - const auto& regs{maxwell3d.regs}; - const u32 num_instances{maxwell3d.mme_draw.instance_count}; + 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)}; scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { if (draw_params.is_indexed) { @@ -218,14 +215,14 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { void RasterizerVulkan::Clear() { MICROPROFILE_SCOPE(Vulkan_Clearing); - if (!maxwell3d.ShouldExecute()) { + if (!maxwell3d->ShouldExecute()) { return; } FlushWork(); query_cache.UpdateCounters(); - auto& regs = maxwell3d.regs; + auto& regs = maxwell3d->regs; const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || regs.clear_buffers.A; const bool use_depth = regs.clear_buffers.Z; @@ -339,9 +336,9 @@ void RasterizerVulkan::DispatchCompute() { return; } std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; - pipeline->Configure(kepler_compute, gpu_memory, scheduler, buffer_cache, texture_cache); + pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const std::array dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); @@ -451,10 +448,11 @@ void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) { void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { if (!gpu.IsAsync()) { - gpu_memory.Write(addr, value); + gpu_memory->Write(addr, value); return; } - fence_manager.SignalSemaphore(addr, value); + auto paddr = gpu_memory->GetPointer(addr); + fence_manager.SignalSemaphore(paddr, value); } void RasterizerVulkan::SignalSyncPoint(u32 value) { @@ -553,12 +551,12 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span memory) { - auto cpu_addr = gpu_memory.GpuToCpuAddress(address); + auto cpu_addr = gpu_memory->GpuToCpuAddress(address); if (!cpu_addr) [[unlikely]] { - gpu_memory.WriteBlock(address, memory.data(), copy_size); + gpu_memory->WriteBlock(address, memory.data(), copy_size); return; } - gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); + gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { std::unique_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { @@ -627,7 +625,7 @@ bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 } void RasterizerVulkan::UpdateDynamicStates() { - auto& regs = maxwell3d.regs; + auto& regs = maxwell3d->regs; UpdateViewportsState(regs); UpdateScissorsState(regs); UpdateDepthBias(regs); @@ -651,7 +649,7 @@ void RasterizerVulkan::UpdateDynamicStates() { } void RasterizerVulkan::BeginTransformFeedback() { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.tfb_enabled == 0) { return; } @@ -667,7 +665,7 @@ void RasterizerVulkan::BeginTransformFeedback() { } void RasterizerVulkan::EndTransformFeedback() { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.tfb_enabled == 0) { return; } @@ -917,7 +915,7 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& } void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { - auto& dirty{maxwell3d.dirty.flags}; + auto& dirty{maxwell3d->dirty.flags}; if (!dirty[Dirty::VertexInput]) { return; } @@ -974,4 +972,41 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) }); } +void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { + CreateChannel(channel); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.CreateChannel(channel); + buffer_cache.CreateChannel(channel); + } + pipeline_cache.CreateChannel(channel); + query_cache.CreateChannel(channel); + state_tracker.SetupTables(channel); +} + +void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { + const s32 channel_id = channel.bind_id; + BindToChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.BindToChannel(channel_id); + buffer_cache.BindToChannel(channel_id); + } + pipeline_cache.BindToChannel(channel_id); + query_cache.BindToChannel(channel_id); + state_tracker.ChangeChannel(channel); + scheduler.InvalidateState(); +} + +void RasterizerVulkan::ReleaseChannel(s32 channel_id) { + EraseChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.EraseChannel(channel_id); + buffer_cache.EraseChannel(channel_id); + } + pipeline_cache.EraseChannel(channel_id); + query_cache.EraseChannel(channel_id); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 0370ea39be..642fe65769 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -8,6 +8,7 @@ #include #include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" @@ -54,13 +55,13 @@ private: BufferCache& buffer_cache; }; -class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { +class RasterizerVulkan final : public VideoCore::RasterizerAccelerated, + protected VideoCommon::ChannelSetupCaches { public: explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - ScreenInfo& screen_info_, const Device& device_, - MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, - Scheduler& scheduler_); + Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_, + const Device& device_, MemoryAllocator& memory_allocator_, + StateTracker& state_tracker_, Scheduler& scheduler_); ~RasterizerVulkan() override; void Draw(bool is_indexed, bool is_instanced) override; @@ -99,6 +100,12 @@ public: void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) override; + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + + void BindChannel(Tegra::Control::ChannelState& channel) override; + + void ReleaseChannel(s32 channel_id) override; + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; @@ -134,9 +141,6 @@ private: void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); Tegra::GPU& gpu; - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; ScreenInfo& screen_info; const Device& device; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 9ad0964319..a87bf8dd33 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -7,6 +7,7 @@ #include "common/common_types.h" #include "core/core.h" +#include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" @@ -174,9 +175,8 @@ void SetupDirtyVertexBindings(Tables& tables) { } } // Anonymous namespace -StateTracker::StateTracker(Tegra::GPU& gpu) - : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} { - auto& tables{gpu.Maxwell3D().dirty.tables}; +void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { + auto& tables{channel_state.maxwell_3d->dirty.tables}; SetupDirtyFlags(tables); SetupDirtyViewports(tables); SetupDirtyScissors(tables); @@ -199,4 +199,11 @@ StateTracker::StateTracker(Tegra::GPU& gpu) SetupDirtyVertexBindings(tables); } +void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { + flags = &channel_state.maxwell_3d->dirty.flags; +} + +StateTracker::StateTracker(Tegra::GPU& gpu) + : flags{}, invalidation_flags{MakeInvalidationFlags()} {} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index a85bc1c10d..9f8a887f9c 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -10,6 +10,12 @@ #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" +namespace Tegra { +namespace Control { +struct ChannelState; +} +} // namespace Tegra + namespace Vulkan { namespace Dirty { @@ -56,16 +62,16 @@ public: explicit StateTracker(Tegra::GPU& gpu); void InvalidateCommandBufferState() { - flags |= invalidation_flags; + (*flags) |= invalidation_flags; current_topology = INVALID_TOPOLOGY; } void InvalidateViewports() { - flags[Dirty::Viewports] = true; + (*flags)[Dirty::Viewports] = true; } void InvalidateScissors() { - flags[Dirty::Scissors] = true; + (*flags)[Dirty::Scissors] = true; } bool TouchViewports() { @@ -139,16 +145,20 @@ public: return has_changed; } + void SetupTables(Tegra::Control::ChannelState& channel_state); + + void ChangeChannel(Tegra::Control::ChannelState& channel_state); + private: static constexpr auto INVALID_TOPOLOGY = static_cast(~0u); bool Exchange(std::size_t id, bool new_value) const noexcept { - const bool is_dirty = flags[id]; - flags[id] = new_value; + const bool is_dirty = (*flags)[id]; + (*flags)[id] = new_value; return is_dirty; } - Tegra::Engines::Maxwell3D::DirtyState::Flags& flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; }; diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 164e4ee0ef..f530665794 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/object_pool.h" +#include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" @@ -33,29 +34,25 @@ void ShaderCache::SyncGuestHost() { RemovePendingShaders(); } -ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_) - : gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, kepler_compute{kepler_compute_}, - rasterizer{rasterizer_} {} +ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {} bool ShaderCache::RefreshStages(std::array& unique_hashes) { - auto& dirty{maxwell3d.dirty.flags}; + auto& dirty{maxwell3d->dirty.flags}; if (!dirty[VideoCommon::Dirty::Shaders]) { return last_shaders_valid; } dirty[VideoCommon::Dirty::Shaders] = false; - const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; + const GPUVAddr base_addr{maxwell3d->regs.code_address.CodeAddress()}; for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) { - if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { + if (!maxwell3d->regs.IsShaderConfigEnabled(index)) { unique_hashes[index] = 0; continue; } - const auto& shader_config{maxwell3d.regs.shader_config[index]}; + const auto& shader_config{maxwell3d->regs.shader_config[index]}; const auto program{static_cast(index)}; const GPUVAddr shader_addr{base_addr + shader_config.offset}; - const std::optional cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; + const std::optional cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)}; if (!cpu_shader_addr) { LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr); last_shaders_valid = false; @@ -64,7 +61,7 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)}; if (!shader_info) { const u32 start_address{shader_config.offset}; - GraphicsEnvironment env{maxwell3d, gpu_memory, program, base_addr, start_address}; + GraphicsEnvironment env{*maxwell3d, *gpu_memory, program, base_addr, start_address}; shader_info = MakeShaderInfo(env, *cpu_shader_addr); } shader_infos[index] = shader_info; @@ -75,10 +72,10 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { } const ShaderInfo* ShaderCache::ComputeShader() { - const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; - const auto& qmd{kepler_compute.launch_description}; + const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()}; + const auto& qmd{kepler_compute->launch_description}; const GPUVAddr shader_addr{program_base + qmd.program_start}; - const std::optional cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; + const std::optional cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)}; if (!cpu_shader_addr) { LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr); return nullptr; @@ -86,22 +83,22 @@ const ShaderInfo* ShaderCache::ComputeShader() { if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) { return shader; } - ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; + ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; return MakeShaderInfo(env, *cpu_shader_addr); } void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result, const std::array& unique_hashes) { size_t env_index{}; - const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; + const GPUVAddr base_addr{maxwell3d->regs.code_address.CodeAddress()}; for (size_t index = 0; index < NUM_PROGRAMS; ++index) { if (unique_hashes[index] == 0) { continue; } const auto program{static_cast(index)}; auto& env{result.envs[index]}; - const u32 start_address{maxwell3d.regs.shader_config[index].offset}; - env = GraphicsEnvironment{maxwell3d, gpu_memory, program, base_addr, start_address}; + const u32 start_address{maxwell3d->regs.shader_config[index].offset}; + env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, base_addr, start_address}; env.SetCachedSize(shader_infos[index]->size_bytes); result.env_ptrs[env_index++] = &env; } diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index f67cea8c49..a4391202de 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -12,6 +12,7 @@ #include #include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/shader_environment.h" @@ -19,6 +20,10 @@ namespace Tegra { class MemoryManager; } +namespace Tegra::Control { +struct ChannelState; +} + namespace VideoCommon { class GenericEnvironment; @@ -28,7 +33,7 @@ struct ShaderInfo { size_t size_bytes{}; }; -class ShaderCache { +class ShaderCache : public VideoCommon::ChannelSetupCaches { static constexpr u64 YUZU_PAGEBITS = 14; static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS; @@ -71,9 +76,7 @@ protected: } }; - explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_); + explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_); /// @brief Update the hashes and information of shader stages /// @param unique_hashes Shader hashes to store into when a stage is enabled @@ -88,10 +91,6 @@ protected: void GetGraphicsEnvironments(GraphicsEnvironments& result, const std::array& unique_hashes); - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - std::array shader_infos{}; bool last_shaders_valid = false; diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 1f85ec9da0..620565684b 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -88,6 +88,9 @@ struct ImageBase { u32 scale_rating = 0; u64 scale_tick = 0; bool has_scaled = false; + + size_t channel = 0; + ImageFlagBits flags = ImageFlagBits::CpuModified; GPUVAddr gpu_addr = 0; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 1dbe01bc0b..2731aead07 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/settings.h" +#include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" #include "video_core/texture_cache/image_view_base.h" @@ -29,12 +30,8 @@ using VideoCore::Surface::SurfaceType; using namespace Common::Literals; template -TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_) - : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_}, - kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_} { +TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_) + : runtime{runtime_}, rasterizer{rasterizer_} { // Configure null sampler TSCEntry sampler_descriptor{}; sampler_descriptor.min_filter.Assign(Tegra::Texture::TextureFilter::Linear); @@ -42,6 +39,13 @@ TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); sampler_descriptor.cubemap_anisotropy.Assign(1); + // Setup channels + current_channel_id = UNSET_CHANNEL; + state = nullptr; + maxwell3d = nullptr; + kepler_compute = nullptr; + gpu_memory = nullptr; + // Make sure the first index is reserved for the null resources // This way the null resource becomes a compile time constant void(slot_images.insert(NullImageParams{})); @@ -93,7 +97,7 @@ void TextureCache

::RunGarbageCollector() { const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); } if (True(image.flags & ImageFlagBits::Tracked)) { UntrackImage(image, image_id); @@ -152,22 +156,23 @@ void TextureCache

::MarkModification(ImageId id) noexcept { template template void TextureCache

::FillGraphicsImageViews(std::span views) { - FillImageViews(graphics_image_table, graphics_image_view_ids, views); + FillImageViews(state->graphics_image_table, state->graphics_image_view_ids, + views); } template void TextureCache

::FillComputeImageViews(std::span views) { - FillImageViews(compute_image_table, compute_image_view_ids, views); + FillImageViews(state->compute_image_table, state->compute_image_view_ids, views); } template typename P::Sampler* TextureCache

::GetGraphicsSampler(u32 index) { - if (index > graphics_sampler_table.Limit()) { + if (index > state->graphics_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return &slot_samplers[NULL_SAMPLER_ID]; } - const auto [descriptor, is_new] = graphics_sampler_table.Read(index); - SamplerId& id = graphics_sampler_ids[index]; + const auto [descriptor, is_new] = state->graphics_sampler_table.Read(index); + SamplerId& id = state->graphics_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } @@ -176,12 +181,12 @@ typename P::Sampler* TextureCache

::GetGraphicsSampler(u32 index) { template typename P::Sampler* TextureCache

::GetComputeSampler(u32 index) { - if (index > compute_sampler_table.Limit()) { + if (index > state->compute_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return &slot_samplers[NULL_SAMPLER_ID]; } - const auto [descriptor, is_new] = compute_sampler_table.Read(index); - SamplerId& id = compute_sampler_ids[index]; + const auto [descriptor, is_new] = state->compute_sampler_table.Read(index); + SamplerId& id = state->compute_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } @@ -191,34 +196,34 @@ typename P::Sampler* TextureCache

::GetComputeSampler(u32 index) { template void TextureCache

::SynchronizeGraphicsDescriptors() { using SamplerIndex = Tegra::Engines::Maxwell3D::Regs::SamplerIndex; - const bool linked_tsc = maxwell3d.regs.sampler_index == SamplerIndex::ViaHeaderIndex; - const u32 tic_limit = maxwell3d.regs.tic.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d.regs.tsc.limit; - if (graphics_sampler_table.Synchornize(maxwell3d.regs.tsc.Address(), tsc_limit)) { - graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); + const bool linked_tsc = maxwell3d->regs.sampler_index == SamplerIndex::ViaHeaderIndex; + const u32 tic_limit = maxwell3d->regs.tic.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tsc.limit; + if (state->graphics_sampler_table.Synchornize(maxwell3d->regs.tsc.Address(), tsc_limit)) { + state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (graphics_image_table.Synchornize(maxwell3d.regs.tic.Address(), tic_limit)) { - graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); + if (state->graphics_image_table.Synchornize(maxwell3d->regs.tic.Address(), tic_limit)) { + state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } template void TextureCache

::SynchronizeComputeDescriptors() { - const bool linked_tsc = kepler_compute.launch_description.linked_tsc; - const u32 tic_limit = kepler_compute.regs.tic.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute.regs.tsc.limit; - const GPUVAddr tsc_gpu_addr = kepler_compute.regs.tsc.Address(); - if (compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { - compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); + const bool linked_tsc = kepler_compute->launch_description.linked_tsc; + const u32 tic_limit = kepler_compute->regs.tic.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; + const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address(); + if (state->compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { + state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (compute_image_table.Synchornize(kepler_compute.regs.tic.Address(), tic_limit)) { - compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); + if (state->compute_image_table.Synchornize(kepler_compute->regs.tic.Address(), tic_limit)) { + state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } template bool TextureCache

::RescaleRenderTargets(bool is_clear) { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; u32 scale_rating = 0; bool rescaled = false; std::array tmp_color_images{}; @@ -315,7 +320,7 @@ bool TextureCache

::RescaleRenderTargets(bool is_clear) { template void TextureCache

::UpdateRenderTargets(bool is_clear) { using namespace VideoCommon::Dirty; - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::RenderTargets]) { for (size_t index = 0; index < NUM_RT; ++index) { ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; @@ -342,7 +347,7 @@ void TextureCache

::UpdateRenderTargets(bool is_clear) { PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id)); for (size_t index = 0; index < NUM_RT; ++index) { - render_targets.draw_buffers[index] = static_cast(maxwell3d.regs.rt_control.Map(index)); + render_targets.draw_buffers[index] = static_cast(maxwell3d->regs.rt_control.Map(index)); } u32 up_scale = 1; u32 down_shift = 0; @@ -351,8 +356,8 @@ void TextureCache

::UpdateRenderTargets(bool is_clear) { down_shift = Settings::values.resolution_info.down_shift; } render_targets.size = Extent2D{ - (maxwell3d.regs.render_area.width * up_scale) >> down_shift, - (maxwell3d.regs.render_area.height * up_scale) >> down_shift, + (maxwell3d->regs.render_area.width * up_scale) >> down_shift, + (maxwell3d->regs.render_area.height * up_scale) >> down_shift, }; flags[Dirty::DepthBiasGlobal] = true; @@ -458,7 +463,7 @@ void TextureCache

::DownloadMemory(VAddr cpu_addr, size_t size) { const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); } } @@ -655,7 +660,7 @@ void TextureCache

::PopAsyncFlushes() { for (const ImageId image_id : download_ids) { const ImageBase& image = slot_images[image_id]; const auto copies = FullDownloadCopies(image.info); - SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, download_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span); download_map.offset += image.unswizzled_size_bytes; download_span = download_span.subspan(image.unswizzled_size_bytes); } @@ -714,26 +719,26 @@ void TextureCache

::UploadImageContents(Image& image, StagingBuffer& staging) const GPUVAddr gpu_addr = image.gpu_addr; if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { - gpu_memory.ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); + gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); const auto uploads = FullUploadSwizzles(image.info); runtime.AccelerateImageUpload(image, staging, uploads); } else if (True(image.flags & ImageFlagBits::Converted)) { std::vector unswizzled_data(image.unswizzled_size_bytes); - auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, unswizzled_data); + auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data); ConvertImage(unswizzled_data, image.info, mapped_span, copies); image.UploadMemory(staging, copies); } else { - const auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, mapped_span); + const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span); image.UploadMemory(staging, copies); } } template ImageViewId TextureCache

::FindImageView(const TICEntry& config) { - if (!IsValidEntry(gpu_memory, config)) { + if (!IsValidEntry(*gpu_memory, config)) { return NULL_IMAGE_VIEW_ID; } - const auto [pair, is_new] = image_views.try_emplace(config); + const auto [pair, is_new] = state->image_views.try_emplace(config); ImageViewId& image_view_id = pair->second; if (is_new) { image_view_id = CreateImageView(config); @@ -777,9 +782,9 @@ ImageId TextureCache

::FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_a template ImageId TextureCache

::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, RelaxedOptions options) { - std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr) { - cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info)); + cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info)); if (!cpu_addr) { return ImageId{}; } @@ -860,7 +865,7 @@ void TextureCache

::InvalidateScale(Image& image) { image.scale_tick = frame_tick + 1; } const std::span image_view_ids = image.image_view_ids; - auto& dirty = maxwell3d.dirty.flags; + auto& dirty = maxwell3d->dirty.flags; dirty[Dirty::RenderTargets] = true; dirty[Dirty::ZetaBuffer] = true; for (size_t rt = 0; rt < NUM_RT; ++rt) { @@ -881,11 +886,11 @@ void TextureCache

::InvalidateScale(Image& image) { image.image_view_ids.clear(); image.image_view_infos.clear(); if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(compute_image_view_ids, CORRUPT_ID); + std::ranges::fill(state->graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(state->compute_image_view_ids, CORRUPT_ID); } - graphics_image_table.Invalidate(); - compute_image_table.Invalidate(); + state->graphics_image_table.Invalidate(); + state->compute_image_table.Invalidate(); has_deleted_images = true; } @@ -929,10 +934,10 @@ bool TextureCache

::ScaleDown(Image& image) { template ImageId TextureCache

::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, RelaxedOptions options) { - std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr) { const auto size = CalculateGuestSizeInBytes(info); - cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, size); + cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, size); if (!cpu_addr) { const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space; virtual_invalid_space += Common::AlignUp(size, 32); @@ -1050,7 +1055,7 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); Image& new_image = slot_images[new_image_id]; - if (!gpu_memory.IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { + if (!gpu_memory->IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { new_image.flags |= ImageFlagBits::Sparse; } @@ -1192,7 +1197,7 @@ SamplerId TextureCache

::FindSampler(const TSCEntry& config) { if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) { return NULL_SAMPLER_ID; } - const auto [pair, is_new] = samplers.try_emplace(config); + const auto [pair, is_new] = state->samplers.try_emplace(config); if (is_new) { pair->second = slot_samplers.insert(runtime, config); } @@ -1201,7 +1206,7 @@ SamplerId TextureCache

::FindSampler(const TSCEntry& config) { template ImageViewId TextureCache

::FindColorBuffer(size_t index, bool is_clear) { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (index >= regs.rt_control.count) { return ImageViewId{}; } @@ -1219,7 +1224,7 @@ ImageViewId TextureCache

::FindColorBuffer(size_t index, bool is_clear) { template ImageViewId TextureCache

::FindDepthBuffer(bool is_clear) { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (!regs.zeta_enable) { return ImageViewId{}; } @@ -1321,8 +1326,8 @@ void TextureCache

::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Fu static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { - const auto it = gpu_page_table.find(page); - if (it == gpu_page_table.end()) { + const auto it = state->gpu_page_table.find(page); + if (it == state->gpu_page_table.end()) { if constexpr (BOOL_BREAK) { return false; } else { @@ -1403,9 +1408,9 @@ template void TextureCache

::ForEachSparseSegment(ImageBase& image, Func&& func) { using FuncReturn = typename std::invoke_result::type; static constexpr bool RETURNS_BOOL = std::is_same_v; - const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); + const auto segments = gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); for (const auto& [gpu_addr, size] : segments) { - std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); if constexpr (RETURNS_BOOL) { if (func(gpu_addr, *cpu_addr, size)) { @@ -1449,7 +1454,7 @@ void TextureCache

::RegisterImage(ImageId image_id) { image.lru_index = lru_cache.Insert(image_id, frame_tick); ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, - [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); }); + [this, image_id](u64 page) { state->gpu_page_table[page].push_back(image_id); }); if (False(image.flags & ImageFlagBits::Sparse)) { auto map_id = slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id); @@ -1497,8 +1502,9 @@ void TextureCache

::UnregisterImage(ImageId image_id) { } image_ids.erase(vector_it); }; - ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, - [this, &clear_page_table](u64 page) { clear_page_table(page, gpu_page_table); }); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) { + clear_page_table(page, state->gpu_page_table); + }); if (False(image.flags & ImageFlagBits::Sparse)) { const auto map_id = image.map_view_id; ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { @@ -1631,7 +1637,7 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); // Mark render targets as dirty - auto& dirty = maxwell3d.dirty.flags; + auto& dirty = maxwell3d->dirty.flags; dirty[Dirty::RenderTargets] = true; dirty[Dirty::ZetaBuffer] = true; for (size_t rt = 0; rt < NUM_RT; ++rt) { @@ -1681,22 +1687,24 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { if (alloc_images.empty()) { image_allocs_table.erase(alloc_it); } - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(compute_image_view_ids, CORRUPT_ID); + for (auto& this_state : channel_storage) { + if constexpr (ENABLE_VALIDATION) { + std::ranges::fill(this_state.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(this_state.compute_image_view_ids, CORRUPT_ID); + } + this_state.graphics_image_table.Invalidate(); + this_state.compute_image_table.Invalidate(); } - graphics_image_table.Invalidate(); - compute_image_table.Invalidate(); has_deleted_images = true; } template void TextureCache

::RemoveImageViewReferences(std::span removed_views) { - auto it = image_views.begin(); - while (it != image_views.end()) { + auto it = state->image_views.begin(); + while (it != state->image_views.end()) { const auto found = std::ranges::find(removed_views, it->second); if (found != removed_views.end()) { - it = image_views.erase(it); + it = state->image_views.erase(it); } else { ++it; } @@ -1943,7 +1951,7 @@ bool TextureCache

::IsFullClear(ImageViewId id) { const ImageViewBase& image_view = slot_image_views[id]; const ImageBase& image = slot_images[image_view.image_id]; const Extent3D size = image_view.size; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; const auto& scissor = regs.scissor_test[0]; if (image.info.resources.levels > 1 || image.info.resources.layers > 1) { // Images with multiple resources can't be cleared in a single call @@ -1958,4 +1966,61 @@ bool TextureCache

::IsFullClear(ImageViewId id) { scissor.max_y >= size.height; } +template +TextureCache

::ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& state) noexcept + : maxwell3d{*state.maxwell_3d}, kepler_compute{*state.kepler_compute}, + gpu_memory{*state.memory_manager}, graphics_image_table{gpu_memory}, + graphics_sampler_table{gpu_memory}, compute_image_table{gpu_memory}, compute_sampler_table{ + gpu_memory} {} + +template +void TextureCache

::CreateChannel(struct Tegra::Control::ChannelState& channel) { + ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0); + auto new_id = [this, &channel]() { + if (!free_channel_ids.empty()) { + auto id = free_channel_ids.front(); + free_channel_ids.pop_front(); + new (&channel_storage[id]) ChannelInfo(channel); + return id; + } + channel_storage.emplace_back(channel); + return channel_storage.size() - 1; + }(); + channel_map.emplace(channel.bind_id, new_id); + if (current_channel_id != UNSET_CHANNEL) { + state = &channel_storage[current_channel_id]; + } +} + +/// Bind a channel for execution. +template +void TextureCache

::BindToChannel(s32 id) { + auto it = channel_map.find(id); + ASSERT(it != channel_map.end() && id >= 0); + current_channel_id = it->second; + state = &channel_storage[current_channel_id]; + maxwell3d = &state->maxwell3d; + kepler_compute = &state->kepler_compute; + gpu_memory = &state->gpu_memory; +} + +/// Erase channel's state. +template +void TextureCache

::EraseChannel(s32 id) { + const auto it = channel_map.find(id); + ASSERT(it != channel_map.end() && id >= 0); + const auto this_id = it->second; + free_channel_ids.push_back(this_id); + channel_map.erase(it); + if (this_id == current_channel_id) { + current_channel_id = UNSET_CHANNEL; + state = nullptr; + maxwell3d = nullptr; + kepler_compute = nullptr; + gpu_memory = nullptr; + } else if (current_channel_id != UNSET_CHANNEL) { + state = &channel_storage[current_channel_id]; + } +} + } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 7e6c6cef20..69efcb7182 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -3,6 +3,8 @@ #pragma once +#include +#include #include #include #include @@ -26,6 +28,10 @@ #include "video_core/texture_cache/types.h" #include "video_core/textures/texture.h" +namespace Tegra::Control { +struct ChannelState; +} + namespace VideoCommon { using Tegra::Texture::SwizzleSource; @@ -58,6 +64,8 @@ class TextureCache { /// True when the API can provide info about the memory of the device. static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; + static constexpr size_t UNSET_CHANNEL{std::numeric_limits::max()}; + static constexpr s64 TARGET_THRESHOLD = 4_GiB; static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB; static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB; @@ -85,8 +93,7 @@ class TextureCache { }; public: - explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&, - Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&); + explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&); /// Notify the cache that a new frame has been queued void TickFrame(); @@ -171,6 +178,15 @@ public: [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept; + /// Create channel state. + void CreateChannel(struct Tegra::Control::ChannelState& channel); + + /// Bind a channel for execution. + void BindToChannel(s32 id); + + /// Erase channel's state. + void EraseChannel(s32 id); + std::mutex mutex; private: @@ -338,31 +354,52 @@ private: u64 GetScaledImageSizeBytes(ImageBase& image); Runtime& runtime; + + struct ChannelInfo { + ChannelInfo() = delete; + ChannelInfo(struct Tegra::Control::ChannelState& state) noexcept; + ChannelInfo(const ChannelInfo& state) = delete; + ChannelInfo& operator=(const ChannelInfo&) = delete; + ChannelInfo(ChannelInfo&& other) noexcept = default; + ChannelInfo& operator=(ChannelInfo&& other) noexcept = default; + + Tegra::Engines::Maxwell3D& maxwell3d; + Tegra::Engines::KeplerCompute& kepler_compute; + Tegra::MemoryManager& gpu_memory; + + DescriptorTable graphics_image_table{gpu_memory}; + DescriptorTable graphics_sampler_table{gpu_memory}; + std::vector graphics_sampler_ids; + std::vector graphics_image_view_ids; + + DescriptorTable compute_image_table{gpu_memory}; + DescriptorTable compute_sampler_table{gpu_memory}; + std::vector compute_sampler_ids; + std::vector compute_image_view_ids; + + std::unordered_map image_views; + std::unordered_map samplers; + + std::unordered_map, IdentityHash> gpu_page_table; + }; + + std::deque channel_storage; + std::deque free_channel_ids; + std::unordered_map channel_map; + + ChannelInfo* state; + size_t current_channel_id{UNSET_CHANNEL}; VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; - - DescriptorTable graphics_image_table{gpu_memory}; - DescriptorTable graphics_sampler_table{gpu_memory}; - std::vector graphics_sampler_ids; - std::vector graphics_image_view_ids; - - DescriptorTable compute_image_table{gpu_memory}; - DescriptorTable compute_sampler_table{gpu_memory}; - std::vector compute_sampler_ids; - std::vector compute_image_view_ids; + Tegra::Engines::Maxwell3D* maxwell3d; + Tegra::Engines::KeplerCompute* kepler_compute; + Tegra::MemoryManager* gpu_memory; RenderTargets render_targets; - std::unordered_map image_views; - std::unordered_map samplers; std::unordered_map framebuffers; std::unordered_map, IdentityHash> page_table; - std::unordered_map, IdentityHash> gpu_page_table; std::unordered_map, IdentityHash> sparse_page_table; - std::unordered_map> sparse_views; VAddr virtual_invalid_space{}; From 2c62563ab58a70236a39571149f8370f3fdfb2a3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 7 Nov 2021 14:17:32 +0100 Subject: [PATCH 053/245] NVHOST_CTRl: Implement missing method and fix some stuffs. --- src/CMakeLists.txt | 1 + .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 30 ++++++++++++++++--- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 5 ++-- src/video_core/gpu.cpp | 5 ++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54de1dc94f..3575a3cb3e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -121,6 +121,7 @@ else() if (ARCHITECTURE_x86_64) add_compile_options("-mcx16") + add_compile_options("-fwrapv") endif() if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index abde2a6d35..0e22d92730 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -52,6 +52,8 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& return IocCtrlEventRegister(input, output); case 0x20: return IocCtrlEventUnregister(input, output); + case 0x21: + return IocCtrlEventUnregisterBatch(input, output); } break; default: @@ -249,6 +251,25 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector& input, return FreeEvent(event_id); } +NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector& input, + std::vector& output) { + IocCtrlEventUnregisterBatchParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + u64 event_mask = params.user_events; + LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); + + auto lock = NvEventsLock(); + while (event_mask != 0) { + const u64 event_id = std::countr_zero(event_mask); + event_mask &= ~(1ULL << event_id); + const auto result = FreeEvent(static_cast(event_id)); + if (result != NvResult::Success) { + return result; + } + } + return NvResult::Success; +} + NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::vector& output) { IocCtrlEventClearParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); @@ -362,10 +383,11 @@ u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { } void nvhost_ctrl::SignalNvEvent(u32 syncpoint_id, u32 value) { - const u32 max = MaxNvEvents - std::countl_zero(events_mask); - const u32 min = std::countr_zero(events_mask); - for (u32 i = min; i < max; i++) { - auto& event = events[i]; + u64 signal_mask = events_mask; + while (signal_mask != 0) { + const u64 event_id = std::countr_zero(signal_mask); + signal_mask &= ~(1ULL << event_id); + auto& event = events[event_id]; if (event.assigned_syncpt != syncpoint_id || event.assigned_value != value) { continue; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index f2fc5d0472..9bd10257bb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -177,16 +177,17 @@ private: static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, "IocCtrlEventUnregisterParams is incorrect size"); - struct IocCtrlEventKill { + struct IocCtrlEventUnregisterBatchParams { u64_le user_events{}; }; - static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); + static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, "IocCtrlEventKill is incorrect size"); NvResult NvOsGetConfigU32(const std::vector& input, std::vector& output); NvResult IocCtrlEventWait(const std::vector& input, std::vector& output, bool is_allocation); NvResult IocCtrlEventRegister(const std::vector& input, std::vector& output); NvResult IocCtrlEventUnregister(const std::vector& input, std::vector& output); + NvResult IocCtrlEventUnregisterBatch(const std::vector& input, std::vector& output); NvResult IocCtrlClearEventWait(const std::vector& input, std::vector& output); NvResult FreeEvent(u32 slot); diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 80a1c69e00..8c0ff00941 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -249,6 +249,11 @@ struct GPU::Impl { void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { std::scoped_lock lock{sync_mutex}; + u32 current_value = syncpoints.at(syncpoint_id).load(); + if ((static_cast(current_value) - static_cast(value)) >= 0) { + TriggerCpuInterrupt(syncpoint_id, value); + return; + } auto& interrupt = syncpt_interrupts.at(syncpoint_id); bool contains = std::any_of(interrupt.begin(), interrupt.end(), [value](u32 in_value) { return in_value == value; }); From d7990c159e956e5431c501fa94405dd04496197c Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 7 Nov 2021 17:15:28 +0100 Subject: [PATCH 054/245] OpenGl: Implement Channels. --- .../renderer_opengl/gl_rasterizer.cpp | 174 +++++++++++------- .../renderer_opengl/gl_rasterizer.h | 13 +- .../renderer_opengl/gl_state_tracker.cpp | 17 +- .../renderer_opengl/gl_state_tracker.h | 82 +++++---- .../renderer_opengl/renderer_opengl.cpp | 2 +- .../renderer_vulkan/renderer_vulkan.cpp | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 2 +- .../renderer_vulkan/vk_state_tracker.cpp | 8 +- .../renderer_vulkan/vk_state_tracker.h | 4 +- 9 files changed, 186 insertions(+), 118 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e8d61bd417..8cfa0013fb 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -16,7 +16,7 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" - +#include "video_core/control/channel_state.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" @@ -56,9 +56,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra Core::Memory::Memory& cpu_memory_, const Device& device_, ScreenInfo& screen_info_, ProgramManager& program_manager_, StateTracker& state_tracker_) - : RasterizerAccelerated(cpu_memory_), gpu(gpu_), maxwell3d(gpu.Maxwell3D()), - kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_), - screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_), + : RasterizerAccelerated(cpu_memory_), gpu(gpu_), device(device_), screen_info(screen_info_), + program_manager(program_manager_), state_tracker(state_tracker_), texture_cache_runtime(device, program_manager, state_tracker), texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device), buffer_cache(*this, cpu_memory_, buffer_cache_runtime), @@ -70,7 +69,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra RasterizerOpenGL::~RasterizerOpenGL() = default; void RasterizerOpenGL::SyncVertexFormats() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::VertexFormats]) { return; } @@ -88,7 +87,7 @@ void RasterizerOpenGL::SyncVertexFormats() { } flags[Dirty::VertexFormat0 + index] = false; - const auto attrib = maxwell3d.regs.vertex_attrib_format[index]; + const auto attrib = maxwell3d->regs.vertex_attrib_format[index]; const auto gl_index = static_cast(index); // Disable constant attributes. @@ -112,13 +111,13 @@ void RasterizerOpenGL::SyncVertexFormats() { } void RasterizerOpenGL::SyncVertexInstances() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::VertexInstances]) { return; } flags[Dirty::VertexInstances] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { if (!flags[Dirty::VertexInstance0 + index]) { continue; @@ -139,11 +138,11 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load void RasterizerOpenGL::Clear() { MICROPROFILE_SCOPE(OpenGL_Clears); - if (!maxwell3d.ShouldExecute()) { + if (!maxwell3d->ShouldExecute()) { return; } - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; bool use_color{}; bool use_depth{}; bool use_stencil{}; @@ -221,17 +220,17 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { SyncState(); - const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology); + const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); BeginTransformFeedback(pipeline, primitive_mode); - const GLuint base_instance = static_cast(maxwell3d.regs.vb_base_instance); + const GLuint base_instance = static_cast(maxwell3d->regs.vb_base_instance); const GLsizei num_instances = - static_cast(is_instanced ? maxwell3d.mme_draw.instance_count : 1); + static_cast(is_instanced ? maxwell3d->mme_draw.instance_count : 1); if (is_indexed) { - const GLint base_vertex = static_cast(maxwell3d.regs.vb_element_base); - const GLsizei num_vertices = static_cast(maxwell3d.regs.index_array.count); + const GLint base_vertex = static_cast(maxwell3d->regs.vb_element_base); + const GLsizei num_vertices = static_cast(maxwell3d->regs.index_array.count); const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); - const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format); + const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_array.format); if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { glDrawElements(primitive_mode, num_vertices, format, offset); } else if (num_instances == 1 && base_instance == 0) { @@ -250,8 +249,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { base_instance); } } else { - const GLint base_vertex = static_cast(maxwell3d.regs.vertex_buffer.first); - const GLsizei num_vertices = static_cast(maxwell3d.regs.vertex_buffer.count); + const GLint base_vertex = static_cast(maxwell3d->regs.vertex_buffer.first); + const GLsizei num_vertices = static_cast(maxwell3d->regs.vertex_buffer.count); if (num_instances == 1 && base_instance == 0) { glDrawArrays(primitive_mode, base_vertex, num_vertices); } else if (base_instance == 0) { @@ -273,7 +272,7 @@ void RasterizerOpenGL::DispatchCompute() { return; } pipeline->Configure(); - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); ++num_queued_commands; has_written_global_memory |= pipeline->WritesGlobalMemory(); @@ -388,10 +387,10 @@ void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) { void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { if (!gpu.IsAsync()) { - gpu_memory.Write(addr, value); + gpu_memory->Write(addr, value); return; } - auto paddr = gpu_memory.GetPointer(addr); + auto paddr = gpu_memory->GetPointer(addr); fence_manager.SignalSemaphore(paddr, value); } @@ -483,12 +482,12 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span memory) { - auto cpu_addr = gpu_memory.GpuToCpuAddress(address); + auto cpu_addr = gpu_memory->GpuToCpuAddress(address); if (!cpu_addr) [[unlikely]] { - gpu_memory.WriteBlock(address, memory.data(), copy_size); + gpu_memory->WriteBlock(address, memory.data(), copy_size); return; } - gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); + gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { std::unique_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { @@ -551,8 +550,8 @@ void RasterizerOpenGL::SyncState() { } void RasterizerOpenGL::SyncViewport() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; const bool rescale_viewports = flags[VideoCommon::Dirty::RescaleViewports]; const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; @@ -657,23 +656,23 @@ void RasterizerOpenGL::SyncViewport() { } void RasterizerOpenGL::SyncDepthClamp() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::DepthClampEnabled]) { return; } flags[Dirty::DepthClampEnabled] = false; - oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0); + oglEnable(GL_DEPTH_CLAMP, maxwell3d->regs.view_volume_clip_control.depth_clamp_disabled == 0); } void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::ClipDistances] && !flags[VideoCommon::Dirty::Shaders]) { return; } flags[Dirty::ClipDistances] = false; - clip_mask &= maxwell3d.regs.clip_distance_enabled; + clip_mask &= maxwell3d->regs.clip_distance_enabled; if (clip_mask == last_clip_distance_mask) { return; } @@ -689,8 +688,8 @@ void RasterizerOpenGL::SyncClipCoef() { } void RasterizerOpenGL::SyncCullMode() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; if (flags[Dirty::CullTest]) { flags[Dirty::CullTest] = false; @@ -705,23 +704,23 @@ void RasterizerOpenGL::SyncCullMode() { } void RasterizerOpenGL::SyncPrimitiveRestart() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PrimitiveRestart]) { return; } flags[Dirty::PrimitiveRestart] = false; - if (maxwell3d.regs.primitive_restart.enabled) { + if (maxwell3d->regs.primitive_restart.enabled) { glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index); + glPrimitiveRestartIndex(maxwell3d->regs.primitive_restart.index); } else { glDisable(GL_PRIMITIVE_RESTART); } } void RasterizerOpenGL::SyncDepthTestState() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; if (flags[Dirty::DepthMask]) { flags[Dirty::DepthMask] = false; @@ -740,13 +739,13 @@ void RasterizerOpenGL::SyncDepthTestState() { } void RasterizerOpenGL::SyncStencilTestState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::StencilTest]) { return; } flags[Dirty::StencilTest] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; oglEnable(GL_STENCIL_TEST, regs.stencil_enable); glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), @@ -771,23 +770,23 @@ void RasterizerOpenGL::SyncStencilTestState() { } void RasterizerOpenGL::SyncRasterizeEnable() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::RasterizeEnable]) { return; } flags[Dirty::RasterizeEnable] = false; - oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0); + oglEnable(GL_RASTERIZER_DISCARD, maxwell3d->regs.rasterize_enable == 0); } void RasterizerOpenGL::SyncPolygonModes() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PolygonModes]) { return; } flags[Dirty::PolygonModes] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.fill_rectangle) { if (!GLAD_GL_NV_fill_rectangle) { LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported"); @@ -820,7 +819,7 @@ void RasterizerOpenGL::SyncPolygonModes() { } void RasterizerOpenGL::SyncColorMask() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::ColorMasks]) { return; } @@ -829,7 +828,7 @@ void RasterizerOpenGL::SyncColorMask() { const bool force = flags[Dirty::ColorMaskCommon]; flags[Dirty::ColorMaskCommon] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.color_mask_common) { if (!force && !flags[Dirty::ColorMask0]) { return; @@ -854,30 +853,30 @@ void RasterizerOpenGL::SyncColorMask() { } void RasterizerOpenGL::SyncMultiSampleState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::MultisampleControl]) { return; } flags[Dirty::MultisampleControl] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage); oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one); } void RasterizerOpenGL::SyncFragmentColorClampState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::FragmentClampColor]) { return; } flags[Dirty::FragmentClampColor] = false; - glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE); + glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d->regs.frag_color_clamp ? GL_TRUE : GL_FALSE); } void RasterizerOpenGL::SyncBlendState() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; if (flags[Dirty::BlendColor]) { flags[Dirty::BlendColor] = false; @@ -934,13 +933,13 @@ void RasterizerOpenGL::SyncBlendState() { } void RasterizerOpenGL::SyncLogicOpState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::LogicOp]) { return; } flags[Dirty::LogicOp] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.logic_op.enable) { glEnable(GL_COLOR_LOGIC_OP); glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation)); @@ -950,7 +949,7 @@ void RasterizerOpenGL::SyncLogicOpState() { } void RasterizerOpenGL::SyncScissorTest() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::Scissors] && !flags[VideoCommon::Dirty::RescaleScissors]) { return; } @@ -959,7 +958,7 @@ void RasterizerOpenGL::SyncScissorTest() { const bool force = flags[VideoCommon::Dirty::RescaleScissors]; flags[VideoCommon::Dirty::RescaleScissors] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; const auto& resolution = Settings::values.resolution_info; const bool is_rescaling{texture_cache.IsRescaling()}; @@ -995,39 +994,39 @@ void RasterizerOpenGL::SyncScissorTest() { } void RasterizerOpenGL::SyncPointState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PointSize]) { return; } flags[Dirty::PointSize] = false; - oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable); - oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d.regs.vp_point_size.enable); + oglEnable(GL_POINT_SPRITE, maxwell3d->regs.point_sprite_enable); + oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.vp_point_size.enable); const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; - glPointSize(std::max(1.0f, maxwell3d.regs.point_size * scale)); + glPointSize(std::max(1.0f, maxwell3d->regs.point_size * scale)); } void RasterizerOpenGL::SyncLineState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::LineWidth]) { return; } flags[Dirty::LineWidth] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable); glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased); } void RasterizerOpenGL::SyncPolygonOffset() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PolygonOffset]) { return; } flags[Dirty::PolygonOffset] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable); oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable); oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable); @@ -1041,13 +1040,13 @@ void RasterizerOpenGL::SyncPolygonOffset() { } void RasterizerOpenGL::SyncAlphaTest() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::AlphaTest]) { return; } flags[Dirty::AlphaTest] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.alpha_test_enabled) { glEnable(GL_ALPHA_TEST); glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref); @@ -1057,17 +1056,17 @@ void RasterizerOpenGL::SyncAlphaTest() { } void RasterizerOpenGL::SyncFramebufferSRGB() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::FramebufferSRGB]) { return; } flags[Dirty::FramebufferSRGB] = false; - oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb); + oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d->regs.framebuffer_srgb); } void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.tfb_enabled == 0) { return; } @@ -1086,11 +1085,48 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum } void RasterizerOpenGL::EndTransformFeedback() { - if (maxwell3d.regs.tfb_enabled != 0) { + if (maxwell3d->regs.tfb_enabled != 0) { glEndTransformFeedback(); } } +void RasterizerOpenGL::InitializeChannel(Tegra::Control::ChannelState& channel) { + CreateChannel(channel); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.CreateChannel(channel); + buffer_cache.CreateChannel(channel); + } + shader_cache.CreateChannel(channel); + query_cache.CreateChannel(channel); + state_tracker.SetupTables(channel); +} + +void RasterizerOpenGL::BindChannel(Tegra::Control::ChannelState& channel) { + const s32 channel_id = channel.bind_id; + BindToChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.BindToChannel(channel_id); + buffer_cache.BindToChannel(channel_id); + } + shader_cache.BindToChannel(channel_id); + query_cache.BindToChannel(channel_id); + state_tracker.ChangeChannel(channel); + state_tracker.InvalidateState(); +} + +void RasterizerOpenGL::ReleaseChannel(s32 channel_id) { + EraseChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.EraseChannel(channel_id); + buffer_cache.EraseChannel(channel_id); + } + shader_cache.EraseChannel(channel_id); + query_cache.EraseChannel(channel_id); +} + AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 31a16fcbaf..39b20cfb20 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -12,6 +12,7 @@ #include #include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" @@ -58,7 +59,8 @@ private: BufferCache& buffer_cache; }; -class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { +class RasterizerOpenGL : public VideoCore::RasterizerAccelerated, + protected VideoCommon::ChannelSetupCaches { public: explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Core::Memory::Memory& cpu_memory_, const Device& device_, @@ -107,6 +109,12 @@ public: return num_queued_commands > 0; } + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + + void BindChannel(Tegra::Control::ChannelState& channel) override; + + void ReleaseChannel(s32 channel_id) override; + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; @@ -191,9 +199,6 @@ private: void EndTransformFeedback(); Tegra::GPU& gpu; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; const Device& device; ScreenInfo& screen_info; diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index 912725ef72..3657f867d3 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -7,8 +7,8 @@ #include "common/common_types.h" #include "core/core.h" +#include "video_core/control/channel_state.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" #include "video_core/renderer_opengl/gl_state_tracker.h" #define OFF(field_name) MAXWELL3D_REG_INDEX(field_name) @@ -202,9 +202,8 @@ void SetupDirtyMisc(Tables& tables) { } // Anonymous namespace -StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} { - auto& dirty = gpu.Maxwell3D().dirty; - auto& tables = dirty.tables; +void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { + auto& tables{channel_state.maxwell_3d->dirty.tables}; SetupDirtyFlags(tables); SetupDirtyColorMasks(tables); SetupDirtyViewports(tables); @@ -230,4 +229,14 @@ StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} SetupDirtyMisc(tables); } +void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { + flags = &channel_state.maxwell_3d->dirty.flags; +} + +void StateTracker::InvalidateState() { + flags->set(); +} + +StateTracker::StateTracker() : flags{} {} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h index 04e024f085..97d32768b5 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.h +++ b/src/video_core/renderer_opengl/gl_state_tracker.h @@ -12,8 +12,10 @@ #include "video_core/engines/maxwell_3d.h" namespace Tegra { -class GPU; +namespace Control { +struct ChannelState; } +} // namespace Tegra namespace OpenGL { @@ -83,7 +85,7 @@ static_assert(Last <= std::numeric_limits::max()); class StateTracker { public: - explicit StateTracker(Tegra::GPU& gpu); + explicit StateTracker(); void BindIndexBuffer(GLuint new_index_buffer) { if (index_buffer == new_index_buffer) { @@ -121,94 +123,106 @@ public: } void NotifyScreenDrawVertexArray() { - flags[OpenGL::Dirty::VertexFormats] = true; - flags[OpenGL::Dirty::VertexFormat0 + 0] = true; - flags[OpenGL::Dirty::VertexFormat0 + 1] = true; + (*flags)[OpenGL::Dirty::VertexFormats] = true; + (*flags)[OpenGL::Dirty::VertexFormat0 + 0] = true; + (*flags)[OpenGL::Dirty::VertexFormat0 + 1] = true; - flags[VideoCommon::Dirty::VertexBuffers] = true; - flags[VideoCommon::Dirty::VertexBuffer0] = true; + (*flags)[VideoCommon::Dirty::VertexBuffers] = true; + (*flags)[VideoCommon::Dirty::VertexBuffer0] = true; - flags[OpenGL::Dirty::VertexInstances] = true; - flags[OpenGL::Dirty::VertexInstance0 + 0] = true; - flags[OpenGL::Dirty::VertexInstance0 + 1] = true; + (*flags)[OpenGL::Dirty::VertexInstances] = true; + (*flags)[OpenGL::Dirty::VertexInstance0 + 0] = true; + (*flags)[OpenGL::Dirty::VertexInstance0 + 1] = true; } void NotifyPolygonModes() { - flags[OpenGL::Dirty::PolygonModes] = true; - flags[OpenGL::Dirty::PolygonModeFront] = true; - flags[OpenGL::Dirty::PolygonModeBack] = true; + (*flags)[OpenGL::Dirty::PolygonModes] = true; + (*flags)[OpenGL::Dirty::PolygonModeFront] = true; + (*flags)[OpenGL::Dirty::PolygonModeBack] = true; } void NotifyViewport0() { - flags[OpenGL::Dirty::Viewports] = true; - flags[OpenGL::Dirty::Viewport0] = true; + (*flags)[OpenGL::Dirty::Viewports] = true; + (*flags)[OpenGL::Dirty::Viewport0] = true; } void NotifyScissor0() { - flags[OpenGL::Dirty::Scissors] = true; - flags[OpenGL::Dirty::Scissor0] = true; + (*flags)[OpenGL::Dirty::Scissors] = true; + (*flags)[OpenGL::Dirty::Scissor0] = true; } void NotifyColorMask(size_t index) { - flags[OpenGL::Dirty::ColorMasks] = true; - flags[OpenGL::Dirty::ColorMask0 + index] = true; + (*flags)[OpenGL::Dirty::ColorMasks] = true; + (*flags)[OpenGL::Dirty::ColorMask0 + index] = true; } void NotifyBlend0() { - flags[OpenGL::Dirty::BlendStates] = true; - flags[OpenGL::Dirty::BlendState0] = true; + (*flags)[OpenGL::Dirty::BlendStates] = true; + (*flags)[OpenGL::Dirty::BlendState0] = true; } void NotifyFramebuffer() { - flags[VideoCommon::Dirty::RenderTargets] = true; + (*flags)[VideoCommon::Dirty::RenderTargets] = true; } void NotifyFrontFace() { - flags[OpenGL::Dirty::FrontFace] = true; + (*flags)[OpenGL::Dirty::FrontFace] = true; } void NotifyCullTest() { - flags[OpenGL::Dirty::CullTest] = true; + (*flags)[OpenGL::Dirty::CullTest] = true; } void NotifyDepthMask() { - flags[OpenGL::Dirty::DepthMask] = true; + (*flags)[OpenGL::Dirty::DepthMask] = true; } void NotifyDepthTest() { - flags[OpenGL::Dirty::DepthTest] = true; + (*flags)[OpenGL::Dirty::DepthTest] = true; } void NotifyStencilTest() { - flags[OpenGL::Dirty::StencilTest] = true; + (*flags)[OpenGL::Dirty::StencilTest] = true; } void NotifyPolygonOffset() { - flags[OpenGL::Dirty::PolygonOffset] = true; + (*flags)[OpenGL::Dirty::PolygonOffset] = true; } void NotifyRasterizeEnable() { - flags[OpenGL::Dirty::RasterizeEnable] = true; + (*flags)[OpenGL::Dirty::RasterizeEnable] = true; } void NotifyFramebufferSRGB() { - flags[OpenGL::Dirty::FramebufferSRGB] = true; + (*flags)[OpenGL::Dirty::FramebufferSRGB] = true; } void NotifyLogicOp() { - flags[OpenGL::Dirty::LogicOp] = true; + (*flags)[OpenGL::Dirty::LogicOp] = true; } void NotifyClipControl() { - flags[OpenGL::Dirty::ClipControl] = true; + (*flags)[OpenGL::Dirty::ClipControl] = true; } void NotifyAlphaTest() { - flags[OpenGL::Dirty::AlphaTest] = true; + (*flags)[OpenGL::Dirty::AlphaTest] = true; } + void NotifyRange(u8 start, u8 end) { + for (auto flag = start; flag <= end; flag++) { + (*flags)[flag] = true; + } + } + + void SetupTables(Tegra::Control::ChannelState& channel_state); + + void ChangeChannel(Tegra::Control::ChannelState& channel_state); + + void InvalidateState(); + private: - Tegra::Engines::Maxwell3D::DirtyState::Flags& flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; GLuint framebuffer = 0; GLuint index_buffer = 0; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 34f3f7a679..8bd5eba7e2 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -131,7 +131,7 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, - emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{gpu}, + emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{}, program_manager{device}, rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 68c2bc34c4..d12669c9d4 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -106,7 +106,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, surface(CreateSurface(instance, render_window)), device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), - state_tracker(gpu), + state_tracker(), scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5d9ff0589d..bf750452f4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -995,7 +995,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { pipeline_cache.BindToChannel(channel_id); query_cache.BindToChannel(channel_id); state_tracker.ChangeChannel(channel); - scheduler.InvalidateState(); + state_tracker.InvalidateState(); } void RasterizerVulkan::ReleaseChannel(s32 channel_id) { diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index a87bf8dd33..5a11d3267b 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -10,7 +10,6 @@ #include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" #define OFF(field_name) MAXWELL3D_REG_INDEX(field_name) @@ -203,7 +202,10 @@ void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { flags = &channel_state.maxwell_3d->dirty.flags; } -StateTracker::StateTracker(Tegra::GPU& gpu) - : flags{}, invalidation_flags{MakeInvalidationFlags()} {} +void StateTracker::InvalidateState() { + flags->set(); +} + +StateTracker::StateTracker() : flags{}, invalidation_flags{MakeInvalidationFlags()} {} } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 9f8a887f9c..c107d9c246 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -59,7 +59,7 @@ class StateTracker { using Maxwell = Tegra::Engines::Maxwell3D::Regs; public: - explicit StateTracker(Tegra::GPU& gpu); + explicit StateTracker(); void InvalidateCommandBufferState() { (*flags) |= invalidation_flags; @@ -149,6 +149,8 @@ public: void ChangeChannel(Tegra::Control::ChannelState& channel_state); + void InvalidateState(); + private: static constexpr auto INVALID_TOPOLOGY = static_cast(~0u); From 3f8e7a55851a613becf715cbf3016a8e9f63d65f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 7 Nov 2021 17:52:45 +0100 Subject: [PATCH 055/245] VideoCore: Fix channels with disk pipeline/shader cache. --- .../renderer_opengl/gl_compute_pipeline.cpp | 19 ++++++------- .../renderer_opengl/gl_compute_pipeline.h | 16 +++++++---- .../renderer_opengl/gl_graphics_pipeline.cpp | 28 +++++++++---------- .../renderer_opengl/gl_graphics_pipeline.h | 16 +++++++---- .../renderer_opengl/gl_rasterizer.cpp | 1 + .../renderer_opengl/gl_shader_cache.cpp | 11 ++++---- .../renderer_vulkan/vk_graphics_pipeline.cpp | 17 ++++++----- .../renderer_vulkan/vk_graphics_pipeline.h | 28 +++++++++++-------- .../renderer_vulkan/vk_pipeline_cache.cpp | 8 +++--- .../renderer_vulkan/vk_rasterizer.cpp | 2 ++ src/video_core/texture_cache/texture_cache.h | 12 ++++---- 11 files changed, 87 insertions(+), 71 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index 1f0f156ede..26b51f442b 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp @@ -28,12 +28,11 @@ bool ComputePipelineKey::operator==(const ComputePipelineKey& rhs) const noexcep } ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_, - BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::KeplerCompute& kepler_compute_, - ProgramManager& program_manager_, const Shader::Info& info_, - std::string code, std::vector code_v) - : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, gpu_memory{gpu_memory_}, - kepler_compute{kepler_compute_}, program_manager{program_manager_}, info{info_} { + BufferCache& buffer_cache_, ProgramManager& program_manager_, + const Shader::Info& info_, std::string code, + std::vector code_v) + : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, + program_manager{program_manager_}, info{info_} { switch (device.GetShaderBackend()) { case Settings::ShaderBackend::GLSL: source_program = CreateProgram(code, GL_COMPUTE_SHADER); @@ -86,7 +85,7 @@ void ComputePipeline::Configure() { GLsizei texture_binding{}; GLsizei image_binding{}; - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const auto& cbufs{qmd.const_buffer_config}; const bool via_header_index{qmd.linked_tsc != 0}; const auto read_handle{[&](const auto& desc, u32 index) { @@ -101,12 +100,12 @@ void ComputePipeline::Configure() { const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + secondary_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr)}; + const u32 rhs_raw{gpu_memory->Read(separate_addr)}; return TexturePair(lhs_raw | rhs_raw, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + return TexturePair(gpu_memory->Read(addr), via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) { for (u32 index = 0; index < desc.count; ++index) { diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h index 723f27f114..6534dec325 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.h +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h @@ -49,10 +49,8 @@ static_assert(std::is_trivially_constructible_v); class ComputePipeline { public: explicit ComputePipeline(const Device& device, TextureCache& texture_cache_, - BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::KeplerCompute& kepler_compute_, - ProgramManager& program_manager_, const Shader::Info& info_, - std::string code, std::vector code_v); + BufferCache& buffer_cache_, ProgramManager& program_manager_, + const Shader::Info& info_, std::string code, std::vector code_v); void Configure(); @@ -60,11 +58,17 @@ public: return writes_global_memory; } + void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, + Tegra::MemoryManager* gpu_memory_) { + kepler_compute = kepler_compute_; + gpu_memory = gpu_memory_; + } + private: TextureCache& texture_cache; BufferCache& buffer_cache; - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::KeplerCompute& kepler_compute; + Tegra::MemoryManager* gpu_memory; + Tegra::Engines::KeplerCompute* kepler_compute; ProgramManager& program_manager; Shader::Info info; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 67eae369dd..c877d77926 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -169,15 +169,15 @@ ConfigureFuncPtr ConfigureFunc(const std::array& infos, u32 ena } } // Anonymous namespace -GraphicsPipeline::GraphicsPipeline( - const Device& device, TextureCache& texture_cache_, BufferCache& buffer_cache_, - Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, - ProgramManager& program_manager_, StateTracker& state_tracker_, ShaderWorker* thread_worker, - VideoCore::ShaderNotify* shader_notify, std::array sources, - std::array, 5> sources_spirv, const std::array& infos, - const GraphicsPipelineKey& key_) - : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, - gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, program_manager{program_manager_}, +GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_cache_, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, ShaderWorker* thread_worker, + VideoCore::ShaderNotify* shader_notify, + std::array sources, + std::array, 5> sources_spirv, + const std::array& infos, + const GraphicsPipelineKey& key_) + : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{state_tracker_}, key{key_} { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -285,7 +285,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.runtime.SetBaseStorageBindings(base_storage_bindings); buffer_cache.runtime.SetEnableStorageBuffers(use_storage_buffers); - const auto& regs{maxwell3d.regs}; + const auto& regs{maxwell3d->regs}; const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const Shader::Info& info{stage_infos[stage]}; @@ -299,7 +299,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { ++ssbo_index; } } - const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; + const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers}; const auto read_handle{[&](const auto& desc, u32 index) { ASSERT(cbufs[desc.cbuf_index].enabled); const u32 index_offset{index << desc.size_shift}; @@ -312,13 +312,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + second_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr)}; + const u32 rhs_raw{gpu_memory->Read(separate_addr)}; const u32 raw{lhs_raw | rhs_raw}; return TexturePair(raw, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + return TexturePair(gpu_memory->Read(addr), via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE { for (u32 index = 0; index < desc.count; ++index) { diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 4ec15b9666..a0f0e63cba 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -71,10 +71,9 @@ static_assert(std::is_trivially_constructible_v); class GraphicsPipeline { public: explicit GraphicsPipeline(const Device& device, TextureCache& texture_cache_, - BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::Maxwell3D& maxwell3d_, - ProgramManager& program_manager_, StateTracker& state_tracker_, - ShaderWorker* thread_worker, VideoCore::ShaderNotify* shader_notify, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, ShaderWorker* thread_worker, + VideoCore::ShaderNotify* shader_notify, std::array sources, std::array, 5> sources_spirv, const std::array& infos, @@ -107,6 +106,11 @@ public: }; } + void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { + maxwell3d = maxwell3d_; + gpu_memory = gpu_memory_; + } + private: template void ConfigureImpl(bool is_indexed); @@ -119,8 +123,8 @@ private: TextureCache& texture_cache; BufferCache& buffer_cache; - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::Maxwell3D& maxwell3d; + Tegra::MemoryManager* gpu_memory; + Tegra::Engines::Maxwell3D* maxwell3d; ProgramManager& program_manager; StateTracker& state_tracker; const GraphicsPipelineKey key; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8cfa0013fb..4eb9bd1160 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -216,6 +216,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { return; } std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); SyncState(); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 494581d0d3..5a29a41d21 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -477,9 +477,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( previous_program = &program; } auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; - return std::make_unique( - device, texture_cache, buffer_cache, *gpu_memory, *maxwell3d, program_manager, - state_tracker, thread_worker, &shader_notify, sources, sources_spirv, infos, key); + return std::make_unique(device, texture_cache, buffer_cache, program_manager, + state_tracker, thread_worker, &shader_notify, sources, + sources_spirv, infos, key); } catch (Shader::Exception& exception) { LOG_ERROR(Render_OpenGL, "{}", exception.what()); @@ -533,9 +533,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline( break; } - return std::make_unique(device, texture_cache, buffer_cache, *gpu_memory, - *kepler_compute, program_manager, program.info, code, - code_spirv); + return std::make_unique(device, texture_cache, buffer_cache, program_manager, + program.info, code, code_spirv); } catch (Shader::Exception& exception) { LOG_ERROR(Render_OpenGL, "{}", exception.what()); return nullptr; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 5aca8f0385..1e993185f9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -215,15 +215,14 @@ ConfigureFuncPtr ConfigureFunc(const std::array& m } // Anonymous namespace GraphicsPipeline::GraphicsPipeline( - Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, Scheduler& scheduler_, - BufferCache& buffer_cache_, TextureCache& texture_cache_, + Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) - : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, - texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, + : key{key_}, device{device_}, texture_cache{texture_cache_}, + buffer_cache{buffer_cache_}, scheduler{scheduler_}, update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -288,7 +287,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes); - const auto& regs{maxwell3d.regs}; + const auto& regs{maxwell3d->regs}; const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const Shader::Info& info{stage_infos[stage]}; @@ -302,7 +301,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { ++ssbo_index; } } - const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; + const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers}; const auto read_handle{[&](const auto& desc, u32 index) { ASSERT(cbufs[desc.cbuf_index].enabled); const u32 index_offset{index << desc.size_shift}; @@ -315,13 +314,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + second_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr)}; + const u32 rhs_raw{gpu_memory->Read(separate_addr)}; const u32 raw{lhs_raw | rhs_raw}; return TexturePair(raw, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + return TexturePair(gpu_memory->Read(addr), via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE { for (u32 index = 0; index < desc.count; ++index) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index e8949a9ab0..85602592b3 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -69,15 +69,16 @@ class GraphicsPipeline { static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; public: - explicit GraphicsPipeline( - Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, - Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, - VideoCore::ShaderNotify* shader_notify, const Device& device, - DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, - Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, - RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, - std::array stages, - const std::array& infos); + explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache, + TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, + const Device& device, DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue, + Common::ThreadWorker* worker_thread, + PipelineStatistics* pipeline_statistics, + RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key, + std::array stages, + const std::array& infos); GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; @@ -109,6 +110,11 @@ public: return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl(is_indexed); }; } + void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { + maxwell3d = maxwell3d_; + gpu_memory = gpu_memory_; + } + private: template void ConfigureImpl(bool is_indexed); @@ -120,8 +126,8 @@ private: void Validate(); const GraphicsPipelineCacheKey key; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::MemoryManager& gpu_memory; + Tegra::Engines::Maxwell3D* maxwell3d; + Tegra::MemoryManager* gpu_memory; const Device& device; TextureCache& texture_cache; BufferCache& buffer_cache; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b1e0b96c49..732e7b6f24 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -555,10 +555,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( previous_stage = &program; } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique( - *maxwell3d, *gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, - descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, - std::move(modules), infos); + return std::make_unique(scheduler, buffer_cache, texture_cache, + &shader_notify, device, descriptor_pool, + update_descriptor_queue, thread_worker, statistics, + render_pass_cache, key, std::move(modules), infos); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bf750452f4..7e08055442 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -190,6 +190,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { return; } std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + // update engine as channel may be different. + pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); BeginTransformFeedback(); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 2731aead07..e8b0b0a3ba 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -885,12 +885,14 @@ void TextureCache

::InvalidateScale(Image& image) { } image.image_view_ids.clear(); image.image_view_infos.clear(); - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(state->graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(state->compute_image_view_ids, CORRUPT_ID); + for (auto& this_state : channel_storage) { + if constexpr (ENABLE_VALIDATION) { + std::ranges::fill(this_state.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(this_state.compute_image_view_ids, CORRUPT_ID); + } + this_state.graphics_image_table.Invalidate(); + this_state.compute_image_table.Invalidate(); } - state->graphics_image_table.Invalidate(); - state->compute_image_table.Invalidate(); has_deleted_images = true; } From b617874724c461cba270a00c0f8e67fc4a6d553a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 10 Nov 2021 17:37:17 +0100 Subject: [PATCH 056/245] Common: implement MultiLevelPageTable. --- src/common/CMakeLists.txt | 2 + src/common/multi_level_page_table.cpp | 7 +++ src/common/multi_level_page_table.h | 79 +++++++++++++++++++++++++ src/common/multi_level_page_table.inc | 83 +++++++++++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 src/common/multi_level_page_table.cpp create mode 100644 src/common/multi_level_page_table.h create mode 100644 src/common/multi_level_page_table.inc diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3447fabd8f..2db4148194 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -81,6 +81,8 @@ add_library(common STATIC microprofile.cpp microprofile.h microprofileui.h + multi_level_page_table.cpp + multi_level_page_table.h nvidia_flags.cpp nvidia_flags.h page_table.cpp diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp new file mode 100644 index 0000000000..561785ca74 --- /dev/null +++ b/src/common/multi_level_page_table.cpp @@ -0,0 +1,7 @@ +#include "common/multi_level_page_table.inc" + +namespace Common { +template class Common::MultiLevelPageTable; +template class Common::MultiLevelPageTable; +template class Common::MultiLevelPageTable; +} // namespace Common diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h new file mode 100644 index 0000000000..dde1cc962e --- /dev/null +++ b/src/common/multi_level_page_table.h @@ -0,0 +1,79 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { + +template +class MultiLevelPageTable final { +public: + constexpr MultiLevelPageTable() = default; + explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits, + std::size_t page_bits); + + ~MultiLevelPageTable() noexcept; + + MultiLevelPageTable(const MultiLevelPageTable&) = delete; + MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete; + + MultiLevelPageTable(MultiLevelPageTable&& other) noexcept + : address_space_bits{std::exchange(other.address_space_bits, 0)}, + first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange( + other.page_bits, 0)}, + first_level_shift{std::exchange(other.first_level_shift, 0)}, + first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)}, + first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr, + nullptr)} {} + + MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept { + address_space_bits = std::exchange(other.address_space_bits, 0); + first_level_bits = std::exchange(other.first_level_bits, 0); + page_bits = std::exchange(other.page_bits, 0); + first_level_shift = std::exchange(other.first_level_shift, 0); + first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0); + alloc_size = std::exchange(other.alloc_size, 0); + first_level_map = std::move(other.first_level_map); + base_ptr = std::exchange(other.base_ptr, nullptr); + return *this; + } + + void ReserveRange(u64 start, std::size_t size); + + [[nodiscard]] constexpr const BaseAddr& operator[](std::size_t index) const { + return base_ptr[index]; + } + + [[nodiscard]] constexpr BaseAddr& operator[](std::size_t index) { + return base_ptr[index]; + } + + [[nodiscard]] constexpr BaseAddr* data() { + return base_ptr; + } + + [[nodiscard]] constexpr const BaseAddr* data() const { + return base_ptr; + } + +private: + void AllocateLevel(u64 level); + + std::size_t address_space_bits{}; + std::size_t first_level_bits{}; + std::size_t page_bits{}; + std::size_t first_level_shift{}; + std::size_t first_level_chunk_size{}; + std::size_t alloc_size{}; + std::vector first_level_map{}; + BaseAddr* base_ptr{}; +}; + +} // namespace Common diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc new file mode 100644 index 0000000000..a75e61f9d7 --- /dev/null +++ b/src/common/multi_level_page_table.inc @@ -0,0 +1,83 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "common/assert.h" +#include "common/multi_level_page_table.h" + +namespace Common { + +template +MultiLevelPageTable::MultiLevelPageTable(std::size_t address_space_bits_, + std::size_t first_level_bits_, + std::size_t page_bits_) + : address_space_bits{address_space_bits_}, + first_level_bits{first_level_bits_}, page_bits{page_bits_} { + first_level_shift = address_space_bits - first_level_bits; + first_level_chunk_size = 1ULL << (first_level_shift - page_bits); + alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr); + std::size_t first_level_size = 1ULL << first_level_bits; + first_level_map.resize(first_level_size, nullptr); +#ifdef _WIN32 + void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)}; +#else + void* base{mmap(nullptr, alloc_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; + + if (base == MAP_FAILED) { + base = nullptr; + } +#endif + + ASSERT(base); + base_ptr = reinterpret_cast(base); +} + +template +MultiLevelPageTable::~MultiLevelPageTable() noexcept { + if (!base_ptr) { + return; + } +#ifdef _WIN32 + ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE)); +#else + ASSERT(munmap(base_ptr, alloc_size) == 0); +#endif +} + +template +void MultiLevelPageTable::ReserveRange(u64 start, std::size_t size) { + const u64 new_start = start >> first_level_shift; + const u64 new_end = + (start + size + (first_level_chunk_size << page_bits) - 1) >> first_level_shift; + for (u64 i = new_start; i <= new_end; i++) { + if (!first_level_map[i]) { + AllocateLevel(i); + } + } +} + +template +void MultiLevelPageTable::AllocateLevel(u64 level) { + void* ptr = reinterpret_cast(base_ptr) + level * first_level_chunk_size; +#ifdef _WIN32 + void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)}; +#else + void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)}; + + if (base == MAP_FAILED) { + base = nullptr; + } +#endif + ASSERT(base); + + first_level_map[level] = base; +} + +} // namespace Common From cbaf3fb433a351f7d9509f17f88d4896ba66afd1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 11 Nov 2021 21:24:40 +0100 Subject: [PATCH 057/245] VideoCore: Update MemoryManager --- src/common/multi_level_page_table.cpp | 1 + src/common/multi_level_page_table.inc | 7 +- src/video_core/memory_manager.cpp | 147 ++++++++++---------------- src/video_core/memory_manager.h | 98 +++++------------ 4 files changed, 86 insertions(+), 167 deletions(-) diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp index 561785ca74..aed04d0b59 100644 --- a/src/common/multi_level_page_table.cpp +++ b/src/common/multi_level_page_table.cpp @@ -4,4 +4,5 @@ namespace Common { template class Common::MultiLevelPageTable; template class Common::MultiLevelPageTable; template class Common::MultiLevelPageTable; +template class Common::MultiLevelPageTable; } // namespace Common diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc index a75e61f9d7..7fbcb908ac 100644 --- a/src/common/multi_level_page_table.inc +++ b/src/common/multi_level_page_table.inc @@ -20,7 +20,7 @@ MultiLevelPageTable::MultiLevelPageTable(std::size_t address_space_bit : address_space_bits{address_space_bits_}, first_level_bits{first_level_bits_}, page_bits{page_bits_} { first_level_shift = address_space_bits - first_level_bits; - first_level_chunk_size = 1ULL << (first_level_shift - page_bits); + first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr); alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr); std::size_t first_level_size = 1ULL << first_level_bits; first_level_map.resize(first_level_size, nullptr); @@ -53,8 +53,7 @@ MultiLevelPageTable::~MultiLevelPageTable() noexcept { template void MultiLevelPageTable::ReserveRange(u64 start, std::size_t size) { const u64 new_start = start >> first_level_shift; - const u64 new_end = - (start + size + (first_level_chunk_size << page_bits) - 1) >> first_level_shift; + const u64 new_end = (start + size) >> first_level_shift; for (u64 i = new_start; i <= new_end; i++) { if (!first_level_map[i]) { AllocateLevel(i); @@ -64,7 +63,7 @@ void MultiLevelPageTable::ReserveRange(u64 start, std::size_t size) { template void MultiLevelPageTable::AllocateLevel(u64 level) { - void* ptr = reinterpret_cast(base_ptr) + level * first_level_chunk_size; + void* ptr = reinterpret_cast(base_ptr) + level * first_level_chunk_size; #ifdef _WIN32 void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)}; #else diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index a3efd365e3..1e090279f0 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -16,36 +16,63 @@ namespace Tegra { -MemoryManager::MemoryManager(Core::System& system_) - : system{system_}, page_table(page_table_size) {} +MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 page_bits_) + : system{system_}, address_space_bits{address_space_bits_}, page_bits{page_bits_}, entries{}, + page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits} { + address_space_size = 1ULL << address_space_bits; + allocate_start = address_space_bits > 32 ? 1ULL << 32 : 0; + page_size = 1ULL << page_bits; + page_mask = page_size - 1ULL; + const u64 page_table_bits = address_space_bits - cpu_page_bits; + const u64 page_table_size = 1ULL << page_table_bits; + page_table_mask = page_table_size - 1; + + entries.resize(page_table_size / 32, 0); +} MemoryManager::~MemoryManager() = default; -void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { - rasterizer = rasterizer_; +MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const { + position = position >> page_bits; + const u64 entry_mask = entries[position / 32]; + const size_t sub_index = position % 32; + return static_cast((entry_mask >> (2 * sub_index)) & 0x03ULL); } -GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { +void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { + position = position >> page_bits; + const u64 entry_mask = entries[position / 32]; + const size_t sub_index = position % 32; + entries[position / 32] = + (~(3ULL << sub_index * 2) & entry_mask) | (static_cast(entry) << sub_index * 2); +} + +template +GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, + size_t size) { u64 remaining_size{size}; + if constexpr (entry_type == EntryType::Mapped) { + page_table.ReserveRange(gpu_addr, size); + } for (u64 offset{}; offset < size; offset += page_size) { - if (remaining_size < page_size) { - SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size); - } else { - SetPageEntry(gpu_addr + offset, page_entry + offset); + const GPUVAddr current_gpu_addr = gpu_addr + offset; + SetEntry(current_gpu_addr, entry_type); + if constexpr (entry_type == EntryType::Mapped) { + const VAddr current_cpu_addr = cpu_addr + offset; + const auto index = PageEntryIndex(current_gpu_addr); + page_table[index] = static_cast(current_cpu_addr >> 12ULL); } remaining_size -= page_size; } return gpu_addr; } +void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + rasterizer = rasterizer_; +} + GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { - const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); - if (it != map_ranges.end() && it->first == gpu_addr) { - it->second = size; - } else { - map_ranges.insert(it, MapRange{gpu_addr, size}); - } - return UpdateRange(gpu_addr, cpu_addr, size); + return PageTableOp(gpu_addr, cpu_addr, size); } GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) { @@ -62,13 +89,6 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { if (size == 0) { return; } - const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); - if (it != map_ranges.end()) { - ASSERT(it->first == gpu_addr); - map_ranges.erase(it); - } else { - ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr); - } const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); for (const auto& [map_addr, map_size] : submapped_ranges) { @@ -79,63 +99,23 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { rasterizer->UnmapMemory(*cpu_addr, map_size); } - UpdateRange(gpu_addr, PageEntry::State::Unmapped, size); + PageTableOp(gpu_addr, 0, size); } std::optional MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { for (u64 offset{}; offset < size; offset += page_size) { - if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) { + if (GetEntry(gpu_addr + offset) != EntryType::Free) { return std::nullopt; } } - return UpdateRange(gpu_addr, PageEntry::State::Allocated, size); + return PageTableOp(gpu_addr, 0, size); } GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) { return *AllocateFixed(*FindFreeRange(size, align), size); } -void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) { - if (!page_entry.IsValid()) { - return; - } - - ASSERT(system.CurrentProcess() - ->PageTable() - .LockForDeviceAddressSpace(page_entry.ToAddress(), size) - .IsSuccess()); -} - -void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) { - if (!page_entry.IsValid()) { - return; - } - - ASSERT(system.CurrentProcess() - ->PageTable() - .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size) - .IsSuccess()); -} - -PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const { - return page_table[PageEntryIndex(gpu_addr)]; -} - -void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { - // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to - // improper tracking, but should be fixed in the future. - - //// Unlock the old page - // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size); - - //// Lock the new page - // TryLockPage(page_entry, size); - auto& current_page = page_table[PageEntryIndex(gpu_addr)]; - - current_page = page_entry; -} - std::optional MemoryManager::FindFreeRange(std::size_t size, std::size_t align, bool start_32bit_address) const { if (!align) { @@ -145,9 +125,9 @@ std::optional MemoryManager::FindFreeRange(std::size_t size, std::size } u64 available_size{}; - GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start}; + GPUVAddr gpu_addr{allocate_start}; while (gpu_addr + available_size < address_space_size) { - if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) { + if (GetEntry(gpu_addr + available_size) == EntryType::Free) { available_size += page_size; if (available_size >= size) { @@ -168,15 +148,12 @@ std::optional MemoryManager::FindFreeRange(std::size_t size, std::size } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { - if (gpu_addr == 0) { - return std::nullopt; - } - const auto page_entry{GetPageEntry(gpu_addr)}; - if (!page_entry.IsValid()) { + if (GetEntry(gpu_addr) != EntryType::Mapped) { return std::nullopt; } - return page_entry.ToAddress() + (gpu_addr & page_mask); + const VAddr cpu_addr_base = static_cast(page_table[PageEntryIndex(gpu_addr)]) << 12ULL; + return cpu_addr_base + (gpu_addr & page_mask); } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const { @@ -227,10 +204,6 @@ template void MemoryManager::Write(GPUVAddr addr, u32 data); template void MemoryManager::Write(GPUVAddr addr, u64 data); u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) { - if (!GetPageEntry(gpu_addr).IsValid()) { - return {}; - } - const auto address{GpuToCpuAddress(gpu_addr)}; if (!address) { return {}; @@ -240,10 +213,6 @@ u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) { } const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { - if (!GetPageEntry(gpu_addr).IsValid()) { - return {}; - } - const auto address{GpuToCpuAddress(gpu_addr)}; if (!address) { return {}; @@ -252,12 +221,6 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { return system.Memory().GetPointer(*address); } -size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept { - auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first); - --it; - return it->second - (gpu_addr - it->first); -} - void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, bool is_safe) const { std::size_t remaining_size{size}; @@ -268,7 +231,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std: const std::size_t copy_amount{ std::min(static_cast(page_size) - page_offset, remaining_size)}; const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr && *page_addr != 0) { + if (page_addr) { const auto src_addr{*page_addr + page_offset}; if (is_safe) { // Flush must happen on the rasterizer interface, such that memory is always @@ -307,7 +270,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe const std::size_t copy_amount{ std::min(static_cast(page_size) - page_offset, remaining_size)}; const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr && *page_addr != 0) { + if (page_addr) { const auto dest_addr{*page_addr + page_offset}; if (is_safe) { @@ -392,7 +355,7 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons size_t page_index{gpu_addr >> page_bits}; const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; while (page_index < page_last) { - if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) { + if (GetEntry(page_index << page_bits) == EntryType::Free) { return false; } ++page_index; @@ -408,7 +371,7 @@ std::vector> MemoryManager::GetSubmappedRange( size_t page_offset{gpu_addr & page_mask}; std::optional> last_segment{}; std::optional old_page_addr{}; - const auto extend_size = [&last_segment, &page_index, &page_offset](std::size_t bytes) { + const auto extend_size = [this, &last_segment, &page_index, &page_offset](std::size_t bytes) { if (!last_segment) { const GPUVAddr new_base_addr = (page_index << page_bits) + page_offset; last_segment = {new_base_addr, bytes}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 74f9ce1754..0a763fd198 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -8,6 +8,7 @@ #include #include "common/common_types.h" +#include "common/multi_level_page_table.h" namespace VideoCore { class RasterizerInterface; @@ -19,55 +20,10 @@ class System; namespace Tegra { -class PageEntry final { -public: - enum class State : u32 { - Unmapped = static_cast(-1), - Allocated = static_cast(-2), - }; - - constexpr PageEntry() = default; - constexpr PageEntry(State state_) : state{state_} {} - constexpr PageEntry(VAddr addr) : state{static_cast(addr >> ShiftBits)} {} - - [[nodiscard]] constexpr bool IsUnmapped() const { - return state == State::Unmapped; - } - - [[nodiscard]] constexpr bool IsAllocated() const { - return state == State::Allocated; - } - - [[nodiscard]] constexpr bool IsValid() const { - return !IsUnmapped() && !IsAllocated(); - } - - [[nodiscard]] constexpr VAddr ToAddress() const { - if (!IsValid()) { - return {}; - } - - return static_cast(state) << ShiftBits; - } - - [[nodiscard]] constexpr PageEntry operator+(u64 offset) const { - // If this is a reserved value, offsets do not apply - if (!IsValid()) { - return *this; - } - return PageEntry{(static_cast(state) << ShiftBits) + offset}; - } - -private: - static constexpr std::size_t ShiftBits{12}; - - State state{State::Unmapped}; -}; -static_assert(sizeof(PageEntry) == 4, "PageEntry is too large"); - class MemoryManager final { public: - explicit MemoryManager(Core::System& system_); + explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40, + u64 page_bits_ = 16); ~MemoryManager(); /// Binds a renderer to the memory manager. @@ -86,9 +42,6 @@ public: [[nodiscard]] u8* GetPointer(GPUVAddr addr); [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; - /// Returns the number of bytes until the end of the memory map containing the given GPU address - [[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept; - /** * ReadBlock and WriteBlock are full read and write operations over virtual * GPU Memory. It's important to use these when GPU memory may not be continuous @@ -145,44 +98,47 @@ public: void FlushRegion(GPUVAddr gpu_addr, size_t size) const; private: - [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; - void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); - GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); [[nodiscard]] std::optional FindFreeRange(std::size_t size, std::size_t align, bool start_32bit_address = false) const; - void TryLockPage(PageEntry page_entry, std::size_t size); - void TryUnlockPage(PageEntry page_entry, std::size_t size); - void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, bool is_safe) const; void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, bool is_safe); - [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { + [[nodiscard]] inline std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { return (gpu_addr >> page_bits) & page_table_mask; } - static constexpr u64 address_space_size = 1ULL << 40; - static constexpr u64 address_space_start = 1ULL << 32; - static constexpr u64 address_space_start_low = 1ULL << 16; - static constexpr u64 page_bits{16}; - static constexpr u64 page_size{1 << page_bits}; - static constexpr u64 page_mask{page_size - 1}; - static constexpr u64 page_table_bits{24}; - static constexpr u64 page_table_size{1 << page_table_bits}; - static constexpr u64 page_table_mask{page_table_size - 1}; - Core::System& system; + const u64 address_space_bits; + const u64 page_bits; + u64 address_space_size; + u64 allocate_start; + u64 page_size; + u64 page_mask; + u64 page_table_mask; + static constexpr u64 cpu_page_bits{12}; + VideoCore::RasterizerInterface* rasterizer = nullptr; - std::vector page_table; + enum class EntryType : u64 { + Free = 0, + Reserved = 1, + Mapped = 2, + }; - using MapRange = std::pair; - std::vector map_ranges; + std::vector entries; - std::vector> cache_invalidate_queue; + template + GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size); + + EntryType GetEntry(size_t position) const; + + void SetEntry(size_t position, EntryType entry); + + Common::MultiLevelPageTable page_table; }; } // namespace Tegra From 835b950f7ec300920b920b9b45f82ceaa4b33813 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 13 Nov 2021 02:29:54 +0100 Subject: [PATCH 058/245] NvHostCtrl: Fix merge of nvflinger. --- src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 9bd10257bb..594d206007 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -180,7 +180,8 @@ private: struct IocCtrlEventUnregisterBatchParams { u64_le user_events{}; }; - static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, "IocCtrlEventKill is incorrect size"); + static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, + "IocCtrlEventKill is incorrect size"); NvResult NvOsGetConfigU32(const std::vector& input, std::vector& output); NvResult IocCtrlEventWait(const std::vector& input, std::vector& output, From 0f4ae3cc52a740bac836f8f8c2ff5d560b8ca46e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 13 Nov 2021 03:05:32 +0100 Subject: [PATCH 059/245] MemoryManager: Temporary Fix for NVDEC. --- src/video_core/memory_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 1e090279f0..9e946d448a 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -125,7 +125,7 @@ std::optional MemoryManager::FindFreeRange(std::size_t size, std::size } u64 available_size{}; - GPUVAddr gpu_addr{allocate_start}; + GPUVAddr gpu_addr{start_32bit_address ? 0 : allocate_start}; while (gpu_addr + available_size < address_space_size) { if (GetEntry(gpu_addr + available_size) == EntryType::Free) { available_size += page_size; From c6ea0c650e38b7c475fea221b9655c754f7aaccc Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 13 Nov 2021 03:25:35 +0100 Subject: [PATCH 060/245] NVDRV: Update copyright notices. --- src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | 6 ++++-- src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | 6 ++++-- src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 2 +- src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | 6 ++++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9946ce6246..5c70c9a57b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #include #include diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 4ecae3caf6..f5fb33ba7d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 0e22d92730..a859a7abdc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors // (https://github.com/skyline-emu/) // SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 // or any later version Refer to the license.txt file included. diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 594d206007..d56aea4059 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once From feb49c822d9cabc5bc7be9eab1f2bf4ba460176a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 14 Nov 2021 20:55:52 +0100 Subject: [PATCH 061/245] NVDRV: Remake ASGPU --- src/common/CMakeLists.txt | 2 + src/common/address_space.cpp | 11 + src/common/address_space.h | 134 +++++ src/common/address_space.inc | 338 +++++++++++++ .../service/nvdrv/devices/nvhost_as_gpu.cpp | 460 ++++++++++++------ .../hle/service/nvdrv/devices/nvhost_as_gpu.h | 163 ++++--- src/video_core/memory_manager.cpp | 10 +- src/video_core/memory_manager.h | 3 +- 8 files changed, 882 insertions(+), 239 deletions(-) create mode 100644 src/common/address_space.cpp create mode 100644 src/common/address_space.h create mode 100644 src/common/address_space.inc diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2db4148194..a026968734 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -17,6 +17,8 @@ endif () include(GenerateSCMRev) add_library(common STATIC + address_space.cpp + address_space.h algorithm.h alignment.h announce_multiplayer_room.h diff --git a/src/common/address_space.cpp b/src/common/address_space.cpp new file mode 100644 index 0000000000..6db85be87b --- /dev/null +++ b/src/common/address_space.cpp @@ -0,0 +1,11 @@ +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#include "common/address_space.inc" + +namespace Common { + +template class Common::FlatAllocator; + +} diff --git a/src/common/address_space.h b/src/common/address_space.h new file mode 100644 index 0000000000..fd2f32b7d3 --- /dev/null +++ b/src/common/address_space.h @@ -0,0 +1,134 @@ +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { +template +concept AddressSpaceValid = std::is_unsigned_v && sizeof(VaType) * 8 >= AddressSpaceBits; + +struct EmptyStruct {}; + +/** + * @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector + */ +template +requires AddressSpaceValid class FlatAddressSpaceMap { +private: + std::function + unmapCallback{}; //!< Callback called when the mappings in an region have changed + +protected: + /** + * @brief Represents a block of memory in the AS, the physical mapping is contiguous until + * another block with a different phys address is hit + */ + struct Block { + VaType virt{UnmappedVa}; //!< VA of the block + PaType phys{UnmappedPa}; //!< PA of the block, will increase 1-1 with VA until a new block + //!< is encountered + [[no_unique_address]] ExtraBlockInfo extraInfo; + + Block() = default; + + Block(VaType virt, PaType phys, ExtraBlockInfo extraInfo) + : virt(virt), phys(phys), extraInfo(extraInfo) {} + + constexpr bool Valid() { + return virt != UnmappedVa; + } + + constexpr bool Mapped() { + return phys != UnmappedPa; + } + + constexpr bool Unmapped() { + return phys == UnmappedPa; + } + + bool operator<(const VaType& pVirt) const { + return virt < pVirt; + } + }; + + std::mutex blockMutex; + std::vector blocks{Block{}}; + + /** + * @brief Maps a PA range into the given AS region + * @note blockMutex MUST be locked when calling this + */ + void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo); + + /** + * @brief Unmaps the given range and merges it with other unmapped regions + * @note blockMutex MUST be locked when calling this + */ + void UnmapLocked(VaType virt, VaType size); + +public: + static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) + + ((1ULL << (AddressSpaceBits - 1)) - + 1)}; //!< The maximum VA that this AS can technically reach + + VaType vaLimit{VaMaximum}; //!< A soft limit on the maximum VA of the AS + + FlatAddressSpaceMap(VaType vaLimit, std::function unmapCallback = {}); + + FlatAddressSpaceMap() = default; + + void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo = {}) { + std::scoped_lock lock(blockMutex); + MapLocked(virt, phys, size, extraInfo); + } + + void Unmap(VaType virt, VaType size) { + std::scoped_lock lock(blockMutex); + UnmapLocked(virt, size); + } +}; + +/** + * @brief FlatMemoryManager specialises FlatAddressSpaceMap to work as an allocator, with an + * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block + */ +template +requires AddressSpaceValid class FlatAllocator + : public FlatAddressSpaceMap { +private: + using Base = FlatAddressSpaceMap; + + VaType currentLinearAllocEnd; //!< The end address for the initial linear allocation pass, once + //!< this reaches the AS limit the slower allocation path will be + //!< used + +public: + VaType vaStart; //!< The base VA of the allocator, no allocations will be below this + + FlatAllocator(VaType vaStart, VaType vaLimit = Base::VaMaximum); + + /** + * @brief Allocates a region in the AS of the given size and returns its address + */ + VaType Allocate(VaType size); + + /** + * @brief Marks the given region in the AS as allocated + */ + void AllocateFixed(VaType virt, VaType size); + + /** + * @brief Frees an AS region so it can be used again + */ + void Free(VaType virt, VaType size); +}; +} // namespace Common diff --git a/src/common/address_space.inc b/src/common/address_space.inc new file mode 100644 index 0000000000..907c55d88e --- /dev/null +++ b/src/common/address_space.inc @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPLv3 or later +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "common/address_space.h" +#include "common/assert.h" + +#define MAP_MEMBER(returnType) \ + template \ + requires AddressSpaceValid returnType FlatAddressSpaceMap< \ + VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo> +#define MAP_MEMBER_CONST() \ + template \ + requires AddressSpaceValid FlatAddressSpaceMap< \ + VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo> + +#define MM_MEMBER(returnType) \ + template \ + requires AddressSpaceValid returnType \ + FlatMemoryManager + +#define ALLOC_MEMBER(returnType) \ + template \ + requires AddressSpaceValid returnType \ + FlatAllocator +#define ALLOC_MEMBER_CONST() \ + template \ + requires AddressSpaceValid \ + FlatAllocator + +namespace Common { +MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType vaLimit, + std::function unmapCallback) + : unmapCallback(std::move(unmapCallback)), vaLimit(vaLimit) { + if (vaLimit > VaMaximum) + UNREACHABLE_MSG("Invalid VA limit!"); +} + +MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo) { + VaType virtEnd{virt + size}; + + if (virtEnd > vaLimit) + UNREACHABLE_MSG("Trying to map a block past the VA limit: virtEnd: 0x{:X}, vaLimit: 0x{:X}", + virtEnd, vaLimit); + + auto blockEndSuccessor{std::lower_bound(blocks.begin(), blocks.end(), virtEnd)}; + if (blockEndSuccessor == blocks.begin()) + UNREACHABLE_MSG("Trying to map a block before the VA start: virtEnd: 0x{:X}", virtEnd); + + auto blockEndPredecessor{std::prev(blockEndSuccessor)}; + + if (blockEndSuccessor != blocks.end()) { + // We have blocks in front of us, if one is directly in front then we don't have to add a + // tail + if (blockEndSuccessor->virt != virtEnd) { + PaType tailPhys{[&]() -> PaType { + if constexpr (!PaContigSplit) { + return blockEndPredecessor + ->phys; // Always propagate unmapped regions rather than calculating offset + } else { + if (blockEndPredecessor->Unmapped()) + return blockEndPredecessor->phys; // Always propagate unmapped regions + // rather than calculating offset + else + return blockEndPredecessor->phys + virtEnd - blockEndPredecessor->virt; + } + }()}; + + if (blockEndPredecessor->virt >= virt) { + // If this block's start would be overlapped by the map then reuse it as a tail + // block + blockEndPredecessor->virt = virtEnd; + blockEndPredecessor->phys = tailPhys; + blockEndPredecessor->extraInfo = blockEndPredecessor->extraInfo; + + // No longer predecessor anymore + blockEndSuccessor = blockEndPredecessor--; + } else { + // Else insert a new one and we're done + blocks.insert(blockEndSuccessor, + {Block(virt, phys, extraInfo), + Block(virtEnd, tailPhys, blockEndPredecessor->extraInfo)}); + if (unmapCallback) + unmapCallback(virt, size); + + return; + } + } + } else { + // blockEndPredecessor will always be unmapped as blocks has to be terminated by an unmapped + // chunk + if (blockEndPredecessor != blocks.begin() && blockEndPredecessor->virt >= virt) { + // Move the unmapped block start backwards + blockEndPredecessor->virt = virtEnd; + + // No longer predecessor anymore + blockEndSuccessor = blockEndPredecessor--; + } else { + // Else insert a new one and we're done + blocks.insert(blockEndSuccessor, + {Block(virt, phys, extraInfo), Block(virtEnd, UnmappedPa, {})}); + if (unmapCallback) + unmapCallback(virt, size); + + return; + } + } + + auto blockStartSuccessor{blockEndSuccessor}; + + // Walk the block vector to find the start successor as this is more efficient than another + // binary search in most scenarios + while (std::prev(blockStartSuccessor)->virt >= virt) + blockStartSuccessor--; + + // Check that the start successor is either the end block or something in between + if (blockStartSuccessor->virt > virtEnd) { + UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", blockStartSuccessor->virt); + } else if (blockStartSuccessor->virt == virtEnd) { + // We need to create a new block as there are none spare that we would overwrite + blocks.insert(blockStartSuccessor, Block(virt, phys, extraInfo)); + } else { + // Erase overwritten blocks + if (auto eraseStart{std::next(blockStartSuccessor)}; eraseStart != blockEndSuccessor) + blocks.erase(eraseStart, blockEndSuccessor); + + // Reuse a block that would otherwise be overwritten as a start block + blockStartSuccessor->virt = virt; + blockStartSuccessor->phys = phys; + blockStartSuccessor->extraInfo = extraInfo; + } + + if (unmapCallback) + unmapCallback(virt, size); +} + +MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { + VaType virtEnd{virt + size}; + + if (virtEnd > vaLimit) + UNREACHABLE_MSG("Trying to map a block past the VA limit: virtEnd: 0x{:X}, vaLimit: 0x{:X}", + virtEnd, vaLimit); + + auto blockEndSuccessor{std::lower_bound(blocks.begin(), blocks.end(), virtEnd)}; + if (blockEndSuccessor == blocks.begin()) + UNREACHABLE_MSG("Trying to unmap a block before the VA start: virtEnd: 0x{:X}", virtEnd); + + auto blockEndPredecessor{std::prev(blockEndSuccessor)}; + + auto walkBackToPredecessor{[&](auto iter) { + while (iter->virt >= virt) + iter--; + + return iter; + }}; + + auto eraseBlocksWithEndUnmapped{[&](auto unmappedEnd) { + auto blockStartPredecessor{walkBackToPredecessor(unmappedEnd)}; + auto blockStartSuccessor{std::next(blockStartPredecessor)}; + + auto eraseEnd{[&]() { + if (blockStartPredecessor->Unmapped()) { + // If the start predecessor is unmapped then we can erase everything in our region + // and be done + return std::next(unmappedEnd); + } else { + // Else reuse the end predecessor as the start of our unmapped region then erase all + // up to it + unmappedEnd->virt = virt; + return unmappedEnd; + } + }()}; + + // We can't have two unmapped regions after each other + if (eraseEnd != blocks.end() && + (eraseEnd == blockStartSuccessor || + (blockStartPredecessor->Unmapped() && eraseEnd->Unmapped()))) + UNREACHABLE_MSG("Multiple contiguous unmapped regions are unsupported!"); + + blocks.erase(blockStartSuccessor, eraseEnd); + }}; + + // We can avoid any splitting logic if these are the case + if (blockEndPredecessor->Unmapped()) { + if (blockEndPredecessor->virt > virt) + eraseBlocksWithEndUnmapped(blockEndPredecessor); + + if (unmapCallback) + unmapCallback(virt, size); + + return; // The region is unmapped, bail out early + } else if (blockEndSuccessor->virt == virtEnd && blockEndSuccessor->Unmapped()) { + eraseBlocksWithEndUnmapped(blockEndSuccessor); + + if (unmapCallback) + unmapCallback(virt, size); + + return; // The region is unmapped here and doesn't need splitting, bail out early + } else if (blockEndSuccessor == blocks.end()) { + // This should never happen as the end should always follow an unmapped block + UNREACHABLE_MSG("Unexpected Memory Manager state!"); + } else if (blockEndSuccessor->virt != virtEnd) { + // If one block is directly in front then we don't have to add a tail + + // The previous block is mapped so we will need to add a tail with an offset + PaType tailPhys{[&]() { + if constexpr (PaContigSplit) + return blockEndPredecessor->phys + virtEnd - blockEndPredecessor->virt; + else + return blockEndPredecessor->phys; + }()}; + + if (blockEndPredecessor->virt >= virt) { + // If this block's start would be overlapped by the unmap then reuse it as a tail block + blockEndPredecessor->virt = virtEnd; + blockEndPredecessor->phys = tailPhys; + + // No longer predecessor anymore + blockEndSuccessor = blockEndPredecessor--; + } else { + blocks.insert(blockEndSuccessor, + {Block(virt, UnmappedPa, {}), + Block(virtEnd, tailPhys, blockEndPredecessor->extraInfo)}); + if (unmapCallback) + unmapCallback(virt, size); + + return; // The previous block is mapped and ends before + } + } + + // Walk the block vector to find the start predecessor as this is more efficient than another + // binary search in most scenarios + auto blockStartPredecessor{walkBackToPredecessor(blockEndSuccessor)}; + auto blockStartSuccessor{std::next(blockStartPredecessor)}; + + if (blockStartSuccessor->virt > virtEnd) { + UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", blockStartSuccessor->virt); + } else if (blockStartSuccessor->virt == virtEnd) { + // There are no blocks between the start and the end that would let us skip inserting a new + // one for head + + // The previous block is may be unmapped, if so we don't need to insert any unmaps after it + if (blockStartPredecessor->Mapped()) + blocks.insert(blockStartSuccessor, Block(virt, UnmappedPa, {})); + } else if (blockStartPredecessor->Unmapped()) { + // If the previous block is unmapped + blocks.erase(blockStartSuccessor, blockEndPredecessor); + } else { + // Erase overwritten blocks, skipping the first one as we have written the unmapped start + // block there + if (auto eraseStart{std::next(blockStartSuccessor)}; eraseStart != blockEndSuccessor) + blocks.erase(eraseStart, blockEndSuccessor); + + // Add in the unmapped block header + blockStartSuccessor->virt = virt; + blockStartSuccessor->phys = UnmappedPa; + } + + if (unmapCallback) + unmapCallback(virt, size); +} + +ALLOC_MEMBER_CONST()::FlatAllocator(VaType vaStart, VaType vaLimit) + : Base(vaLimit), currentLinearAllocEnd(vaStart), vaStart(vaStart) {} + +ALLOC_MEMBER(VaType)::Allocate(VaType size) { + std::scoped_lock lock(this->blockMutex); + + VaType allocStart{UnmappedVa}; + VaType allocEnd{currentLinearAllocEnd + size}; + + // Avoid searching backwards in the address space if possible + if (allocEnd >= currentLinearAllocEnd && allocEnd <= this->vaLimit) { + auto allocEndSuccessor{ + std::lower_bound(this->blocks.begin(), this->blocks.end(), allocEnd)}; + if (allocEndSuccessor == this->blocks.begin()) + UNREACHABLE_MSG("First block in AS map is invalid!"); + + auto allocEndPredecessor{std::prev(allocEndSuccessor)}; + if (allocEndPredecessor->virt <= currentLinearAllocEnd) { + allocStart = currentLinearAllocEnd; + } else { + // Skip over fixed any mappings in front of us + while (allocEndSuccessor != this->blocks.end()) { + if (allocEndSuccessor->virt - allocEndPredecessor->virt < size || + allocEndPredecessor->Mapped()) { + allocStart = allocEndPredecessor->virt; + break; + } + + allocEndPredecessor = allocEndSuccessor++; + + // Use the VA limit to calculate if we can fit in the final block since it has no + // successor + if (allocEndSuccessor == this->blocks.end()) { + allocEnd = allocEndPredecessor->virt + size; + + if (allocEnd >= allocEndPredecessor->virt && allocEnd <= this->vaLimit) + allocStart = allocEndPredecessor->virt; + } + } + } + } + + if (allocStart != UnmappedVa) { + currentLinearAllocEnd = allocStart + size; + } else { // If linear allocation overflows the AS then find a gap + if (this->blocks.size() <= 2) + UNREACHABLE_MSG("Unexpected allocator state!"); + + auto searchPredecessor{this->blocks.begin()}; + auto searchSuccessor{std::next(searchPredecessor)}; + + while (searchSuccessor != this->blocks.end() && + (searchSuccessor->virt - searchPredecessor->virt < size || + searchPredecessor->Mapped())) { + searchPredecessor = searchSuccessor++; + } + + if (searchSuccessor != this->blocks.end()) + allocStart = searchPredecessor->virt; + else + return {}; // AS is full + } + + this->MapLocked(allocStart, true, size, {}); + return allocStart; +} + +ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) { + this->Map(virt, true, size); +} + +ALLOC_MEMBER(void)::Free(VaType virt, VaType size) { + this->Unmap(virt, size); +} +} // namespace Common diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 5c70c9a57b..344ddfc900 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -6,6 +6,7 @@ #include #include +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -21,8 +22,8 @@ namespace Service::Nvidia::Devices { nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) - : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, - gmmu{std::make_shared(system)} {} + : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, + gmmu{} {} nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -89,12 +90,49 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& IoctlAllocAsEx params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); - if (params.big_page_size == 0) { - params.big_page_size = DEFAULT_BIG_PAGE_SIZE; + LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); + + std::scoped_lock lock(mutex); + + if (vm.initialised) { + UNREACHABLE_MSG("Cannot initialise an address space twice!"); + return NvResult::InvalidState; } - big_page_size = params.big_page_size; + if (params.big_page_size) { + if (!std::has_single_bit(params.big_page_size)) { + LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + if (!(params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES)) { + LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + vm.big_page_size = params.big_page_size; + vm.big_page_size_bits = static_cast(std::countr_zero(params.big_page_size)); + + vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT; + } + + // If this is unspecified then default values should be used + if (params.va_range_start) { + vm.va_range_start = params.va_range_start; + vm.va_range_split = params.va_range_split; + vm.va_range_end = params.va_range_end; + } + + const u64 start_pages{vm.va_range_start >> VM::PAGE_SIZE_BITS}; + const u64 end_pages{vm.va_range_split >> VM::PAGE_SIZE_BITS}; + vm.small_page_allocator = std::make_shared(start_pages, end_pages); + + const u64 start_big_pages{vm.va_range_split >> vm.big_page_size_bits}; + const u64 end_big_pages{(vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits}; + vm.big_page_allocator = std::make_unique(start_big_pages, end_big_pages); + + gmmu = std::make_shared(system, 40, VM::PAGE_SIZE_BITS); + vm.initialised = true; return NvResult::Success; } @@ -106,21 +144,73 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector& input, std::vector< LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - const auto size{static_cast(params.pages) * static_cast(params.page_size)}; - if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { - params.offset = *(gmmu->AllocateFixed(params.offset, size)); - } else { - params.offset = gmmu->Allocate(size, params.align); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; } - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); - result = NvResult::InsufficientMemory; + if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) { + return NvResult::BadValue; } + if (params.page_size != vm.big_page_size && + ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) { + UNIMPLEMENTED_MSG("Sparse small pages are not implemented!"); + return NvResult::NotImplemented; + } + + const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + allocator.AllocateFixed(static_cast(params.offset >> page_size_bits), params.pages); + } else { + params.offset = static_cast(allocator.Allocate(params.pages)) << page_size_bits; + if (!params.offset) { + UNREACHABLE_MSG("Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } + } + + u64 size{static_cast(params.pages) * params.page_size}; + + if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) { + gmmu->MapSparse(params.offset, size); + } + + allocation_map[params.offset] = { + .size = size, + .page_size = params.page_size, + .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, + }; + std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; +} + +void nvhost_as_gpu::FreeMappingLocked(u64 offset) { + auto mapping{mapping_map.at(offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast(mapping->offset >> page_size_bits), + static_cast(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) + gmmu->MapSparse(offset, mapping->size); + else + gmmu->Unmap(offset, mapping->size); + + mapping_map.erase(offset); } NvResult nvhost_as_gpu::FreeSpace(const std::vector& input, std::vector& output) { @@ -130,7 +220,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector& input, std::vector& LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); - gmmu->Unmap(params.offset, static_cast(params.pages) * params.page_size); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto allocation{allocation_map[params.offset]}; + + if (allocation.page_size != params.page_size || + allocation.size != (static_cast(params.pages) * params.page_size)) { + return NvResult::BadValue; + } + + for (const auto& mapping : allocation.mappings) { + FreeMappingLocked(mapping->offset); + } + + // Unset sparse flag if required + if (allocation.sparse) { + gmmu->Unmap(params.offset, allocation.size); + } + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + allocator.Free(static_cast(params.offset >> page_size_bits), + static_cast(allocation.size >> page_size_bits)); + allocation_map.erase(params.offset); + } catch ([[maybe_unused]] const std::out_of_range& e) { + return NvResult::BadValue; + } std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -141,43 +264,51 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); - auto result = NvResult::Success; std::vector entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + for (const auto& entry : entries) { - LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", - entry.offset, entry.nvmap_handle, entry.pages); + GPUVAddr virtual_address{static_cast(entry.as_offset_big_pages) + << vm.big_page_size_bits}; + u64 size{static_cast(entry.big_pages) << vm.big_page_size_bits}; - if (entry.nvmap_handle == 0) { - // If nvmap handle is null, we should unmap instead. - const auto offset{static_cast(entry.offset) << 0x10}; - const auto size{static_cast(entry.pages) << 0x10}; - gmmu->Unmap(offset, size); - continue; + auto alloc{allocation_map.upper_bound(virtual_address)}; + + if (alloc-- == allocation_map.begin() || + (virtual_address - alloc->first) + size > alloc->second.size) { + LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!"); + return NvResult::BadValue; } - const auto object{nvmap.GetHandle(entry.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); - result = NvResult::InvalidState; - break; + if (!alloc->second.sparse) { + LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!"); + return NvResult::BadValue; } - const auto offset{static_cast(entry.offset) << 0x10}; - const auto size{static_cast(entry.pages) << 0x10}; - const auto map_offset{static_cast(entry.map_offset) << 0x10}; - const auto addr{gmmu->Map(object->address + map_offset, offset, size)}; + if (!entry.handle) { + gmmu->MapSparse(virtual_address, size); + } else { + auto handle{nvmap.GetHandle(entry.handle)}; + if (!handle) { + return NvResult::BadValue; + } - if (!addr) { - LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); - result = NvResult::InvalidState; - break; + VAddr cpu_address{static_cast( + handle->address + + (static_cast(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; + + gmmu->Map(virtual_address, cpu_address, size); } } std::memcpy(output.data(), entries.data(), output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector& output) { @@ -187,75 +318,96 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector(buffer_map->CpuAddr() + params.buffer_offset)}; - const auto gpu_addr{static_cast(params.offset + params.buffer_offset)}; + std::scoped_lock lock(mutex); - if (!gmmu->Map(cpu_addr, gpu_addr, params.mapping_size)) { - LOG_CRITICAL(Service_NVDRV, - "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " - "mapping_size = {}, offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, - params.mapping_size, params.offset); + if (!vm.initialised) { + return NvResult::BadValue; + } - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + // Remaps a subregion of an existing mapping to a different PA + if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { + try { + auto mapping{mapping_map.at(params.offset)}; + + if (mapping->size < params.mapping_size) { + LOG_WARNING(Service_NVDRV, + "Cannot remap a partially mapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::Success; - } else { - LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); + u64 gpu_address{static_cast(params.offset + params.buffer_offset)}; + VAddr cpu_address{mapping->ptr + params.buffer_offset}; - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + gmmu->Map(gpu_address, cpu_address, params.mapping_size); + + return NvResult::Success; + } catch ([[maybe_unused]] const std::out_of_range& e) { + LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } } - const auto object{nvmap.GetHandle(params.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + auto handle{nvmap.GetHandle(params.handle)}; + if (!handle) { + return NvResult::BadValue; } - // The real nvservices doesn't make a distinction between handles and ids, and - // object can only have one handle and it will be the same as its id. Assert that this is the - // case to prevent unexpected behavior. - ASSERT(object->id == params.nvmap_handle); + VAddr cpu_address{static_cast(handle->address + params.buffer_offset)}; + u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; - u64 page_size{params.page_size}; - if (!page_size) { - page_size = object->align; - } + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + auto alloc{allocation_map.upper_bound(params.offset)}; - const auto physical_address{object->address + params.buffer_offset}; - u64 size{params.mapping_size}; - if (!size) { - size = object->size; - } + if (alloc-- == allocation_map.begin() || + (params.offset - alloc->first) + size > alloc->second.size) { + UNREACHABLE_MSG("Cannot perform a fixed mapping into an unallocated region!"); + return NvResult::BadValue; + } - const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; - if (is_alloc) { - params.offset = gmmu->MapAllocate(physical_address, size, page_size); + gmmu->Map(params.offset, cpu_address, size); + + auto mapping{std::make_shared(cpu_address, params.offset, size, true, false, + alloc->second.sparse)}; + alloc->second.mappings.push_back(mapping); + mapping_map[params.offset] = mapping; } else { - params.offset = gmmu->Map(physical_address, params.offset, size); - } + bool big_page{[&]() { + if (Common::IsAligned(handle->align, vm.big_page_size)) + return true; + else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) + return false; + else { + UNREACHABLE(); + return false; + } + }()}; - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); - result = NvResult::InvalidState; - } else { - AddBufferMap(params.offset, size, physical_address, is_alloc); + auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; + u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + params.offset = static_cast(allocator.Allocate( + static_cast(Common::AlignUp(size, page_size) >> page_size_bits))) + << page_size_bits; + if (!params.offset) { + UNREACHABLE_MSG("Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } + + gmmu->Map(params.offset, cpu_address, size); + + auto mapping{ + std::make_shared(cpu_address, params.offset, size, false, big_page, false)}; + mapping_map[params.offset] = mapping; } std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vector& output) { @@ -264,13 +416,36 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vectorUnmap(params.offset, *size); - } else { - LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto mapping{mapping_map.at(params.offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast(mapping->offset >> page_size_bits), + static_cast(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(params.offset, mapping->size); + } else { + gmmu->Unmap(params.offset, mapping->size); + } + + mapping_map.erase(params.offset); + } catch ([[maybe_unused]] const std::out_of_range& e) { + LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); } - std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; } @@ -284,28 +459,37 @@ NvResult nvhost_as_gpu::BindChannel(const std::vector& input, std::vector{ + VaRegion{ + .offset = vm.small_page_allocator->vaStart << VM::PAGE_SIZE_BITS, + .page_size = VM::YUZU_PAGESIZE, + .pages = vm.small_page_allocator->vaLimit - vm.small_page_allocator->vaStart, + }, + VaRegion{ + .offset = vm.big_page_allocator->vaStart << vm.big_page_size_bits, + .page_size = vm.big_page_size, + .pages = vm.big_page_allocator->vaLimit - vm.big_page_allocator->vaStart, + }, + }; +} + NvResult nvhost_as_gpu::GetVARegions(const std::vector& input, std::vector& output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); + LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); - params.buf_size = 0x30; + std::scoped_lock lock(mutex); - params.small = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = DEFAULT_SMALL_PAGE_SIZE, - .pages = 0x3fbfff, - }; + if (!vm.initialised) { + return NvResult::BadValue; + } - params.big = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = big_page_size, - .pages = 0x1bffff, - }; - - // TODO(ogniK): This probably can stay stubbed but should add support way way later + GetVARegionsImpl(params); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -316,64 +500,24 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector& input, std::vector nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { - const auto end{buffer_mappings.upper_bound(gpu_addr)}; - for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { - if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { - return iter->second; - } - } - - return std::nullopt; -} - -void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, - bool is_allocated) { - buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; -} - -std::optional nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { - if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { - std::size_t size{}; - - if (iter->second.IsAllocated()) { - size = iter->second.Size(); - } - - buffer_mappings.erase(iter); - - return size; - } - - return std::nullopt; -} - Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) { LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id); return nullptr; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index f5fb33ba7d..1d27739e26 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -5,14 +5,19 @@ #pragma once +#include +#include #include #include +#include #include #include +#include "common/address_space.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Tegra { @@ -30,17 +35,13 @@ class NvMap; namespace Service::Nvidia::Devices { -constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; -constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; - -class nvmap; - -enum class AddressSpaceFlags : u32 { - None = 0x0, - FixedOffset = 0x1, - Remap = 0x100, +enum class MappingFlags : u32 { + None = 0, + Fixed = 1 << 0, + Sparse = 1 << 1, + Remap = 1 << 8, }; -DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); +DECLARE_ENUM_FLAG_OPERATORS(MappingFlags); class nvhost_as_gpu final : public nvdevice { public: @@ -59,46 +60,15 @@ public: Kernel::KEvent* QueryEvent(u32 event_id) override; -private: - class BufferMap final { - public: - constexpr BufferMap() = default; - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_, - bool is_allocated_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_}, - is_allocated{is_allocated_} {} - - constexpr VAddr StartAddr() const { - return start_addr; - } - - constexpr VAddr EndAddr() const { - return end_addr; - } - - constexpr std::size_t Size() const { - return end_addr - start_addr; - } - - constexpr VAddr CpuAddr() const { - return cpu_addr; - } - - constexpr bool IsAllocated() const { - return is_allocated; - } - - private: - GPUVAddr start_addr{}; - GPUVAddr end_addr{}; - VAddr cpu_addr{}; - bool is_allocated{}; + struct VaRegion { + u64 offset; + u32 page_size; + u32 _pad0_; + u64 pages; }; + static_assert(sizeof(VaRegion) == 0x18); +private: struct IoctlAllocAsEx { u32_le flags{}; // usually passes 1 s32_le as_fd{}; // ignored; passes 0 @@ -113,7 +83,7 @@ private: struct IoctlAllocSpace { u32_le pages{}; u32_le page_size{}; - AddressSpaceFlags flags{}; + MappingFlags flags{}; INSERT_PADDING_WORDS(1); union { u64_le offset; @@ -130,19 +100,19 @@ private: static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); struct IoctlRemapEntry { - u16_le flags{}; - u16_le kind{}; - u32_le nvmap_handle{}; - u32_le map_offset{}; - u32_le offset{}; - u32_le pages{}; + u16 flags; + u16 kind; + NvCore::NvMap::Handle::Id handle; + u32 handle_offset_big_pages; + u32 as_offset_big_pages; + u32 big_pages; }; static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); struct IoctlMapBufferEx { - AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable - u32_le kind{}; // -1 is default - u32_le nvmap_handle{}; + MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable + u32_le kind{}; // -1 is default + NvCore::NvMap::Handle::Id handle; u32_le page_size{}; // 0 means don't care s64_le buffer_offset{}; u64_le mapping_size{}; @@ -160,27 +130,15 @@ private: }; static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); - struct IoctlVaRegion { - u64_le offset{}; - u32_le page_size{}; - INSERT_PADDING_WORDS(1); - u64_le pages{}; - }; - static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); - struct IoctlGetVaRegions { u64_le buf_addr{}; // (contained output user ptr on linux, ignored) u32_le buf_size{}; // forced to 2*sizeof(struct va_region) u32_le reserved{}; - IoctlVaRegion small{}; - IoctlVaRegion big{}; + std::array regions{}; }; - static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, + static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - s32 channel{}; - u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; - NvResult AllocAsEx(const std::vector& input, std::vector& output); NvResult AllocateSpace(const std::vector& input, std::vector& output); NvResult Remap(const std::vector& input, std::vector& output); @@ -189,23 +147,74 @@ private: NvResult FreeSpace(const std::vector& input, std::vector& output); NvResult BindChannel(const std::vector& input, std::vector& output); + void GetVARegionsImpl(IoctlGetVaRegions& params); NvResult GetVARegions(const std::vector& input, std::vector& output); NvResult GetVARegions(const std::vector& input, std::vector& output, std::vector& inline_output); - std::optional FindBufferMap(GPUVAddr gpu_addr) const; - void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); - std::optional RemoveBufferMap(GPUVAddr gpu_addr); + void FreeMappingLocked(u64 offset); Module& module; NvCore::Container& container; NvCore::NvMap& nvmap; + struct Mapping { + VAddr ptr; + u64 offset; + u64 size; + bool fixed; + bool big_page; // Only valid if fixed == false + bool sparse_alloc; + + Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) + : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), + sparse_alloc(sparse_alloc_) {} + }; + + struct Allocation { + u64 size; + std::list> mappings; + u32 page_size; + bool sparse; + }; + + std::map> + mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and + //!< mapping type, this is needed as what was originally a single buffer may + //!< have been split into multiple GPU side buffers with the remap flag. + std::map allocation_map; //!< Holds allocations created by AllocSpace from + //!< which fixed buffers can be mapped into + std::mutex mutex; //!< Locks all AS operations + + struct VM { + static constexpr u32 YUZU_PAGESIZE{0x1000}; + static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)}; + + static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000}; + static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000}; + u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; + u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)}; + + static constexpr u32 VA_START_SHIFT{10}; + static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34}; + static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37}; + u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT}; + u64 va_range_split{DEFAULT_VA_SPLIT}; + u64 va_range_end{DEFAULT_VA_RANGE}; + + using Allocator = Common::FlatAllocator; + + std::unique_ptr big_page_allocator; + std::shared_ptr + small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel + + bool initialised{}; + } vm; std::shared_ptr gmmu; - // This is expected to be ordered, therefore we must use a map, not unordered_map - std::map buffer_mappings; + // s32 channel{}; + // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; }; } // namespace Service::Nvidia::Devices diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 9e946d448a..fc68bcc730 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -71,18 +71,22 @@ void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) rasterizer = rasterizer_; } -GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { +GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) { return PageTableOp(gpu_addr, cpu_addr, size); } +GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size) { + return PageTableOp(gpu_addr, 0, size); +} + GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) { - return Map(cpu_addr, *FindFreeRange(size, align), size); + return Map(*FindFreeRange(size, align), cpu_addr, size); } GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { const std::optional gpu_addr = FindFreeRange(size, 1, true); ASSERT(gpu_addr); - return Map(cpu_addr, *gpu_addr, size); + return Map(*gpu_addr, cpu_addr, size); } void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 0a763fd198..b8878476aa 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -88,7 +88,8 @@ public: std::vector> GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const; - [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); + GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size); + GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size); [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size); [[nodiscard]] std::optional AllocateFixed(GPUVAddr gpu_addr, std::size_t size); From 6fc4012396e98a1a6ac455791b314d2280a12a51 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 16 Nov 2021 00:01:40 +0100 Subject: [PATCH 062/245] VideoCore: Extra Fixes. --- src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 4 +++- src/video_core/engines/puller.cpp | 2 +- src/video_core/texture_cache/util.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index a859a7abdc..54074af75c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -143,7 +143,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector } }(); - must_unmark_fail = true; + must_unmark_fail = false; const auto check_failing = [&]() { if (events[slot].fails > 2) { @@ -164,6 +164,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector if (params.timeout == 0) { if (check_failing()) { + events[slot].fails = 0; return NvResult::Success; } return NvResult::Timeout; @@ -180,6 +181,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector } if (check_failing()) { + event.fails = 0; return NvResult::Success; } diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 37f2ced18f..3866c8746d 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -118,7 +118,7 @@ void Puller::ProcessSemaphoreTriggerMethod() { } void Puller::ProcessSemaphoreRelease() { - memory_manager.Write(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release); + rasterizer->SignalSemaphore(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release); } void Puller::ProcessSemaphoreAcquire() { diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 1820823b22..bea1c27d06 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -755,7 +755,7 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config if (address == 0) { return false; } - if (address > (1ULL << 48)) { + if (address >= (1ULL << 40)) { return false; } if (gpu_memory.GpuToCpuAddress(address).has_value()) { From bb74973bba6005cee5f1409b3b5a6c572da3cf66 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 17 Nov 2021 05:44:01 +0100 Subject: [PATCH 063/245] General: Rebase fixes. --- src/video_core/texture_cache/texture_cache.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index e8b0b0a3ba..0ec999d63f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -885,14 +885,13 @@ void TextureCache

::InvalidateScale(Image& image) { } image.image_view_ids.clear(); image.image_view_infos.clear(); - for (auto& this_state : channel_storage) { - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(this_state.graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(this_state.compute_image_view_ids, CORRUPT_ID); - } - this_state.graphics_image_table.Invalidate(); - this_state.compute_image_table.Invalidate(); + auto& channel_info = channel_storage[image.channel]; + if constexpr (ENABLE_VALIDATION) { + std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); } + channel_info.graphics_image_table.Invalidate(); + channel_info.compute_image_table.Invalidate(); has_deleted_images = true; } From e462191482c6507daed67802c6c1d2c50f10c96e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 17 Dec 2021 16:45:06 +0100 Subject: [PATCH 064/245] Refactor VideoCore to use AS sepparate from Channel. --- src/common/hash.h | 7 + src/video_core/CMakeLists.txt | 1 + .../control/channel_state_cache.cpp | 8 +- src/video_core/control/channel_state_cache.h | 30 +++- .../control/channel_state_cache.inc | 23 +++- src/video_core/memory_manager.cpp | 5 +- src/video_core/memory_manager.h | 9 ++ .../texture_cache/texture_cache.cpp | 16 +++ src/video_core/texture_cache/texture_cache.h | 130 ++++++------------ .../texture_cache/texture_cache_base.h | 96 ++++++------- 10 files changed, 172 insertions(+), 153 deletions(-) create mode 100644 src/video_core/texture_cache/texture_cache.cpp diff --git a/src/common/hash.h b/src/common/hash.h index b6f3e6d6fc..e8fe78b075 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -18,4 +18,11 @@ struct PairHash { } }; +template +struct IdentityHash { + [[nodiscard]] size_t operator()(T value) const noexcept { + return static_cast(value); + } +}; + } // namespace Common diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e216c51a21..35faa70a0a 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -203,6 +203,7 @@ add_library(video_core STATIC texture_cache/render_targets.h texture_cache/samples_helper.h texture_cache/slot_vector.h + texture_cache/texture_cache.cpp texture_cache/texture_cache.h texture_cache/texture_cache_base.h texture_cache/types.h diff --git a/src/video_core/control/channel_state_cache.cpp b/src/video_core/control/channel_state_cache.cpp index f72a97b2f5..ec7ba907ca 100644 --- a/src/video_core/control/channel_state_cache.cpp +++ b/src/video_core/control/channel_state_cache.cpp @@ -1,5 +1,11 @@ #include "video_core/control/channel_state_cache.inc" namespace VideoCommon { + +ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state) + : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, + gpu_memory{*channel_state.memory_manager} {} + template class VideoCommon::ChannelSetupCaches; -} + +} // namespace VideoCommon diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index c8298c0030..c51040c831 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "common/common_types.h" @@ -41,9 +42,10 @@ template class ChannelSetupCaches { public: /// Operations for seting the channel of execution. + virtual ~ChannelSetupCaches(); /// Create channel state. - void CreateChannel(Tegra::Control::ChannelState& channel); + virtual void CreateChannel(Tegra::Control::ChannelState& channel); /// Bind a channel for execution. void BindToChannel(s32 id); @@ -51,18 +53,34 @@ public: /// Erase channel's state. void EraseChannel(s32 id); + Tegra::MemoryManager* GetFromID(size_t id) const { + std::unique_lock lk(config_mutex); + const auto ref = address_spaces.find(id); + return ref->second.gpu_memory; + } + protected: static constexpr size_t UNSET_CHANNEL{std::numeric_limits::max()}; + P* channel_state; + size_t current_channel_id{UNSET_CHANNEL}; + size_t current_address_space{}; + Tegra::Engines::Maxwell3D* maxwell3d; + Tegra::Engines::KeplerCompute* kepler_compute; + Tegra::MemoryManager* gpu_memory; + std::deque

channel_storage; std::deque free_channel_ids; std::unordered_map channel_map; + struct AddresSpaceRef { + size_t ref_count; + size_t storage_id; + Tegra::MemoryManager* gpu_memory; + }; + std::unordered_map address_spaces; + mutable std::mutex config_mutex; - P* channel_state; - size_t current_channel_id{UNSET_CHANNEL}; - Tegra::Engines::Maxwell3D* maxwell3d; - Tegra::Engines::KeplerCompute* kepler_compute; - Tegra::MemoryManager* gpu_memory; + virtual void OnGPUASRegister([[maybe_unused]] size_t map_id) {} }; } // namespace VideoCommon diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc index 3eb73af9f4..185eabc35f 100644 --- a/src/video_core/control/channel_state_cache.inc +++ b/src/video_core/control/channel_state_cache.inc @@ -6,18 +6,18 @@ namespace VideoCommon { -ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state) - : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, - gpu_memory{*channel_state.memory_manager} {} +template +ChannelSetupCaches

::~ChannelSetupCaches() = default; template void ChannelSetupCaches

::CreateChannel(struct Tegra::Control::ChannelState& channel) { + std::unique_lock lk(config_mutex); ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0); auto new_id = [this, &channel]() { if (!free_channel_ids.empty()) { auto id = free_channel_ids.front(); free_channel_ids.pop_front(); - new (&channel_storage[id]) ChannelInfo(channel); + new (&channel_storage[id]) P(channel); return id; } channel_storage.emplace_back(channel); @@ -27,11 +27,24 @@ void ChannelSetupCaches

::CreateChannel(struct Tegra::Control::ChannelState& c if (current_channel_id != UNSET_CHANNEL) { channel_state = &channel_storage[current_channel_id]; } + auto as_it = address_spaces.find(channel.memory_manager->GetID()); + if (as_it != address_spaces.end()) { + as_it->second.ref_count++; + return; + } + AddresSpaceRef new_gpu_mem_ref{ + .ref_count = 1, + .storage_id = address_spaces.size(), + .gpu_memory = channel.memory_manager.get(), + }; + address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref); + OnGPUASRegister(channel.memory_manager->GetID()); } /// Bind a channel for execution. template void ChannelSetupCaches

::BindToChannel(s32 id) { + std::unique_lock lk(config_mutex); auto it = channel_map.find(id); ASSERT(it != channel_map.end() && id >= 0); current_channel_id = it->second; @@ -39,11 +52,13 @@ void ChannelSetupCaches

::BindToChannel(s32 id) { maxwell3d = &channel_state->maxwell3d; kepler_compute = &channel_state->kepler_compute; gpu_memory = &channel_state->gpu_memory; + current_address_space = gpu_memory->GetID(); } /// Erase channel's channel_state. template void ChannelSetupCaches

::EraseChannel(s32 id) { + std::unique_lock lk(config_mutex); const auto it = channel_map.find(id); ASSERT(it != channel_map.end() && id >= 0); const auto this_id = it->second; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index fc68bcc730..d4c0dca78e 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -16,9 +16,12 @@ namespace Tegra { +std::atomic MemoryManager::unique_identifier_generator{}; + MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 page_bits_) : system{system_}, address_space_bits{address_space_bits_}, page_bits{page_bits_}, entries{}, - page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits} { + page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits}, + unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { address_space_size = 1ULL << address_space_bits; allocate_start = address_space_bits > 32 ? 1ULL << 32 : 0; page_size = 1ULL << page_bits; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index b8878476aa..56604ef3e8 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -26,6 +27,10 @@ public: u64 page_bits_ = 16); ~MemoryManager(); + size_t GetID() const { + return unique_identifier; + } + /// Binds a renderer to the memory manager. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); @@ -140,6 +145,10 @@ private: void SetEntry(size_t position, EntryType entry); Common::MultiLevelPageTable page_table; + + const size_t unique_identifier; + + static std::atomic unique_identifier_generator; }; } // namespace Tegra diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp new file mode 100644 index 0000000000..bc905a1a4f --- /dev/null +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -0,0 +1,16 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#include "video_core/control/channel_state_cache.inc" +#include "video_core/texture_cache/texture_cache_base.h" + +namespace VideoCommon { + +TextureCacheChannelInfo::TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept + : ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory}, + compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {} + +template class VideoCommon::ChannelSetupCaches; + +} // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 0ec999d63f..89c5faf88e 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once @@ -41,10 +43,6 @@ TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& // Setup channels current_channel_id = UNSET_CHANNEL; - state = nullptr; - maxwell3d = nullptr; - kepler_compute = nullptr; - gpu_memory = nullptr; // Make sure the first index is reserved for the null resources // This way the null resource becomes a compile time constant @@ -156,23 +154,24 @@ void TextureCache

::MarkModification(ImageId id) noexcept { template template void TextureCache

::FillGraphicsImageViews(std::span views) { - FillImageViews(state->graphics_image_table, state->graphics_image_view_ids, - views); + FillImageViews(channel_state->graphics_image_table, + channel_state->graphics_image_view_ids, views); } template void TextureCache

::FillComputeImageViews(std::span views) { - FillImageViews(state->compute_image_table, state->compute_image_view_ids, views); + FillImageViews(channel_state->compute_image_table, channel_state->compute_image_view_ids, + views); } template typename P::Sampler* TextureCache

::GetGraphicsSampler(u32 index) { - if (index > state->graphics_sampler_table.Limit()) { + if (index > channel_state->graphics_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return &slot_samplers[NULL_SAMPLER_ID]; } - const auto [descriptor, is_new] = state->graphics_sampler_table.Read(index); - SamplerId& id = state->graphics_sampler_ids[index]; + const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); + SamplerId& id = channel_state->graphics_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } @@ -181,12 +180,12 @@ typename P::Sampler* TextureCache

::GetGraphicsSampler(u32 index) { template typename P::Sampler* TextureCache

::GetComputeSampler(u32 index) { - if (index > state->compute_sampler_table.Limit()) { + if (index > channel_state->compute_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return &slot_samplers[NULL_SAMPLER_ID]; } - const auto [descriptor, is_new] = state->compute_sampler_table.Read(index); - SamplerId& id = state->compute_sampler_ids[index]; + const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); + SamplerId& id = channel_state->compute_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } @@ -199,11 +198,12 @@ void TextureCache

::SynchronizeGraphicsDescriptors() { const bool linked_tsc = maxwell3d->regs.sampler_index == SamplerIndex::ViaHeaderIndex; const u32 tic_limit = maxwell3d->regs.tic.limit; const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tsc.limit; - if (state->graphics_sampler_table.Synchornize(maxwell3d->regs.tsc.Address(), tsc_limit)) { - state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); + if (channel_state->graphics_sampler_table.Synchornize(maxwell3d->regs.tsc.Address(), + tsc_limit)) { + channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (state->graphics_image_table.Synchornize(maxwell3d->regs.tic.Address(), tic_limit)) { - state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); + if (channel_state->graphics_image_table.Synchornize(maxwell3d->regs.tic.Address(), tic_limit)) { + channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } @@ -213,11 +213,12 @@ void TextureCache

::SynchronizeComputeDescriptors() { const u32 tic_limit = kepler_compute->regs.tic.limit; const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address(); - if (state->compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { - state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); + if (channel_state->compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { + channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (state->compute_image_table.Synchornize(kepler_compute->regs.tic.Address(), tic_limit)) { - state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); + if (channel_state->compute_image_table.Synchornize(kepler_compute->regs.tic.Address(), + tic_limit)) { + channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } @@ -738,7 +739,7 @@ ImageViewId TextureCache

::FindImageView(const TICEntry& config) { if (!IsValidEntry(*gpu_memory, config)) { return NULL_IMAGE_VIEW_ID; } - const auto [pair, is_new] = state->image_views.try_emplace(config); + const auto [pair, is_new] = channel_state->image_views.try_emplace(config); ImageViewId& image_view_id = pair->second; if (is_new) { image_view_id = CreateImageView(config); @@ -1198,7 +1199,7 @@ SamplerId TextureCache

::FindSampler(const TSCEntry& config) { if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) { return NULL_SAMPLER_ID; } - const auto [pair, is_new] = state->samplers.try_emplace(config); + const auto [pair, is_new] = channel_state->samplers.try_emplace(config); if (is_new) { pair->second = slot_samplers.insert(runtime, config); } @@ -1327,8 +1328,8 @@ void TextureCache

::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Fu static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { - const auto it = state->gpu_page_table.find(page); - if (it == state->gpu_page_table.end()) { + const auto it = channel_state->gpu_page_table->find(page); + if (it == channel_state->gpu_page_table->end()) { if constexpr (BOOL_BREAK) { return false; } else { @@ -1454,8 +1455,9 @@ void TextureCache

::RegisterImage(ImageId image_id) { } image.lru_index = lru_cache.Insert(image_id, frame_tick); - ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, - [this, image_id](u64 page) { state->gpu_page_table[page].push_back(image_id); }); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { + (*channel_state->gpu_page_table)[page].push_back(image_id); + }); if (False(image.flags & ImageFlagBits::Sparse)) { auto map_id = slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id); @@ -1486,9 +1488,9 @@ void TextureCache

::UnregisterImage(ImageId image_id) { image.flags &= ~ImageFlagBits::BadOverlap; lru_cache.Free(image.lru_index); const auto& clear_page_table = - [this, image_id]( - u64 page, - std::unordered_map, IdentityHash>& selected_page_table) { + [this, image_id](u64 page, + std::unordered_map, Common::IdentityHash>& + selected_page_table) { const auto page_it = selected_page_table.find(page); if (page_it == selected_page_table.end()) { ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); @@ -1504,7 +1506,7 @@ void TextureCache

::UnregisterImage(ImageId image_id) { image_ids.erase(vector_it); }; ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) { - clear_page_table(page, state->gpu_page_table); + clear_page_table(page, (*channel_state->gpu_page_table)); }); if (False(image.flags & ImageFlagBits::Sparse)) { const auto map_id = image.map_view_id; @@ -1701,11 +1703,11 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { template void TextureCache

::RemoveImageViewReferences(std::span removed_views) { - auto it = state->image_views.begin(); - while (it != state->image_views.end()) { + auto it = channel_state->image_views.begin(); + while (it != channel_state->image_views.end()) { const auto found = std::ranges::find(removed_views, it->second); if (found != removed_views.end()) { - it = state->image_views.erase(it); + it = channel_state->image_views.erase(it); } else { ++it; } @@ -1967,61 +1969,19 @@ bool TextureCache

::IsFullClear(ImageViewId id) { scissor.max_y >= size.height; } -template -TextureCache

::ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& state) noexcept - : maxwell3d{*state.maxwell_3d}, kepler_compute{*state.kepler_compute}, - gpu_memory{*state.memory_manager}, graphics_image_table{gpu_memory}, - graphics_sampler_table{gpu_memory}, compute_image_table{gpu_memory}, compute_sampler_table{ - gpu_memory} {} - template void TextureCache

::CreateChannel(struct Tegra::Control::ChannelState& channel) { - ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0); - auto new_id = [this, &channel]() { - if (!free_channel_ids.empty()) { - auto id = free_channel_ids.front(); - free_channel_ids.pop_front(); - new (&channel_storage[id]) ChannelInfo(channel); - return id; - } - channel_storage.emplace_back(channel); - return channel_storage.size() - 1; - }(); - channel_map.emplace(channel.bind_id, new_id); - if (current_channel_id != UNSET_CHANNEL) { - state = &channel_storage[current_channel_id]; - } + VideoCommon::ChannelSetupCaches::CreateChannel(channel); + const auto it = channel_map.find(channel.bind_id); + auto* this_state = &channel_storage[it->second]; + const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()]; + this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id]; } /// Bind a channel for execution. template -void TextureCache

::BindToChannel(s32 id) { - auto it = channel_map.find(id); - ASSERT(it != channel_map.end() && id >= 0); - current_channel_id = it->second; - state = &channel_storage[current_channel_id]; - maxwell3d = &state->maxwell3d; - kepler_compute = &state->kepler_compute; - gpu_memory = &state->gpu_memory; -} - -/// Erase channel's state. -template -void TextureCache

::EraseChannel(s32 id) { - const auto it = channel_map.find(id); - ASSERT(it != channel_map.end() && id >= 0); - const auto this_id = it->second; - free_channel_ids.push_back(this_id); - channel_map.erase(it); - if (this_id == current_channel_id) { - current_channel_id = UNSET_CHANNEL; - state = nullptr; - maxwell3d = nullptr; - kepler_compute = nullptr; - gpu_memory = nullptr; - } else if (current_channel_id != UNSET_CHANNEL) { - state = &channel_storage[current_channel_id]; - } +void TextureCache

::OnGPUASRegister([[maybe_unused]] size_t map_id) { + gpu_page_table_storage.emplace_back(); } } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 69efcb7182..b24968b03f 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once @@ -13,9 +15,11 @@ #include #include "common/common_types.h" +#include "common/hash.h" #include "common/literals.h" #include "common/lru_cache.h" #include "video_core/compatible_formats.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/engines/fermi_2d.h" #include "video_core/surface.h" @@ -50,8 +54,35 @@ struct ImageViewInOut { ImageViewId id{}; }; +using TextureCacheGPUMap = std::unordered_map, Common::IdentityHash>; + +class TextureCacheChannelInfo : public ChannelInfo { +public: + TextureCacheChannelInfo() = delete; + TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept; + TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; + TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; + TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default; + TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default; + + DescriptorTable graphics_image_table{gpu_memory}; + DescriptorTable graphics_sampler_table{gpu_memory}; + std::vector graphics_sampler_ids; + std::vector graphics_image_view_ids; + + DescriptorTable compute_image_table{gpu_memory}; + DescriptorTable compute_sampler_table{gpu_memory}; + std::vector compute_sampler_ids; + std::vector compute_image_view_ids; + + std::unordered_map image_views; + std::unordered_map samplers; + + TextureCacheGPUMap* gpu_page_table; +}; + template -class TextureCache { +class TextureCache : public VideoCommon::ChannelSetupCaches { /// Address shift for caching images into a hash table static constexpr u64 YUZU_PAGEBITS = 20; @@ -85,13 +116,6 @@ class TextureCache { PixelFormat src_format; }; - template - struct IdentityHash { - [[nodiscard]] size_t operator()(T value) const noexcept { - return static_cast(value); - } - }; - public: explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&); @@ -179,13 +203,7 @@ public: [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept; /// Create channel state. - void CreateChannel(struct Tegra::Control::ChannelState& channel); - - /// Bind a channel for execution. - void BindToChannel(s32 id); - - /// Erase channel's state. - void EraseChannel(s32 id); + void CreateChannel(Tegra::Control::ChannelState& channel) final override; std::mutex mutex; @@ -221,6 +239,8 @@ private: } } + void OnGPUASRegister(size_t map_id) final override; + /// Runs the Garbage Collector. void RunGarbageCollector(); @@ -355,51 +375,15 @@ private: Runtime& runtime; - struct ChannelInfo { - ChannelInfo() = delete; - ChannelInfo(struct Tegra::Control::ChannelState& state) noexcept; - ChannelInfo(const ChannelInfo& state) = delete; - ChannelInfo& operator=(const ChannelInfo&) = delete; - ChannelInfo(ChannelInfo&& other) noexcept = default; - ChannelInfo& operator=(ChannelInfo&& other) noexcept = default; - - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; - - DescriptorTable graphics_image_table{gpu_memory}; - DescriptorTable graphics_sampler_table{gpu_memory}; - std::vector graphics_sampler_ids; - std::vector graphics_image_view_ids; - - DescriptorTable compute_image_table{gpu_memory}; - DescriptorTable compute_sampler_table{gpu_memory}; - std::vector compute_sampler_ids; - std::vector compute_image_view_ids; - - std::unordered_map image_views; - std::unordered_map samplers; - - std::unordered_map, IdentityHash> gpu_page_table; - }; - - std::deque channel_storage; - std::deque free_channel_ids; - std::unordered_map channel_map; - - ChannelInfo* state; - size_t current_channel_id{UNSET_CHANNEL}; VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D* maxwell3d; - Tegra::Engines::KeplerCompute* kepler_compute; - Tegra::MemoryManager* gpu_memory; + std::deque gpu_page_table_storage; RenderTargets render_targets; std::unordered_map framebuffers; - std::unordered_map, IdentityHash> page_table; - std::unordered_map, IdentityHash> sparse_page_table; + std::unordered_map, Common::IdentityHash> page_table; + std::unordered_map, Common::IdentityHash> sparse_page_table; std::unordered_map> sparse_views; VAddr virtual_invalid_space{}; From 9cf4c8831d6a8b0d9c6e2a48e89b667fd73accee Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 23 Dec 2021 01:40:45 +0100 Subject: [PATCH 065/245] Texture cache: Fix dangling references on multichannel. --- src/video_core/control/channel_state_cache.h | 1 + .../control/channel_state_cache.inc | 17 ++++--- src/video_core/texture_cache/texture_cache.h | 45 ++++++++++--------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index c51040c831..9b1d7362bb 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -72,6 +72,7 @@ protected: std::deque

channel_storage; std::deque free_channel_ids; std::unordered_map channel_map; + std::vector active_channel_ids; struct AddresSpaceRef { size_t ref_count; size_t storage_id; diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc index 185eabc35f..d3ae758b2c 100644 --- a/src/video_core/control/channel_state_cache.inc +++ b/src/video_core/control/channel_state_cache.inc @@ -1,3 +1,6 @@ + +#include + #include "video_core/control/channel_state.h" #include "video_core/control/channel_state_cache.h" #include "video_core/engines/kepler_compute.h" @@ -27,15 +30,16 @@ void ChannelSetupCaches

::CreateChannel(struct Tegra::Control::ChannelState& c if (current_channel_id != UNSET_CHANNEL) { channel_state = &channel_storage[current_channel_id]; } + active_channel_ids.push_back(new_id); auto as_it = address_spaces.find(channel.memory_manager->GetID()); if (as_it != address_spaces.end()) { - as_it->second.ref_count++; - return; + as_it->second.ref_count++; + return; } AddresSpaceRef new_gpu_mem_ref{ - .ref_count = 1, - .storage_id = address_spaces.size(), - .gpu_memory = channel.memory_manager.get(), + .ref_count = 1, + .storage_id = address_spaces.size(), + .gpu_memory = channel.memory_manager.get(), }; address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref); OnGPUASRegister(channel.memory_manager->GetID()); @@ -73,7 +77,8 @@ void ChannelSetupCaches

::EraseChannel(s32 id) { } else if (current_channel_id != UNSET_CHANNEL) { channel_state = &channel_storage[current_channel_id]; } + active_channel_ids.erase( + std::find(active_channel_ids.begin(), active_channel_ids.end(), this_id)); } - } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 89c5faf88e..5ef07d18f6 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -41,9 +41,6 @@ TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); sampler_descriptor.cubemap_anisotropy.Assign(1); - // Setup channels - current_channel_id = UNSET_CHANNEL; - // Make sure the first index is reserved for the null resources // This way the null resource becomes a compile time constant void(slot_images.insert(NullImageParams{})); @@ -886,13 +883,15 @@ void TextureCache

::InvalidateScale(Image& image) { } image.image_view_ids.clear(); image.image_view_infos.clear(); - auto& channel_info = channel_storage[image.channel]; - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); + for (size_t c : active_channel_ids) { + auto& channel_info = channel_storage[c]; + if constexpr (ENABLE_VALIDATION) { + std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); + } + channel_info.graphics_image_table.Invalidate(); + channel_info.compute_image_table.Invalidate(); } - channel_info.graphics_image_table.Invalidate(); - channel_info.compute_image_table.Invalidate(); has_deleted_images = true; } @@ -1690,26 +1689,30 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { if (alloc_images.empty()) { image_allocs_table.erase(alloc_it); } - for (auto& this_state : channel_storage) { + for (size_t c : active_channel_ids) { + auto& channel_info = channel_storage[c]; if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(this_state.graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(this_state.compute_image_view_ids, CORRUPT_ID); + std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); } - this_state.graphics_image_table.Invalidate(); - this_state.compute_image_table.Invalidate(); + channel_info.graphics_image_table.Invalidate(); + channel_info.compute_image_table.Invalidate(); } has_deleted_images = true; } template void TextureCache

::RemoveImageViewReferences(std::span removed_views) { - auto it = channel_state->image_views.begin(); - while (it != channel_state->image_views.end()) { - const auto found = std::ranges::find(removed_views, it->second); - if (found != removed_views.end()) { - it = channel_state->image_views.erase(it); - } else { - ++it; + for (size_t c : active_channel_ids) { + auto& channel_info = channel_storage[c]; + auto it = channel_info.image_views.begin(); + while (it != channel_info.image_views.end()) { + const auto found = std::ranges::find(removed_views, it->second); + if (found != removed_views.end()) { + it = channel_info.image_views.erase(it); + } else { + ++it; + } } } } From f350c3d74ea7880fc6d21f7f638b0d4a70a3246b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 1 Jan 2022 22:03:37 +0100 Subject: [PATCH 066/245] Texture cache: Fix the remaining issues with memory mnagement and unmapping. --- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 3 +++ src/video_core/control/channel_state_cache.h | 10 +++++++ src/video_core/gpu.cpp | 8 ++++++ src/video_core/gpu.h | 2 ++ src/video_core/memory_manager.cpp | 11 +++++++- src/video_core/rasterizer_interface.h | 2 +- .../renderer_opengl/gl_rasterizer.cpp | 4 +-- .../renderer_opengl/gl_rasterizer.h | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 4 +-- .../renderer_vulkan/vk_rasterizer.h | 2 +- src/video_core/texture_cache/texture_cache.h | 27 ++++++++++++++----- .../texture_cache/texture_cache_base.h | 4 +-- 12 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 344ddfc900..db2a6c3b2e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -16,6 +16,7 @@ #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "video_core/control/channel_state.h" +#include "video_core/gpu.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" @@ -24,6 +25,7 @@ namespace Service::Nvidia::Devices { nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, gmmu{} {} + nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -132,6 +134,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& vm.big_page_allocator = std::make_unique(start_big_pages, end_big_pages); gmmu = std::make_shared(system, 40, VM::PAGE_SIZE_BITS); + system.GPU().InitAddressSpace(*gmmu); vm.initialised = true; return NvResult::Success; diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 9b1d7362bb..31d80e8b71 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "common/common_types.h" @@ -59,6 +60,15 @@ public: return ref->second.gpu_memory; } + std::optional getStorageID(size_t id) const { + std::unique_lock lk(config_mutex); + const auto ref = address_spaces.find(id); + if (ref == address_spaces.end()) { + return std::nullopt; + } + return ref->second.storage_id; + } + protected: static constexpr size_t UNSET_CHANNEL{std::numeric_limits::max()}; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8c0ff00941..eebd7f3ff0 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -73,6 +73,10 @@ struct GPU::Impl { rasterizer->InitializeChannel(to_init); } + void InitAddressSpace(Tegra::MemoryManager& memory_manager) { + memory_manager.BindRasterizer(rasterizer); + } + void ReleaseChannel(Control::ChannelState& to_release) { UNIMPLEMENTED(); } @@ -452,6 +456,10 @@ void GPU::ReleaseChannel(Control::ChannelState& to_release) { impl->ReleaseChannel(to_release); } +void GPU::InitAddressSpace(Tegra::MemoryManager& memory_manager) { + impl->InitAddressSpace(memory_manager); +} + void GPU::BindRenderer(std::unique_ptr renderer) { impl->BindRenderer(std::move(renderer)); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 74d55e074e..7e84b0d2f4 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -118,6 +118,8 @@ public: void ReleaseChannel(Control::ChannelState& to_release); + void InitAddressSpace(Tegra::MemoryManager& memory_manager); + /// Request a host GPU memory flush from the CPU. [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index d4c0dca78e..b36067613f 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -59,10 +59,19 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp } for (u64 offset{}; offset < size; offset += page_size) { const GPUVAddr current_gpu_addr = gpu_addr + offset; + [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); SetEntry(current_gpu_addr, entry_type); + if (current_entry_type != entry_type) { + rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); + } if constexpr (entry_type == EntryType::Mapped) { const VAddr current_cpu_addr = cpu_addr + offset; const auto index = PageEntryIndex(current_gpu_addr); + const u32 sub_value = static_cast(current_cpu_addr >> 12ULL); + if (current_entry_type == entry_type && sub_value != page_table[index]) { + rasterizer->InvalidateRegion(static_cast(page_table[index]) << 12ULL, + page_size); + } page_table[index] = static_cast(current_cpu_addr >> 12ULL); } remaining_size -= page_size; @@ -168,7 +177,7 @@ std::optional MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t s const size_t page_last{(addr + size + page_size - 1) >> page_bits}; while (page_index < page_last) { const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr && *page_addr != 0) { + if (page_addr) { return page_addr; } ++page_index; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 8dacb26268..5362aafb67 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -95,7 +95,7 @@ public: virtual void UnmapMemory(VAddr addr, u64 size) = 0; /// Remap GPU memory range. This means underneath backing memory changed - virtual void ModifyGPUMemory(GPUVAddr addr, u64 size) = 0; + virtual void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) = 0; /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// and invalidated diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4eb9bd1160..f745fbf562 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -379,10 +379,10 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { shader_cache.OnCPUWrite(addr, size); } -void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) { +void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { { std::scoped_lock lock{texture_cache.mutex}; - texture_cache.UnmapGPUMemory(addr, size); + texture_cache.UnmapGPUMemory(as_id, addr, size); } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 39b20cfb20..d469075a13 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -82,7 +82,7 @@ public: void OnCPUWrite(VAddr addr, u64 size) override; void SyncGuestHost() override; void UnmapMemory(VAddr addr, u64 size) override; - void ModifyGPUMemory(GPUVAddr addr, u64 size) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalSemaphore(GPUVAddr addr, u32 value) override; void SignalSyncPoint(u32 value) override; void SignalReference() override; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7e08055442..50fdf5e18b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -441,10 +441,10 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { pipeline_cache.OnCPUWrite(addr, size); } -void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) { +void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { { std::scoped_lock lock{texture_cache.mutex}; - texture_cache.UnmapGPUMemory(addr, size); + texture_cache.UnmapGPUMemory(as_id, addr, size); } } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 642fe65769..c836158b87 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -78,7 +78,7 @@ public: void OnCPUWrite(VAddr addr, u64 size) override; void SyncGuestHost() override; void UnmapMemory(VAddr addr, u64 size) override; - void ModifyGPUMemory(GPUVAddr addr, u64 size) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalSemaphore(GPUVAddr addr, u32 value) override; void SignalSyncPoint(u32 value) override; void SignalReference() override; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 5ef07d18f6..a483892e43 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -480,12 +480,20 @@ void TextureCache

::UnmapMemory(VAddr cpu_addr, size_t size) { } template -void TextureCache

::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { +void TextureCache

::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { std::vector deleted_images; - ForEachImageInRegionGPU(gpu_addr, size, + ForEachImageInRegionGPU(as_id, gpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); for (const ImageId id : deleted_images) { Image& image = slot_images[id]; + if (True(image.flags & ImageFlagBits::CpuModified)) { + return; + } + image.flags |= ImageFlagBits::CpuModified; + if (True(image.flags & ImageFlagBits::Tracked)) { + UntrackImage(image, id); + } + /* if (True(image.flags & ImageFlagBits::Remapped)) { continue; } @@ -493,6 +501,7 @@ void TextureCache

::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { if (True(image.flags & ImageFlagBits::Tracked)) { UntrackImage(image, id); } + */ } } @@ -1322,13 +1331,19 @@ void TextureCache

::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f template template -void TextureCache

::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) { +void TextureCache

::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, + Func&& func) { using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; - ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { - const auto it = channel_state->gpu_page_table->find(page); - if (it == channel_state->gpu_page_table->end()) { + auto storage_id = getStorageID(as_id); + if (!storage_id) { + return; + } + auto& gpu_page_table = gpu_page_table_storage[*storage_id]; + ForEachGPUPage(gpu_addr, size, [this, gpu_page_table, &images, gpu_addr, size, func](u64 page) { + const auto it = gpu_page_table.find(page); + if (it == gpu_page_table.end()) { if constexpr (BOOL_BREAK) { return false; } else { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index b24968b03f..2f4db50478 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -173,7 +173,7 @@ public: void UnmapMemory(VAddr cpu_addr, size_t size); /// Remove images in a region - void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); + void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); /// Blit an image with the given parameters void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, @@ -309,7 +309,7 @@ private: void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); template - void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); + void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func); template void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); From e44ac8b821833672f7c99ad22dbe57ac9403279d Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 6 Jan 2022 19:47:15 +0100 Subject: [PATCH 067/245] Texture Cache: Fix GC and GPU Modified on Joins. --- src/video_core/texture_cache/texture_cache.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index a483892e43..66de41f047 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1758,6 +1758,7 @@ void TextureCache

::SynchronizeAliases(ImageId image_id) { boost::container::small_vector aliased_images; Image& image = slot_images[image_id]; bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); + bool any_modified = True(image.flags & ImageFlagBits::GpuModified); u64 most_recent_tick = image.modification_tick; for (const AliasedImage& aliased : image.aliased_images) { ImageBase& aliased_image = slot_images[aliased.id]; @@ -1765,9 +1766,7 @@ void TextureCache

::SynchronizeAliases(ImageId image_id) { most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); aliased_images.push_back(&aliased); any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); - if (True(aliased_image.flags & ImageFlagBits::GpuModified)) { - image.flags |= ImageFlagBits::GpuModified; - } + any_modified |= True(aliased_image.flags & ImageFlagBits::GpuModified); } } if (aliased_images.empty()) { @@ -1782,6 +1781,9 @@ void TextureCache

::SynchronizeAliases(ImageId image_id) { } } image.modification_tick = most_recent_tick; + if (any_modified) { + image.flags |= ImageFlagBits::GpuModified; + } std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) { const ImageBase& lhs_image = slot_images[lhs->id]; const ImageBase& rhs_image = slot_images[rhs->id]; From 668e80a9f42fb4ce0e16f6381d05bcbd286b2da1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 30 Jan 2022 10:31:13 +0100 Subject: [PATCH 068/245] VideoCore: Refactor syncing. --- src/core/core.cpp | 12 ++ src/core/core.h | 9 + .../service/nvdrv/devices/nvdisp_disp0.cpp | 2 +- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 19 +- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 4 + .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 10 +- src/core/hle/service/nvflinger/nvflinger.cpp | 9 +- src/video_core/CMakeLists.txt | 40 ++-- src/video_core/cdma_pusher.cpp | 25 ++- src/video_core/cdma_pusher.h | 15 +- src/video_core/control/channel_state.cpp | 2 +- src/video_core/control/channel_state.h | 2 +- src/video_core/control/channel_state_cache.h | 4 + src/video_core/control/scheduler.cpp | 2 +- src/video_core/control/scheduler.h | 2 +- src/video_core/dma_pusher.h | 26 ++- src/video_core/engines/puller.cpp | 65 +++--- src/video_core/engines/puller.h | 1 - src/video_core/fence_manager.h | 12 +- src/video_core/gpu.cpp | 197 +++++++++--------- src/video_core/gpu.h | 19 +- src/video_core/gpu_thread.cpp | 6 +- src/video_core/gpu_thread.h | 2 + .../codecs/codec.cpp | 36 ++-- .../codecs/codec.h | 14 +- .../codecs/h264.cpp | 4 +- .../{command_classes => host1x}/codecs/h264.h | 6 +- .../codecs/vp8.cpp | 4 +- .../{command_classes => host1x}/codecs/vp8.h | 5 +- .../codecs/vp9.cpp | 8 +- .../{command_classes => host1x}/codecs/vp9.h | 12 +- .../codecs/vp9_types.h | 0 src/video_core/host1x/control.cpp | 35 ++++ .../host1x.h => host1x/control.h} | 17 +- src/video_core/host1x/host1x.h | 33 +++ .../{command_classes => host1x}/nvdec.cpp | 6 +- .../{command_classes => host1x}/nvdec.h | 7 +- .../nvdec_common.h | 4 +- .../sync_manager.cpp | 10 +- .../sync_manager.h | 6 + src/video_core/host1x/syncpoint_manager.cpp | 93 +++++++++ src/video_core/host1x/syncpoint_manager.h | 99 +++++++++ .../{command_classes => host1x}/vic.cpp | 9 +- .../{command_classes => host1x}/vic.h | 7 +- 44 files changed, 648 insertions(+), 252 deletions(-) rename src/video_core/{command_classes => host1x}/codecs/codec.cpp (91%) rename src/video_core/{command_classes => host1x}/codecs/codec.h (78%) rename src/video_core/{command_classes => host1x}/codecs/h264.cpp (98%) rename src/video_core/{command_classes => host1x}/codecs/h264.h (96%) rename src/video_core/{command_classes => host1x}/codecs/vp8.cpp (93%) rename src/video_core/{command_classes => host1x}/codecs/vp8.h (93%) rename src/video_core/{command_classes => host1x}/codecs/vp9.cpp (99%) rename src/video_core/{command_classes => host1x}/codecs/vp9.h (93%) rename src/video_core/{command_classes => host1x}/codecs/vp9_types.h (100%) create mode 100644 src/video_core/host1x/control.cpp rename src/video_core/{command_classes/host1x.h => host1x/control.h} (60%) create mode 100644 src/video_core/host1x/host1x.h rename src/video_core/{command_classes => host1x}/nvdec.cpp (92%) rename src/video_core/{command_classes => host1x}/nvdec.h (88%) rename src/video_core/{command_classes => host1x}/nvdec_common.h (98%) rename src/video_core/{command_classes => host1x}/sync_manager.cpp (77%) rename src/video_core/{command_classes => host1x}/sync_manager.h (95%) create mode 100644 src/video_core/host1x/syncpoint_manager.cpp create mode 100644 src/video_core/host1x/syncpoint_manager.h rename src/video_core/{command_classes => host1x}/vic.cpp (98%) rename src/video_core/{command_classes => host1x}/vic.h (93%) diff --git a/src/core/core.cpp b/src/core/core.cpp index 1210928682..fa059a3948 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -51,6 +51,7 @@ #include "core/telemetry_session.h" #include "core/tools/freezer.h" #include "network/network.h" +#include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -215,6 +216,7 @@ struct System::Impl { telemetry_session = std::make_unique(); + host1x_core = std::make_unique(); gpu_core = VideoCore::CreateGPU(emu_window, system); if (!gpu_core) { return SystemResultStatus::ErrorVideoCore; @@ -373,6 +375,7 @@ struct System::Impl { app_loader.reset(); audio_core.reset(); gpu_core.reset(); + host1x_core.reset(); perf_stats.reset(); kernel.Shutdown(); memory.Reset(); @@ -450,6 +453,7 @@ struct System::Impl { /// AppLoader used to load the current executing application std::unique_ptr app_loader; std::unique_ptr gpu_core; + std::unique_ptr host1x_core; std::unique_ptr interrupt_manager; std::unique_ptr device_memory; std::unique_ptr audio_core; @@ -668,6 +672,14 @@ const Tegra::GPU& System::GPU() const { return *impl->gpu_core; } +Tegra::Host1x::Host1x& System::Host1x() { + return *impl->host1x_core; +} + +const Tegra::Host1x::Host1x& System::Host1x() const { + return *impl->host1x_core; +} + Core::Hardware::InterruptManager& System::InterruptManager() { return *impl->interrupt_manager; } diff --git a/src/core/core.h b/src/core/core.h index 0ce3b1d60c..e4168a921f 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -74,6 +74,9 @@ class TimeManager; namespace Tegra { class DebugContext; class GPU; +namespace Host1x { +class Host1x; +} // namespace Host1x } // namespace Tegra namespace VideoCore { @@ -260,6 +263,12 @@ public: /// Gets an immutable reference to the GPU interface. [[nodiscard]] const Tegra::GPU& GPU() const; + /// Gets a mutable reference to the Host1x interface + [[nodiscard]] Tegra::Host1x::Host1x& Host1x(); + + /// Gets an immutable reference to the Host1x interface. + [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const; + /// Gets a mutable reference to the renderer. [[nodiscard]] VideoCore::RendererBase& Renderer(); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index b1c0e9eb2d..e6a976714f 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -50,7 +50,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form stride, format, transform, crop_rect}; system.GetPerfStats().EndSystemFrame(); - system.GPU().SwapBuffers(&framebuffer); + system.GPU().RequestSwapBuffers(&framebuffer, nullptr, 0); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 54074af75c..ffe42d4235 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -18,6 +18,7 @@ #include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { @@ -129,7 +130,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector return NvResult::Success; } - auto& gpu = system.GPU(); + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); const u32 target_value = params.fence.value; auto lock = NvEventsLock(); @@ -149,7 +150,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector if (events[slot].fails > 2) { { auto lk = system.StallProcesses(); - gpu.WaitFence(fence_id, target_value); + host1x_syncpoint_manager.WaitHost(fence_id, target_value); system.UnstallProcesses(); } params.value.raw = target_value; @@ -198,7 +199,15 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector } params.value.raw |= slot; - gpu.RegisterSyncptInterrupt(fence_id, target_value); + event.wait_handle = + host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() { + auto& event = events[slot]; + if (event.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + event.kevent->GetWritableEvent().Signal(); + } + event.status.store(EventState::Signalled, std::memory_order_release); + }); return NvResult::Timeout; } @@ -288,8 +297,10 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v auto& event = events[event_id]; if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == EventState::Waiting) { - system.GPU().CancelSyncptInterrupt(event.assigned_syncpt, event.assigned_value); + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle); syncpoint_manager.RefreshSyncpoint(event.assigned_syncpt); + event.wait_handle = {}; } event.fails++; event.status.store(EventState::Cancelled, std::memory_order_release); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index d56aea4059..136a1e925e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Service::Nvidia::NvCore { class Container; @@ -78,6 +79,9 @@ private: // Tells if an NVEvent is registered or not bool registered{}; + // Used for waiting on a syncpoint & canceling it. + Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{}; + bool IsBeingUsed() { const auto current_status = status.load(std::memory_order_acquire); return current_status == EventState::Waiting || diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 38d45cb79a..db3e266ad6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -210,10 +210,10 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector& input, std::ve static std::vector BuildWaitCommandList(NvFence fence) { return { - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {fence.value}, - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing), BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id), }; @@ -222,12 +222,12 @@ static std::vector BuildWaitCommandList(NvFence fence) { static std::vector BuildIncrementCommandList(NvFence fence, u32 add_increment) { std::vector result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; for (u32 count = 0; count < add_increment; ++count) { - result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing)); result.emplace_back( BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); @@ -239,7 +239,7 @@ static std::vector BuildIncrementCommandList(NvFence fence static std::vector BuildIncrementWithWfiCommandList(NvFence fence, u32 add_increment) { std::vector result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; const std::vector increment{ diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 8c3013f833..aa112021d2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -24,6 +24,8 @@ #include "core/hle/service/vi/layer/vi_layer.h" #include "core/hle/service/vi/vi_results.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Service::NVFlinger { @@ -267,12 +269,12 @@ void NVFlinger::Compose() { return; // We are likely shutting down } - auto& gpu = system.GPU(); + auto& syncpoint_manager = system.Host1x().GetSyncpointManager(); const auto& multi_fence = buffer.fence; guard->unlock(); for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { const auto& fence = multi_fence.fences[fence_id]; - gpu.WaitFence(fence.id, fence.value); + syncpoint_manager.WaitGuest(fence.id, fence.value); } guard->lock(); @@ -284,6 +286,7 @@ void NVFlinger::Compose() { auto nvdisp = nvdrv->GetDevice(disp_fd); ASSERT(nvdisp); + guard->unlock(); Common::Rectangle crop_rect{ static_cast(buffer.crop.Left()), static_cast(buffer.crop.Top()), static_cast(buffer.crop.Right()), static_cast(buffer.crop.Bottom())}; @@ -292,6 +295,8 @@ void NVFlinger::Compose() { igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), static_cast(buffer.transform), crop_rect); + guard->lock(); + swap_interval = buffer.swap_interval; auto fence = android::Fence::NoFence(); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 35faa70a0a..723f9b67ca 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(host_shaders) if(LIBVA_FOUND) - set_source_files_properties(command_classes/codecs/codec.cpp + set_source_files_properties(host1x/codecs/codec.cpp PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) endif() @@ -15,24 +15,6 @@ add_library(video_core STATIC buffer_cache/buffer_cache.h cdma_pusher.cpp cdma_pusher.h - command_classes/codecs/codec.cpp - command_classes/codecs/codec.h - command_classes/codecs/h264.cpp - command_classes/codecs/h264.h - command_classes/codecs/vp8.cpp - command_classes/codecs/vp8.h - command_classes/codecs/vp9.cpp - command_classes/codecs/vp9.h - command_classes/codecs/vp9_types.h - command_classes/host1x.cpp - command_classes/host1x.h - command_classes/nvdec.cpp - command_classes/nvdec.h - command_classes/nvdec_common.h - command_classes/sync_manager.cpp - command_classes/sync_manager.h - command_classes/vic.cpp - command_classes/vic.h compatible_formats.cpp compatible_formats.h control/channel_state.cpp @@ -63,6 +45,26 @@ add_library(video_core STATIC engines/puller.cpp engines/puller.h framebuffer_config.h + host1x/codecs/codec.cpp + host1x/codecs/codec.h + host1x/codecs/h264.cpp + host1x/codecs/h264.h + host1x/codecs/vp8.cpp + host1x/codecs/vp8.h + host1x/codecs/vp9.cpp + host1x/codecs/vp9.h + host1x/codecs/vp9_types.h + host1x/control.cpp + host1x/control.h + host1x/nvdec.cpp + host1x/nvdec.h + host1x/nvdec_common.h + host1x/sync_manager.cpp + host1x/sync_manager.h + host1x/syncpoint_manager.cpp + host1x/syncpoint_manager.h + host1x/vic.cpp + host1x/vic.h macro/macro.cpp macro/macro.h macro/macro_hle.cpp diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 8e890a85ed..148126347a 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -2,20 +2,22 @@ // SPDX-License-Identifier: MIT #include -#include "command_classes/host1x.h" -#include "command_classes/nvdec.h" -#include "command_classes/vic.h" #include "video_core/cdma_pusher.h" -#include "video_core/command_classes/sync_manager.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" +#include "video_core/host1x/control.h" +#include "video_core/host1x/nvdec.h" +#include "video_core/host1x/nvdec_common.h" +#include "video_core/host1x/sync_manager.h" +#include "video_core/host1x/vic.h" +#include "video_core/memory_manager.h" namespace Tegra { CDmaPusher::CDmaPusher(GPU& gpu_) - : gpu{gpu_}, nvdec_processor(std::make_shared(gpu)), - vic_processor(std::make_unique(gpu, nvdec_processor)), - host1x_processor(std::make_unique(gpu)), - sync_manager(std::make_unique(gpu)) {} + : gpu{gpu_}, nvdec_processor(std::make_shared(gpu)), + vic_processor(std::make_unique(gpu, nvdec_processor)), + host1x_processor(std::make_unique(gpu)), + sync_manager(std::make_unique(gpu)) {} CDmaPusher::~CDmaPusher() = default; @@ -109,16 +111,17 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { case ThiMethod::SetMethod1: LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})", static_cast(vic_thi_state.method_0), data); - vic_processor->ProcessMethod(static_cast(vic_thi_state.method_0), data); + vic_processor->ProcessMethod(static_cast(vic_thi_state.method_0), + data); break; default: break; } break; - case ChClassId::Host1x: + case ChClassId::Control: // This device is mainly for syncpoint synchronization LOG_DEBUG(Service_NVDRV, "Host1X Class Method"); - host1x_processor->ProcessMethod(static_cast(offset), data); + host1x_processor->ProcessMethod(static_cast(offset), data); break; default: UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast(current_class)); diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index d6ffef95f7..de17c20821 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h @@ -13,10 +13,13 @@ namespace Tegra { class GPU; -class Host1x; + +namespace Host1x { +class Control; class Nvdec; class SyncptIncrManager; class Vic; +} // namespace Host1x enum class ChSubmissionMode : u32 { SetClass = 0, @@ -30,7 +33,7 @@ enum class ChSubmissionMode : u32 { enum class ChClassId : u32 { NoClass = 0x0, - Host1x = 0x1, + Control = 0x1, VideoEncodeMpeg = 0x20, VideoEncodeNvEnc = 0x21, VideoStreamingVi = 0x30, @@ -102,10 +105,10 @@ private: void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument); GPU& gpu; - std::shared_ptr nvdec_processor; - std::unique_ptr vic_processor; - std::unique_ptr host1x_processor; - std::unique_ptr sync_manager; + std::shared_ptr nvdec_processor; + std::unique_ptr vic_processor; + std::unique_ptr host1x_processor; + std::unique_ptr sync_manager; ChClassId current_class{}; ThiRegisters vic_thi_state{}; ThiRegisters nvdec_thi_state{}; diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp index 67803fe94c..3613c49927 100644 --- a/src/video_core/control/channel_state.cpp +++ b/src/video_core/control/channel_state.cpp @@ -1,5 +1,5 @@ // Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version +// Licensed under GPLv3 or any later version // Refer to the license.txt file included. #include "common/assert.h" diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h index 82808a6b82..08a7591e17 100644 --- a/src/video_core/control/channel_state.h +++ b/src/video_core/control/channel_state.h @@ -1,5 +1,5 @@ // Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version +// Licensed under GPLv3 or any later version // Refer to the license.txt file included. #pragma once diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 31d80e8b71..dbf833de74 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -1,3 +1,7 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + #pragma once #include diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp index e1abcb188b..a9bb00aa77 100644 --- a/src/video_core/control/scheduler.cpp +++ b/src/video_core/control/scheduler.cpp @@ -1,5 +1,5 @@ // Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version +// Licensed under GPLv3 or any later version // Refer to the license.txt file included. #include diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h index 802e9caffb..c1a7739467 100644 --- a/src/video_core/control/scheduler.h +++ b/src/video_core/control/scheduler.h @@ -1,5 +1,5 @@ // Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version +// Licensed under GPLv3 or any later version // Refer to the license.txt file included. #pragma once diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index fd7c936c40..938f0f11cf 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -37,24 +37,32 @@ enum class SubmissionMode : u32 { // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence // their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. // So the values you see in docs might be multiplied by 4. +// Register documentation: +// https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/cla26f.h +// +// Register Description (approx): +// https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/manuals/volta/gv100/dev_pbdma.ref.txt enum class BufferMethods : u32 { BindObject = 0x0, + Illegal = 0x1, Nop = 0x2, SemaphoreAddressHigh = 0x4, SemaphoreAddressLow = 0x5, - SemaphoreSequence = 0x6, - SemaphoreTrigger = 0x7, - NotifyIntr = 0x8, + SemaphoreSequencePayload = 0x6, + SemaphoreOperation = 0x7, + NonStallInterrupt = 0x8, WrcacheFlush = 0x9, - Unk28 = 0xA, - UnkCacheFlush = 0xB, + MemOpA = 0xA, + MemOpB = 0xB, + MemOpC = 0xC, + MemOpD = 0xD, RefCnt = 0x14, SemaphoreAcquire = 0x1A, SemaphoreRelease = 0x1B, - FenceValue = 0x1C, - FenceAction = 0x1D, - WaitForInterrupt = 0x1E, - Unk7c = 0x1F, + SyncpointPayload = 0x1C, + SyncpointOperation = 0x1D, + WaitForIdle = 0x1E, + CRCCheck = 0x1F, Yield = 0x20, NonPullerMethods = 0x40, }; diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 3866c8746d..8c17639e43 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -68,11 +68,6 @@ void Puller::ProcessFenceActionMethod() { } } -void Puller::ProcessWaitForInterruptMethod() { - // TODO(bunnei) ImplementMe - LOG_WARNING(HW_GPU, "(STUBBED) called"); -} - void Puller::ProcessSemaphoreTriggerMethod() { const auto semaphoreOperationMask = 0xF; const auto op = @@ -91,29 +86,33 @@ void Puller::ProcessSemaphoreTriggerMethod() { block.timestamp = gpu.GetTicks(); memory_manager.WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, sizeof(block)); } else { - const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; - if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || - (op == GpuSemaphoreOperation::AcquireGequal && - static_cast(word - regs.semaphore_sequence) > 0) || - (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) { - // Nothing to do in this case - } else { + do { + const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; regs.acquire_source = true; regs.acquire_value = regs.semaphore_sequence; if (op == GpuSemaphoreOperation::AcquireEqual) { regs.acquire_active = true; regs.acquire_mode = false; + if (word != regs.acquire_value) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } } else if (op == GpuSemaphoreOperation::AcquireGequal) { regs.acquire_active = true; regs.acquire_mode = true; + if (word < regs.acquire_value) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } } else if (op == GpuSemaphoreOperation::AcquireMask) { - // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with - // semaphore_sequence, gives a non-0 result - LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented"); + if (word & regs.semaphore_sequence == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } } else { LOG_ERROR(HW_GPU, "Invalid semaphore operation"); } - } + } while (false); } } @@ -124,6 +123,7 @@ void Puller::ProcessSemaphoreRelease() { void Puller::ProcessSemaphoreAcquire() { const u32 word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); const auto value = regs.semaphore_acquire; + std::this_thread::sleep_for(std::chrono::milliseconds(5)); if (word != value) { regs.acquire_active = true; regs.acquire_value = value; @@ -146,32 +146,39 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { case BufferMethods::Nop: case BufferMethods::SemaphoreAddressHigh: case BufferMethods::SemaphoreAddressLow: - case BufferMethods::SemaphoreSequence: - case BufferMethods::UnkCacheFlush: + case BufferMethods::SemaphoreSequencePayload: case BufferMethods::WrcacheFlush: - case BufferMethods::FenceValue: + case BufferMethods::SyncpointPayload: break; case BufferMethods::RefCnt: rasterizer->SignalReference(); break; - case BufferMethods::FenceAction: + case BufferMethods::SyncpointOperation: ProcessFenceActionMethod(); break; - case BufferMethods::WaitForInterrupt: - ProcessWaitForInterruptMethod(); + case BufferMethods::WaitForIdle: + rasterizer->WaitForIdle(); break; - case BufferMethods::SemaphoreTrigger: { + case BufferMethods::SemaphoreOperation: { ProcessSemaphoreTriggerMethod(); break; } - case BufferMethods::NotifyIntr: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented"); + case BufferMethods::NonStallInterrupt: { + LOG_ERROR(HW_GPU, "Special puller engine method NonStallInterrupt not implemented"); break; } - case BufferMethods::Unk28: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented"); + case BufferMethods::MemOpA: { + LOG_ERROR(HW_GPU, "Memory Operation A"); + break; + } + case BufferMethods::MemOpB: { + // Implement this better. + rasterizer->SyncGuestHost(); + break; + } + case BufferMethods::MemOpC: + case BufferMethods::MemOpD: { + LOG_ERROR(HW_GPU, "Memory Operation C,D"); break; } case BufferMethods::SemaphoreAcquire: { diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h index d948ec790d..b4619e9a88 100644 --- a/src/video_core/engines/puller.h +++ b/src/video_core/engines/puller.h @@ -141,7 +141,6 @@ private: void ProcessSemaphoreAcquire(); void ProcessSemaphoreRelease(); void ProcessSemaphoreTriggerMethod(); - void ProcessWaitForInterruptMethod(); [[nodiscard]] bool ExecuteMethodOnEngine(u32 method); /// Mapping of command subchannels to their bound engine ids diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index d658e038d9..03a70e5e01 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -11,6 +11,8 @@ #include "common/common_types.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" #include "video_core/rasterizer_interface.h" namespace VideoCommon { @@ -72,6 +74,7 @@ public: } void SignalSyncPoint(u32 value) { + syncpoint_manager.IncrementGuest(value); TryReleasePendingFences(); const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); @@ -96,7 +99,7 @@ public: auto payload = current_fence->GetPayload(); std::memcpy(address, &payload, sizeof(payload)); } else { - gpu.IncrementSyncPoint(current_fence->GetPayload()); + syncpoint_manager.IncrementHost(current_fence->GetPayload()); } PopFence(); } @@ -106,8 +109,8 @@ protected: explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, TQueryCache& query_cache_) - : rasterizer{rasterizer_}, gpu{gpu_}, texture_cache{texture_cache_}, - buffer_cache{buffer_cache_}, query_cache{query_cache_} {} + : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()}, + texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} virtual ~FenceManager() = default; @@ -125,6 +128,7 @@ protected: VideoCore::RasterizerInterface& rasterizer; Tegra::GPU& gpu; + Tegra::Host1x::SyncpointManager& syncpoint_manager; TTextureCache& texture_cache; TTBufferCache& buffer_cache; TQueryCache& query_cache; @@ -142,7 +146,7 @@ private: const auto payload = current_fence->GetPayload(); std::memcpy(address, &payload, sizeof(payload)); } else { - gpu.IncrementSyncPoint(current_fence->GetPayload()); + syncpoint_manager.IncrementHost(current_fence->GetPayload()); } PopFence(); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index eebd7f3ff0..1097db08ae 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -28,6 +28,8 @@ #include "video_core/engines/maxwell_dma.h" #include "video_core/gpu.h" #include "video_core/gpu_thread.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" #include "video_core/shader_notify.h" @@ -38,7 +40,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); struct GPU::Impl { explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) - : gpu{gpu_}, system{system_}, use_nvdec{use_nvdec_}, + : gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_}, shader_notify{std::make_unique()}, is_async{is_async_}, gpu_thread{system_, is_async_}, scheduler{std::make_unique(gpu)} {} @@ -115,31 +117,35 @@ struct GPU::Impl { } /// Request a host GPU memory flush from the CPU. - [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size) { - std::unique_lock lck{flush_request_mutex}; - const u64 fence = ++last_flush_fence; - flush_requests.emplace_back(fence, addr, size); + template + [[nodiscard]] u64 RequestSyncOperation(Func&& action) { + std::unique_lock lck{sync_request_mutex}; + const u64 fence = ++last_sync_fence; + sync_requests.emplace_back(action); return fence; } /// Obtains current flush request fence id. - [[nodiscard]] u64 CurrentFlushRequestFence() const { - return current_flush_fence.load(std::memory_order_relaxed); + [[nodiscard]] u64 CurrentSyncRequestFence() const { + return current_sync_fence.load(std::memory_order_relaxed); + } + + void WaitForSyncOperation(const u64 fence) { + std::unique_lock lck{sync_request_mutex}; + sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; }); } /// Tick pending requests within the GPU. void TickWork() { - std::unique_lock lck{flush_request_mutex}; - while (!flush_requests.empty()) { - auto& request = flush_requests.front(); - const u64 fence = request.fence; - const VAddr addr = request.addr; - const std::size_t size = request.size; - flush_requests.pop_front(); - flush_request_mutex.unlock(); - rasterizer->FlushRegion(addr, size); - current_flush_fence.store(fence); - flush_request_mutex.lock(); + std::unique_lock lck{sync_request_mutex}; + while (!sync_requests.empty()) { + auto request = std::move(sync_requests.front()); + sync_requests.pop_front(); + sync_request_mutex.unlock(); + request(); + current_sync_fence.fetch_add(1, std::memory_order_release); + sync_request_mutex.lock(); + sync_request_cv.notify_all(); } } @@ -207,78 +213,26 @@ struct GPU::Impl { /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. void WaitFence(u32 syncpoint_id, u32 value) { - // Synced GPU, is always in sync - if (!is_async) { - return; - } if (syncpoint_id == UINT32_MAX) { - // TODO: Research what this does. - LOG_ERROR(HW_GPU, "Waiting for syncpoint -1 not implemented"); return; } MICROPROFILE_SCOPE(GPU_wait); - std::unique_lock lock{sync_mutex}; - sync_cv.wait(lock, [=, this] { - if (shutting_down.load(std::memory_order_relaxed)) { - // We're shutting down, ensure no threads continue to wait for the next syncpoint - return true; - } - return syncpoints.at(syncpoint_id).load() >= value; - }); + host1x.GetSyncpointManager().WaitHost(syncpoint_id, value); } void IncrementSyncPoint(u32 syncpoint_id) { - auto& syncpoint = syncpoints.at(syncpoint_id); - syncpoint++; - std::scoped_lock lock{sync_mutex}; - sync_cv.notify_all(); - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - if (!interrupt.empty()) { - u32 value = syncpoint.load(); - auto it = interrupt.begin(); - while (it != interrupt.end()) { - if (value >= *it) { - TriggerCpuInterrupt(syncpoint_id, *it); - it = interrupt.erase(it); - continue; - } - it++; - } - } + host1x.GetSyncpointManager().IncrementHost(syncpoint_id); } [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).load(); + return host1x.GetSyncpointManager().GetHostSyncpointValue(syncpoint_id); } void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::scoped_lock lock{sync_mutex}; - u32 current_value = syncpoints.at(syncpoint_id).load(); - if ((static_cast(current_value) - static_cast(value)) >= 0) { + auto& syncpoint_manager = host1x.GetSyncpointManager(); + syncpoint_manager.RegisterHostAction(syncpoint_id, value, [this, syncpoint_id, value]() { TriggerCpuInterrupt(syncpoint_id, value); - return; - } - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - bool contains = std::any_of(interrupt.begin(), interrupt.end(), - [value](u32 in_value) { return in_value == value; }); - if (contains) { - return; - } - interrupt.emplace_back(value); - } - - [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::scoped_lock lock{sync_mutex}; - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - const auto iter = - std::find_if(interrupt.begin(), interrupt.end(), - [value](u32 interrupt_value) { return value == interrupt_value; }); - - if (iter == interrupt.end()) { - return false; - } - interrupt.erase(iter); - return true; + }); } [[nodiscard]] u64 GetTicks() const { @@ -387,8 +341,48 @@ struct GPU::Impl { interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); } + void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, + Service::Nvidia::NvFence* fences, size_t num_fences) { + size_t current_request_counter{}; + { + std::unique_lock lk(request_swap_mutex); + if (free_swap_counters.empty()) { + current_request_counter = request_swap_counters.size(); + request_swap_counters.emplace_back(num_fences); + } else { + current_request_counter = free_swap_counters.front(); + request_swap_counters[current_request_counter] = num_fences; + free_swap_counters.pop_front(); + } + } + const auto wait_fence = + RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { + auto& syncpoint_manager = host1x.GetSyncpointManager(); + if (num_fences == 0) { + renderer->SwapBuffers(framebuffer); + } + const auto executer = [this, current_request_counter, + framebuffer_copy = *framebuffer]() { + { + std::unique_lock lk(request_swap_mutex); + if (--request_swap_counters[current_request_counter] != 0) { + return; + } + free_swap_counters.push_back(current_request_counter); + } + renderer->SwapBuffers(&framebuffer_copy); + }; + for (size_t i = 0; i < num_fences; i++) { + syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); + } + }); + gpu_thread.TickGPU(); + WaitForSyncOperation(wait_fence); + } + GPU& gpu; Core::System& system; + Host1x::Host1x& host1x; std::map> cdma_pushers; std::unique_ptr renderer; @@ -411,18 +405,11 @@ struct GPU::Impl { std::condition_variable sync_cv; - struct FlushRequest { - explicit FlushRequest(u64 fence_, VAddr addr_, std::size_t size_) - : fence{fence_}, addr{addr_}, size{size_} {} - u64 fence; - VAddr addr; - std::size_t size; - }; - - std::list flush_requests; - std::atomic current_flush_fence{}; - u64 last_flush_fence{}; - std::mutex flush_request_mutex; + std::list> sync_requests; + std::atomic current_sync_fence{}; + u64 last_sync_fence{}; + std::mutex sync_request_mutex; + std::condition_variable sync_request_cv; const bool is_async; @@ -433,6 +420,10 @@ struct GPU::Impl { std::unordered_map> channels; Tegra::Control::ChannelState* current_channel; s32 bound_channel{-1}; + + std::deque free_swap_counters; + std::deque request_swap_counters; + std::mutex request_swap_mutex; }; GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) @@ -477,17 +468,32 @@ void GPU::OnCommandListEnd() { } u64 GPU::RequestFlush(VAddr addr, std::size_t size) { - return impl->RequestFlush(addr, size); + return impl->RequestSyncOperation( + [this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); }); } -u64 GPU::CurrentFlushRequestFence() const { - return impl->CurrentFlushRequestFence(); +u64 GPU::CurrentSyncRequestFence() const { + return impl->CurrentSyncRequestFence(); +} + +void GPU::WaitForSyncOperation(u64 fence) { + return impl->WaitForSyncOperation(fence); } void GPU::TickWork() { impl->TickWork(); } +/// Gets a mutable reference to the Host1x interface +Host1x::Host1x& GPU::Host1x() { + return impl->host1x; +} + +/// Gets an immutable reference to the Host1x interface. +const Host1x::Host1x& GPU::Host1x() const { + return impl->host1x; +} + Engines::Maxwell3D& GPU::Maxwell3D() { return impl->Maxwell3D(); } @@ -536,6 +542,11 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { return impl->ShaderNotify(); } +void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, + Service::Nvidia::NvFence* fences, size_t num_fences) { + impl->RequestSwapBuffers(framebuffer, fences, num_fences); +} + void GPU::WaitFence(u32 syncpoint_id, u32 value) { impl->WaitFence(syncpoint_id, value); } @@ -552,10 +563,6 @@ void GPU::RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { impl->RegisterSyncptInterrupt(syncpoint_id, value); } -bool GPU::CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { - return impl->CancelSyncptInterrupt(syncpoint_id, value); -} - u64 GPU::GetTicks() const { return impl->GetTicks(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 7e84b0d2f4..c1a5382572 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -93,6 +93,10 @@ namespace Control { struct ChannelState; } +namespace Host1x { +class Host1x; +} // namespace Host1x + class MemoryManager; class GPU final { @@ -124,11 +128,19 @@ public: [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); /// Obtains current flush request fence id. - [[nodiscard]] u64 CurrentFlushRequestFence() const; + [[nodiscard]] u64 CurrentSyncRequestFence() const; + + void WaitForSyncOperation(u64 fence); /// Tick pending requests within the GPU. void TickWork(); + /// Gets a mutable reference to the Host1x interface + [[nodiscard]] Host1x::Host1x& Host1x(); + + /// Gets an immutable reference to the Host1x interface. + [[nodiscard]] const Host1x::Host1x& Host1x() const; + /// Returns a reference to the Maxwell3D GPU engine. [[nodiscard]] Engines::Maxwell3D& Maxwell3D(); @@ -174,8 +186,6 @@ public: void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); - bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); - [[nodiscard]] u64 GetTicks() const; [[nodiscard]] bool IsAsync() const; @@ -184,6 +194,9 @@ public: void RendererFrameEndNotify(); + void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, + Service::Nvidia::NvFence* fences, size_t num_fences); + /// Performs any additional setup necessary in order to begin GPU emulation. /// This can be used to launch any necessary threads and register any necessary /// core timing events. diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 9844cde43a..2c03545bf1 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -93,8 +93,12 @@ void ThreadManager::FlushRegion(VAddr addr, u64 size) { } auto& gpu = system.GPU(); u64 fence = gpu.RequestFlush(addr, size); + TickGPU(); + gpu.WaitForSyncOperation(fence); +} + +void ThreadManager::TickGPU() { PushCommand(GPUTickCommand(), true); - ASSERT(fence <= gpu.CurrentFlushRequestFence()); } void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index c5078a2b37..64628d3e38 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -135,6 +135,8 @@ public: void OnCommandListEnd(); + void TickGPU(); + private: /// Pushes a command to be executed by the GPU thread u64 PushCommand(CommandData&& command_data, bool block = false); diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp similarity index 91% rename from src/video_core/command_classes/codecs/codec.cpp rename to src/video_core/host1x/codecs/codec.cpp index a5eb97b7f5..70c47ae03d 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp @@ -6,11 +6,11 @@ #include #include "common/assert.h" #include "common/settings.h" -#include "video_core/command_classes/codecs/codec.h" -#include "video_core/command_classes/codecs/h264.h" -#include "video_core/command_classes/codecs/vp8.h" -#include "video_core/command_classes/codecs/vp9.h" #include "video_core/gpu.h" +#include "video_core/host1x/codecs/codec.h" +#include "video_core/host1x/codecs/h264.h" +#include "video_core/host1x/codecs/vp8.h" +#include "video_core/host1x/codecs/vp9.h" #include "video_core/memory_manager.h" extern "C" { @@ -73,7 +73,7 @@ void AVFrameDeleter(AVFrame* ptr) { av_frame_free(&ptr); } -Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) +Codec::Codec(GPU& gpu_, const Host1x::NvdecCommon::NvdecRegisters& regs) : gpu(gpu_), state{regs}, h264_decoder(std::make_unique(gpu)), vp8_decoder(std::make_unique(gpu)), vp9_decoder(std::make_unique(gpu)) {} @@ -168,11 +168,11 @@ void Codec::InitializeGpuDecoder() { void Codec::Initialize() { const AVCodecID codec = [&] { switch (current_codec) { - case NvdecCommon::VideoCodec::H264: + case Host1x::NvdecCommon::VideoCodec::H264: return AV_CODEC_ID_H264; - case NvdecCommon::VideoCodec::VP8: + case Host1x::NvdecCommon::VideoCodec::VP8: return AV_CODEC_ID_VP8; - case NvdecCommon::VideoCodec::VP9: + case Host1x::NvdecCommon::VideoCodec::VP9: return AV_CODEC_ID_VP9; default: UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); @@ -197,7 +197,7 @@ void Codec::Initialize() { initialized = true; } -void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { +void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { if (current_codec != codec) { current_codec = codec; LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName()); @@ -215,11 +215,11 @@ void Codec::Decode() { bool vp9_hidden_frame = false; const auto& frame_data = [&]() { switch (current_codec) { - case Tegra::NvdecCommon::VideoCodec::H264: + case Tegra::Host1x::NvdecCommon::VideoCodec::H264: return h264_decoder->ComposeFrame(state, is_first_frame); - case Tegra::NvdecCommon::VideoCodec::VP8: + case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: return vp8_decoder->ComposeFrame(state); - case Tegra::NvdecCommon::VideoCodec::VP9: + case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: vp9_decoder->ComposeFrame(state); vp9_hidden_frame = vp9_decoder->WasFrameHidden(); return vp9_decoder->GetFrameBytes(); @@ -287,21 +287,21 @@ AVFramePtr Codec::GetCurrentFrame() { return frame; } -NvdecCommon::VideoCodec Codec::GetCurrentCodec() const { +Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const { return current_codec; } std::string_view Codec::GetCurrentCodecName() const { switch (current_codec) { - case NvdecCommon::VideoCodec::None: + case Host1x::NvdecCommon::VideoCodec::None: return "None"; - case NvdecCommon::VideoCodec::H264: + case Host1x::NvdecCommon::VideoCodec::H264: return "H264"; - case NvdecCommon::VideoCodec::VP8: + case Host1x::NvdecCommon::VideoCodec::VP8: return "VP8"; - case NvdecCommon::VideoCodec::H265: + case Host1x::NvdecCommon::VideoCodec::H265: return "H265"; - case NvdecCommon::VideoCodec::VP9: + case Host1x::NvdecCommon::VideoCodec::VP9: return "VP9"; default: return "Unknown"; diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/host1x/codecs/codec.h similarity index 78% rename from src/video_core/command_classes/codecs/codec.h rename to src/video_core/host1x/codecs/codec.h index 0c2405465c..117cb3ccd6 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h @@ -6,8 +6,8 @@ #include #include #include - -#include "video_core/command_classes/nvdec_common.h" +#include "common/common_types.h" +#include "video_core/host1x/nvdec_common.h" extern "C" { #if defined(__GNUC__) || defined(__clang__) @@ -34,14 +34,14 @@ class VP9; class Codec { public: - explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs); + explicit Codec(GPU& gpu, const Host1x::NvdecCommon::NvdecRegisters& regs); ~Codec(); /// Initialize the codec, returning success or failure void Initialize(); /// Sets NVDEC video stream codec - void SetTargetCodec(NvdecCommon::VideoCodec codec); + void SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec); /// Call decoders to construct headers, decode AVFrame with ffmpeg void Decode(); @@ -50,7 +50,7 @@ public: [[nodiscard]] AVFramePtr GetCurrentFrame(); /// Returns the value of current_codec - [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; + [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; /// Return name of the current codec [[nodiscard]] std::string_view GetCurrentCodecName() const; @@ -63,14 +63,14 @@ private: bool CreateGpuAvDevice(); bool initialized{}; - NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; + Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; const AVCodec* av_codec{nullptr}; AVCodecContext* av_codec_ctx{nullptr}; AVBufferRef* av_gpu_decoder{nullptr}; GPU& gpu; - const NvdecCommon::NvdecRegisters& state; + const Host1x::NvdecCommon::NvdecRegisters& state; std::unique_ptr h264_decoder; std::unique_ptr vp8_decoder; std::unique_ptr vp9_decoder; diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp similarity index 98% rename from src/video_core/command_classes/codecs/h264.cpp rename to src/video_core/host1x/codecs/h264.cpp index e2acd54d4f..95534bc85f 100644 --- a/src/video_core/command_classes/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -5,8 +5,8 @@ #include #include "common/settings.h" -#include "video_core/command_classes/codecs/h264.h" #include "video_core/gpu.h" +#include "video_core/host1x/codecs/h264.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -28,7 +28,7 @@ H264::H264(GPU& gpu_) : gpu(gpu_) {} H264::~H264() = default; -const std::vector& H264::ComposeFrame(const NvdecCommon::NvdecRegisters& state, +const std::vector& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame) { H264DecoderContext context; gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/host1x/codecs/h264.h similarity index 96% rename from src/video_core/command_classes/codecs/h264.h rename to src/video_core/host1x/codecs/h264.h index 261574364e..a98730474a 100644 --- a/src/video_core/command_classes/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h @@ -8,7 +8,7 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "video_core/command_classes/nvdec_common.h" +#include "video_core/host1x/nvdec_common.h" namespace Tegra { class GPU; @@ -59,8 +59,8 @@ public: ~H264(); /// Compose the H264 frame for FFmpeg decoding - [[nodiscard]] const std::vector& ComposeFrame(const NvdecCommon::NvdecRegisters& state, - bool is_first_frame = false); + [[nodiscard]] const std::vector& ComposeFrame( + const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); private: std::vector frame; diff --git a/src/video_core/command_classes/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp similarity index 93% rename from src/video_core/command_classes/codecs/vp8.cpp rename to src/video_core/host1x/codecs/vp8.cpp index c83b9bbc27..aac026e17c 100644 --- a/src/video_core/command_classes/codecs/vp8.cpp +++ b/src/video_core/host1x/codecs/vp8.cpp @@ -3,8 +3,8 @@ #include -#include "video_core/command_classes/codecs/vp8.h" #include "video_core/gpu.h" +#include "video_core/host1x/codecs/vp8.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -12,7 +12,7 @@ VP8::VP8(GPU& gpu_) : gpu(gpu_) {} VP8::~VP8() = default; -const std::vector& VP8::ComposeFrame(const NvdecCommon::NvdecRegisters& state) { +const std::vector& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { VP8PictureInfo info; gpu.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); diff --git a/src/video_core/command_classes/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h similarity index 93% rename from src/video_core/command_classes/codecs/vp8.h rename to src/video_core/host1x/codecs/vp8.h index 3357667b08..a1dfa5f03d 100644 --- a/src/video_core/command_classes/codecs/vp8.h +++ b/src/video_core/host1x/codecs/vp8.h @@ -8,7 +8,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" -#include "video_core/command_classes/nvdec_common.h" +#include "video_core/host1x/nvdec_common.h" namespace Tegra { class GPU; @@ -20,7 +20,8 @@ public: ~VP8(); /// Compose the VP8 frame for FFmpeg decoding - [[nodiscard]] const std::vector& ComposeFrame(const NvdecCommon::NvdecRegisters& state); + [[nodiscard]] const std::vector& ComposeFrame( + const Host1x::NvdecCommon::NvdecRegisters& state); private: std::vector frame; diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp similarity index 99% rename from src/video_core/command_classes/codecs/vp9.cpp rename to src/video_core/host1x/codecs/vp9.cpp index c01431441d..bc50c6ba41 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/host1x/codecs/vp9.cpp @@ -4,8 +4,8 @@ #include // for std::copy #include #include "common/assert.h" -#include "video_core/command_classes/codecs/vp9.h" #include "video_core/gpu.h" +#include "video_core/host1x/codecs/vp9.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -355,7 +355,7 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_ } } -Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { +Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) { PictureInfo picture_info; gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); Vp9PictureInfo vp9_info = picture_info.Convert(); @@ -376,7 +376,7 @@ void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { entropy.Convert(dst); } -Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) { +Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { Vp9FrameContainer current_frame{}; { gpu.SyncGuestHost(); @@ -769,7 +769,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { return uncomp_writer; } -void VP9::ComposeFrame(const NvdecCommon::NvdecRegisters& state) { +void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { std::vector bitstream; { Vp9FrameContainer curr_frame = GetCurrentFrame(state); diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h similarity index 93% rename from src/video_core/command_classes/codecs/vp9.h rename to src/video_core/host1x/codecs/vp9.h index ecc40e8b1b..a425c0fa40 100644 --- a/src/video_core/command_classes/codecs/vp9.h +++ b/src/video_core/host1x/codecs/vp9.h @@ -8,8 +8,8 @@ #include "common/common_types.h" #include "common/stream.h" -#include "video_core/command_classes/codecs/vp9_types.h" -#include "video_core/command_classes/nvdec_common.h" +#include "video_core/host1x/codecs/vp9_types.h" +#include "video_core/host1x/nvdec_common.h" namespace Tegra { class GPU; @@ -117,7 +117,7 @@ public: /// Composes the VP9 frame from the GPU state information. /// Based on the official VP9 spec documentation - void ComposeFrame(const NvdecCommon::NvdecRegisters& state); + void ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state); /// Returns true if the most recent frame was a hidden frame. [[nodiscard]] bool WasFrameHidden() const { @@ -162,13 +162,15 @@ private: void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob); /// Returns VP9 information from NVDEC provided offset and size - [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state); + [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo( + const Host1x::NvdecCommon::NvdecRegisters& state); /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct void InsertEntropy(u64 offset, Vp9EntropyProbs& dst); /// Returns frame to be decoded after buffering - [[nodiscard]] Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state); + [[nodiscard]] Vp9FrameContainer GetCurrentFrame( + const Host1x::NvdecCommon::NvdecRegisters& state); /// Use NVDEC providied information to compose the headers for the current frame [[nodiscard]] std::vector ComposeCompressedHeader(); diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h similarity index 100% rename from src/video_core/command_classes/codecs/vp9_types.h rename to src/video_core/host1x/codecs/vp9_types.h diff --git a/src/video_core/host1x/control.cpp b/src/video_core/host1x/control.cpp new file mode 100644 index 0000000000..b72b01aa33 --- /dev/null +++ b/src/video_core/host1x/control.cpp @@ -0,0 +1,35 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "video_core/gpu.h" +#include "video_core/host1x/control.h" +#include "video_core/host1x/host1x.h" + +namespace Tegra::Host1x { + +Control::Control(GPU& gpu_) : gpu(gpu_) {} + +Control::~Control() = default; + +void Control::ProcessMethod(Method method, u32 argument) { + switch (method) { + case Method::LoadSyncptPayload32: + syncpoint_value = argument; + break; + case Method::WaitSyncpt: + case Method::WaitSyncpt32: + Execute(argument); + break; + default: + UNIMPLEMENTED_MSG("Control method 0x{:X}", static_cast(method)); + break; + } +} + +void Control::Execute(u32 data) { + gpu.Host1x().GetSyncpointManager().WaitHost(data, syncpoint_value); +} + +} // namespace Tegra::Host1x diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/host1x/control.h similarity index 60% rename from src/video_core/command_classes/host1x.h rename to src/video_core/host1x/control.h index bb48a4381e..04dac7d514 100644 --- a/src/video_core/command_classes/host1x.h +++ b/src/video_core/host1x/control.h @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once @@ -7,9 +9,12 @@ namespace Tegra { class GPU; + +namespace Host1x { + class Nvdec; -class Host1x { +class Control { public: enum class Method : u32 { WaitSyncpt = 0x8, @@ -17,8 +22,8 @@ public: WaitSyncpt32 = 0x50, }; - explicit Host1x(GPU& gpu); - ~Host1x(); + explicit Control(GPU& gpu); + ~Control(); /// Writes the method into the state, Invoke Execute() if encountered void ProcessMethod(Method method, u32 argument); @@ -31,4 +36,6 @@ private: GPU& gpu; }; +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h new file mode 100644 index 0000000000..2971be2861 --- /dev/null +++ b/src/video_core/host1x/host1x.h @@ -0,0 +1,33 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "video_core/host1x/syncpoint_manager.h" + +namespace Tegra { + +namespace Host1x { + +class Host1x { +public: + Host1x() : syncpoint_manager{} {} + + SyncpointManager& GetSyncpointManager() { + return syncpoint_manager; + } + + const SyncpointManager& GetSyncpointManager() const { + return syncpoint_manager; + } + +private: + SyncpointManager syncpoint_manager; +}; + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/host1x/nvdec.cpp similarity index 92% rename from src/video_core/command_classes/nvdec.cpp rename to src/video_core/host1x/nvdec.cpp index 4fbbe3da62..5f6decd0de 100644 --- a/src/video_core/command_classes/nvdec.cpp +++ b/src/video_core/host1x/nvdec.cpp @@ -2,10 +2,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" -#include "video_core/command_classes/nvdec.h" #include "video_core/gpu.h" +#include "video_core/host1x/nvdec.h" -namespace Tegra { +namespace Tegra::Host1x { #define NVDEC_REG_INDEX(field_name) \ (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64)) @@ -44,4 +44,4 @@ void Nvdec::Execute() { } } -} // namespace Tegra +} // namespace Tegra::Host1x diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/host1x/nvdec.h similarity index 88% rename from src/video_core/command_classes/nvdec.h rename to src/video_core/host1x/nvdec.h index 488531fc6b..41ba1f7a04 100644 --- a/src/video_core/command_classes/nvdec.h +++ b/src/video_core/host1x/nvdec.h @@ -6,11 +6,13 @@ #include #include #include "common/common_types.h" -#include "video_core/command_classes/codecs/codec.h" +#include "video_core/host1x/codecs/codec.h" namespace Tegra { class GPU; +namespace Host1x { + class Nvdec { public: explicit Nvdec(GPU& gpu); @@ -30,4 +32,7 @@ private: NvdecCommon::NvdecRegisters state; std::unique_ptr codec; }; + +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/command_classes/nvdec_common.h b/src/video_core/host1x/nvdec_common.h similarity index 98% rename from src/video_core/command_classes/nvdec_common.h rename to src/video_core/host1x/nvdec_common.h index 521e5b52b7..49d67ebbe5 100644 --- a/src/video_core/command_classes/nvdec_common.h +++ b/src/video_core/host1x/nvdec_common.h @@ -7,7 +7,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" -namespace Tegra::NvdecCommon { +namespace Tegra::Host1x::NvdecCommon { enum class VideoCodec : u64 { None = 0x0, @@ -94,4 +94,4 @@ ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176); #undef ASSERT_REG_POSITION -} // namespace Tegra::NvdecCommon +} // namespace Tegra::Host1x::NvdecCommon diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/host1x/sync_manager.cpp similarity index 77% rename from src/video_core/command_classes/sync_manager.cpp rename to src/video_core/host1x/sync_manager.cpp index 67e58046fa..8694f77e2a 100644 --- a/src/video_core/command_classes/sync_manager.cpp +++ b/src/video_core/host1x/sync_manager.cpp @@ -4,8 +4,12 @@ #include #include "sync_manager.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Tegra { +namespace Host1x { + SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {} SyncptIncrManager::~SyncptIncrManager() = default; @@ -36,8 +40,12 @@ void SyncptIncrManager::IncrementAllDone() { if (!increments[done_count].complete) { break; } - gpu.IncrementSyncPoint(increments[done_count].syncpt_id); + auto& syncpoint_manager = gpu.Host1x().GetSyncpointManager(); + syncpoint_manager.IncrementGuest(increments[done_count].syncpt_id); + syncpoint_manager.IncrementHost(increments[done_count].syncpt_id); } increments.erase(increments.begin(), increments.begin() + done_count); } + +} // namespace Host1x } // namespace Tegra diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/host1x/sync_manager.h similarity index 95% rename from src/video_core/command_classes/sync_manager.h rename to src/video_core/host1x/sync_manager.h index 6dfaae0808..aba72d5c58 100644 --- a/src/video_core/command_classes/sync_manager.h +++ b/src/video_core/host1x/sync_manager.h @@ -8,7 +8,11 @@ #include "common/common_types.h" namespace Tegra { + class GPU; + +namespace Host1x { + struct SyncptIncr { u32 id; u32 class_id; @@ -44,4 +48,6 @@ private: GPU& gpu; }; +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp new file mode 100644 index 0000000000..c606b8bd0d --- /dev/null +++ b/src/video_core/host1x/syncpoint_manager.cpp @@ -0,0 +1,93 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#include "video_core/host1x/syncpoint_manager.h" + +namespace Tegra { + +namespace Host1x { + +SyncpointManager::ActionHandle SyncpointManager::RegisterAction( + std::atomic& syncpoint, std::list& action_storage, u32 expected_value, + std::function& action) { + if (syncpoint.load(std::memory_order_acquire) >= expected_value) { + action(); + return {}; + } + + std::unique_lock lk(guard); + if (syncpoint.load(std::memory_order_relaxed) >= expected_value) { + action(); + return {}; + } + auto it = action_storage.begin(); + while (it != action_storage.end()) { + if (it->expected_value >= expected_value) { + break; + } + ++it; + } + return action_storage.emplace(it, expected_value, action); +} + +void SyncpointManager::DeregisterAction(std::list& action_storage, + ActionHandle& handle) { + std::unique_lock lk(guard); + action_storage.erase(handle); +} + +void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) { + DeregisterAction(guest_action_storage[syncpoint_id], handle); +} + +void SyncpointManager::DeregisterHostAction(u32 syncpoint_id, ActionHandle& handle) { + DeregisterAction(host_action_storage[syncpoint_id], handle); +} + +void SyncpointManager::IncrementGuest(u32 syncpoint_id) { + Increment(syncpoints_guest[syncpoint_id], wait_guest_cv, guest_action_storage[syncpoint_id]); +} + +void SyncpointManager::IncrementHost(u32 syncpoint_id) { + Increment(syncpoints_host[syncpoint_id], wait_host_cv, host_action_storage[syncpoint_id]); +} + +void SyncpointManager::WaitGuest(u32 syncpoint_id, u32 expected_value) { + Wait(syncpoints_guest[syncpoint_id], wait_guest_cv, expected_value); +} + +void SyncpointManager::WaitHost(u32 syncpoint_id, u32 expected_value) { + Wait(syncpoints_host[syncpoint_id], wait_host_cv, expected_value); +} + +void SyncpointManager::Increment(std::atomic& syncpoint, std::condition_variable& wait_cv, + std::list& action_storage) { + auto new_value{syncpoint.fetch_add(1, std::memory_order_acq_rel) + 1}; + + std::unique_lock lk(guard); + auto it = action_storage.begin(); + while (it != action_storage.end()) { + if (it->expected_value > new_value) { + break; + } + it->action(); + it = action_storage.erase(it); + } + wait_cv.notify_all(); +} + +void SyncpointManager::Wait(std::atomic& syncpoint, std::condition_variable& wait_cv, + u32 expected_value) { + const auto pred = [&]() { return syncpoint.load(std::memory_order_acquire) >= expected_value; }; + if (pred()) { + return; + } + + std::unique_lock lk(guard); + wait_cv.wait(lk, pred); +} + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h new file mode 100644 index 0000000000..0ecc040ab8 --- /dev/null +++ b/src/video_core/host1x/syncpoint_manager.h @@ -0,0 +1,99 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Tegra { + +namespace Host1x { + +class SyncpointManager { +public: + u32 GetGuestSyncpointValue(u32 id) { + return syncpoints_guest[id].load(std::memory_order_acquire); + } + + u32 GetHostSyncpointValue(u32 id) { + return syncpoints_host[id].load(std::memory_order_acquire); + } + + struct RegisteredAction { + RegisteredAction(u32 expected_value_, std::function& action_) + : expected_value{expected_value_}, action{action_} {} + u32 expected_value; + std::function action; + }; + using ActionHandle = std::list::iterator; + + template + ActionHandle RegisterGuestAction(u32 syncpoint_id, u32 expected_value, Func&& action) { + std::function func(action); + return RegisterAction(syncpoints_guest[syncpoint_id], guest_action_storage[syncpoint_id], + expected_value, func); + } + + template + ActionHandle RegisterHostAction(u32 syncpoint_id, u32 expected_value, Func&& action) { + std::function func(action); + return RegisterAction(syncpoints_host[syncpoint_id], host_action_storage[syncpoint_id], + expected_value, func); + } + + void DeregisterGuestAction(u32 syncpoint_id,ActionHandle& handle); + + void DeregisterHostAction(u32 syncpoint_id,ActionHandle& handle); + + void IncrementGuest(u32 syncpoint_id); + + void IncrementHost(u32 syncpoint_id); + + void WaitGuest(u32 syncpoint_id, u32 expected_value); + + void WaitHost(u32 syncpoint_id, u32 expected_value); + + bool IsReadyGuest(u32 syncpoint_id, u32 expected_value) { + return syncpoints_guest[syncpoint_id].load(std::memory_order_acquire) >= expected_value; + } + + bool IsReadyHost(u32 syncpoint_id, u32 expected_value) { + return syncpoints_host[syncpoint_id].load(std::memory_order_acquire) >= expected_value; + } + +private: + void Increment(std::atomic& syncpoint, std::condition_variable& wait_cv, + std::list& action_storage); + + ActionHandle RegisterAction(std::atomic& syncpoint, + std::list& action_storage, u32 expected_value, + std::function& action); + + void DeregisterAction(std::list& action_storage, ActionHandle& handle); + + void Wait(std::atomic& syncpoint, std::condition_variable& wait_cv, u32 expected_value); + + static constexpr size_t NUM_MAX_SYNCPOINTS = 192; + + std::array, NUM_MAX_SYNCPOINTS> syncpoints_guest{}; + std::array, NUM_MAX_SYNCPOINTS> syncpoints_host{}; + + std::array, NUM_MAX_SYNCPOINTS> guest_action_storage; + std::array, NUM_MAX_SYNCPOINTS> host_action_storage; + + std::mutex guard; + std::condition_variable wait_guest_cv; + std::condition_variable wait_host_cv; +}; + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/host1x/vic.cpp similarity index 98% rename from src/video_core/command_classes/vic.cpp rename to src/video_core/host1x/vic.cpp index 7c17df353a..a9422670a1 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -18,14 +18,17 @@ extern "C" { #include "common/bit_field.h" #include "common/logging/log.h" -#include "video_core/command_classes/nvdec.h" -#include "video_core/command_classes/vic.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" +#include "video_core/host1x/nvdec.h" +#include "video_core/host1x/vic.h" #include "video_core/memory_manager.h" #include "video_core/textures/decoders.h" namespace Tegra { + +namespace Host1x { + namespace { enum class VideoPixelFormat : u64_le { RGBA8 = 0x1f, @@ -235,4 +238,6 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { chroma_buffer.size()); } +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/command_classes/vic.h b/src/video_core/host1x/vic.h similarity index 93% rename from src/video_core/command_classes/vic.h rename to src/video_core/host1x/vic.h index 010daa6b64..c51f8af7e9 100644 --- a/src/video_core/command_classes/vic.h +++ b/src/video_core/host1x/vic.h @@ -11,6 +11,9 @@ struct SwsContext; namespace Tegra { class GPU; + +namespace Host1x { + class Nvdec; union VicConfig; @@ -40,7 +43,7 @@ private: void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); GPU& gpu; - std::shared_ptr nvdec_processor; + std::shared_ptr nvdec_processor; /// Avoid reallocation of the following buffers every frame, as their /// size does not change during a stream @@ -58,4 +61,6 @@ private: s32 scaler_height{}; }; +} // namespace Host1x + } // namespace Tegra From 2931101e6f5aa755566ef40f6e6dc71909fd3e92 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 30 Jan 2022 22:26:01 +0100 Subject: [PATCH 069/245] NVDRV: Refactor Host1x --- src/core/core.cpp | 2 +- src/core/hle/service/nvdrv/core/container.cpp | 8 ++-- src/core/hle/service/nvdrv/core/container.h | 10 +++-- src/core/hle/service/nvdrv/core/nvmap.cpp | 33 +++++++---------- src/core/hle/service/nvdrv/core/nvmap.h | 18 +++++++-- .../service/nvdrv/core/syncpoint_manager.cpp | 6 +-- .../service/nvdrv/core/syncpoint_manager.h | 12 ++++-- .../nvdrv/devices/nvhost_nvdec_common.cpp | 37 ++++++------------- src/core/hle/service/nvdrv/nvdrv.cpp | 2 +- src/video_core/CMakeLists.txt | 2 + src/video_core/cdma_pusher.cpp | 12 +++--- src/video_core/cdma_pusher.h | 7 ++-- src/video_core/gpu.cpp | 27 +------------- src/video_core/gpu.h | 6 --- src/video_core/host1x/codecs/codec.cpp | 10 ++--- src/video_core/host1x/codecs/codec.h | 9 +++-- src/video_core/host1x/codecs/h264.cpp | 13 ++++--- src/video_core/host1x/codecs/h264.h | 10 +++-- src/video_core/host1x/codecs/vp8.cpp | 8 ++-- src/video_core/host1x/codecs/vp8.h | 10 +++-- src/video_core/host1x/codecs/vp9.cpp | 12 +++--- src/video_core/host1x/codecs/vp9.h | 10 +++-- src/video_core/host1x/codecs/vp9_types.h | 1 - src/video_core/host1x/control.cpp | 5 +-- src/video_core/host1x/control.h | 6 +-- src/video_core/host1x/host1x.cpp | 18 +++++++++ src/video_core/host1x/host1x.h | 27 +++++++++++++- src/video_core/host1x/nvdec.cpp | 5 ++- src/video_core/host1x/nvdec.h | 7 ++-- src/video_core/host1x/sync_manager.cpp | 5 +-- src/video_core/host1x/sync_manager.h | 8 ++-- src/video_core/host1x/vic.cpp | 22 +++++------ src/video_core/host1x/vic.h | 6 +-- 33 files changed, 201 insertions(+), 173 deletions(-) create mode 100644 src/video_core/host1x/host1x.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index fa059a3948..13d02e75f5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -216,7 +216,7 @@ struct System::Impl { telemetry_session = std::make_unique(); - host1x_core = std::make_unique(); + host1x_core = std::make_unique(system); gpu_core = VideoCore::CreateGPU(emu_window, system); if (!gpu_core) { return SystemResultStatus::ErrorVideoCore; diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index 97b5b2c86f..fbd66f0016 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -6,18 +6,18 @@ #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::NvCore { struct ContainerImpl { - ContainerImpl(Tegra::GPU& gpu_) : file{}, manager{gpu_} {} + ContainerImpl(Tegra::Host1x::Host1x& host1x_) : file{host1x_}, manager{host1x_} {} NvMap file; SyncpointManager manager; }; -Container::Container(Tegra::GPU& gpu_) { - impl = std::make_unique(gpu_); +Container::Container(Tegra::Host1x::Host1x& host1x_) { + impl = std::make_unique(host1x_); } Container::~Container() = default; diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index 91ac2305ab..da75d74ff7 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -8,8 +8,12 @@ #include namespace Tegra { -class GPU; -} + +namespace Host1x { +class Host1x; +} // namespace Host1x + +} // namespace Tegra namespace Service::Nvidia::NvCore { @@ -20,7 +24,7 @@ struct ContainerImpl; class Container { public: - Container(Tegra::GPU& gpu_); + Container(Tegra::Host1x::Host1x& host1x); ~Container(); NvMap& GetNvMapFile(); diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 1126daeb5e..9acec7ba66 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/memory.h" +#include "video_core/host1x/host1x.h" using Core::Memory::YUZU_PAGESIZE; @@ -61,7 +62,7 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) { return NvResult::Success; } -NvMap::NvMap() = default; +NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} void NvMap::AddHandle(std::shared_ptr handle_description) { std::scoped_lock lock(handles_lock); @@ -77,12 +78,11 @@ void NvMap::UnmapHandle(Handle& handle_description) { } // Free and unmap the handle from the SMMU - /* - state.soc->smmu.Unmap(handle_description.pin_virt_address, - static_cast(handle_description.aligned_size)); - smmuAllocator.Free(handle_description.pin_virt_address, - static_cast(handle_description.aligned_size)); handle_description.pin_virt_address = 0; - */ + host1x.MemoryManager().Unmap(static_cast(handle_description.pin_virt_address), + handle_description.aligned_size); + host1x.Allocator().Free(handle_description.pin_virt_address, + static_cast(handle_description.aligned_size)); + handle_description.pin_virt_address = 0; } bool NvMap::TryRemoveHandle(const Handle& handle_description) { @@ -131,12 +131,9 @@ VAddr NvMap::GetHandleAddress(Handle::Id handle) { } u32 NvMap::PinHandle(NvMap::Handle::Id handle) { - UNIMPLEMENTED_MSG("pinning"); - return 0; - /* auto handle_description{GetHandle(handle)}; - if (!handle_description) - [[unlikely]] return 0; + if (!handle_description) [[unlikely]] + return 0; std::scoped_lock lock(handle_description->mutex); if (!handle_description->pins) { @@ -157,8 +154,10 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { // If not then allocate some space and map it u32 address{}; + auto& smmu_allocator = host1x.Allocator(); + auto& smmu_memory_manager = host1x.MemoryManager(); while (!(address = - smmuAllocator.Allocate(static_cast(handle_description->aligned_size)))) { + smmu_allocator.Allocate(static_cast(handle_description->aligned_size)))) { // Free handles until the allocation succeeds std::scoped_lock queueLock(unmap_queue_lock); if (auto freeHandleDesc{unmap_queue.front()}) { @@ -172,19 +171,16 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { } } - state.soc->smmu.Map(address, handle_description->GetPointer(), - static_cast(handle_description->aligned_size)); + smmu_memory_manager.Map(static_cast(address), handle_description->address, + handle_description->aligned_size); handle_description->pin_virt_address = address; } handle_description->pins++; return handle_description->pin_virt_address; - */ } void NvMap::UnpinHandle(Handle::Id handle) { - UNIMPLEMENTED_MSG("Unpinning"); - /* auto handle_description{GetHandle(handle)}; if (!handle_description) return; @@ -199,7 +195,6 @@ void NvMap::UnpinHandle(Handle::Id handle) { unmap_queue.push_back(handle_description); handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); } - */ } std::optional NvMap::FreeHandle(Handle::Id handle, bool internal_session) { diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index 5e6c73589a..5acdc961e6 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -15,6 +15,14 @@ #include "common/common_types.h" #include "core/hle/service/nvdrv/nvdata.h" +namespace Tegra { + +namespace Host1x { +class Host1x; +} // namespace Host1x + +} // namespace Tegra + namespace Service::Nvidia::NvCore { /** * @brief The nvmap core class holds the global state for nvmap and provides methods to manage @@ -90,15 +98,17 @@ public: }; private: - std::list> unmap_queue; - std::mutex unmap_queue_lock; //!< Protects access to `unmap_queue` + std::list> unmap_queue{}; + std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` - std::unordered_map> handles; //!< Main owning map of handles + std::unordered_map> + handles{}; //!< Main owning map of handles std::mutex handles_lock; //!< Protects access to `handles` static constexpr u32 HandleIdIncrement{ 4}; //!< Each new handle ID is an increment of 4 from the previous std::atomic next_handle_id{HandleIdIncrement}; + Tegra::Host1x::Host1x& host1x; void AddHandle(std::shared_ptr handle); @@ -125,7 +135,7 @@ public: bool was_uncached; //!< If the handle was allocated as uncached }; - NvMap(); + NvMap(Tegra::Host1x::Host1x& host1x); /** * @brief Creates an unallocated handle of the given size diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index ff6cbb37e5..61e00448c9 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -3,16 +3,16 @@ #include "common/assert.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::NvCore { -SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} +SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} SyncpointManager::~SyncpointManager() = default; u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { - syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); + syncpoints[syncpoint_id].min = host1x.GetSyncpointManager().GetHostSyncpointValue(syncpoint_id); return GetSyncpointMin(syncpoint_id); } diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h index cf7f0b4bee..f332edc6e4 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -10,14 +10,18 @@ #include "core/hle/service/nvdrv/nvdata.h" namespace Tegra { -class GPU; -} + +namespace Host1x { +class Host1x; +} // namespace Host1x + +} // namespace Tegra namespace Service::Nvidia::NvCore { class SyncpointManager final { public: - explicit SyncpointManager(Tegra::GPU& gpu_); + explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); ~SyncpointManager(); /** @@ -78,7 +82,7 @@ private: std::array syncpoints{}; - Tegra::GPU& gpu; + Tegra::Host1x::Host1x& host1x; }; } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 77e6a1cd64..b17589aa3e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -13,6 +13,7 @@ #include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #include "core/memory.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" @@ -140,29 +141,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); - auto& gpu = system.GPU(); - for (auto& cmd_buffer : cmd_buffer_handles) { - auto object{nvmap.GetHandle(cmd_buffer.map_handle)}; - if (!object) { - LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - if (object->dma_map_addr == 0) { - // NVDEC and VIC memory is in the 32-bit address space - // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space - const GPUVAddr low_addr = - gpu.MemoryManager().MapAllocate32(object->address, object->size); - object->dma_map_addr = static_cast(low_addr); - // Ensure that the dma_map_addr is indeed in the lower 32-bit address space. - ASSERT(object->dma_map_addr == low_addr); - } - if (!object->dma_map_addr) { - LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); - } else { - cmd_buffer.map_address = static_cast(object->dma_map_addr); - } + cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle); } std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), @@ -172,11 +152,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto } NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector& input, std::vector& output) { - // This is intntionally stubbed. - // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame - // addresses, and risk invalidating data before the async GPU thread is done with it + IoctlMapBuffer params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); + std::vector cmd_buffer_handles(params.num_entries); + + SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); + for (auto& cmd_buffer : cmd_buffer_handles) { + nvmap.UnpinHandle(cmd_buffer.map_handle); + } + std::memset(output.data(), 0, output.size()); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index b39a4c6db5..8a9f3c7174 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -71,7 +71,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} { + : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { std::shared_ptr device = std::make_shared(system, *this, container); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 723f9b67ca..40e6d1ec48 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -56,6 +56,8 @@ add_library(video_core STATIC host1x/codecs/vp9_types.h host1x/control.cpp host1x/control.h + host1x/host1x.cpp + host1x/host1x.h host1x/nvdec.cpp host1x/nvdec.h host1x/nvdec_common.h diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 148126347a..28a2d2090d 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -4,8 +4,8 @@ #include #include "video_core/cdma_pusher.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" #include "video_core/host1x/control.h" +#include "video_core/host1x/host1x.h" #include "video_core/host1x/nvdec.h" #include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/sync_manager.h" @@ -13,11 +13,11 @@ #include "video_core/memory_manager.h" namespace Tegra { -CDmaPusher::CDmaPusher(GPU& gpu_) - : gpu{gpu_}, nvdec_processor(std::make_shared(gpu)), - vic_processor(std::make_unique(gpu, nvdec_processor)), - host1x_processor(std::make_unique(gpu)), - sync_manager(std::make_unique(gpu)) {} +CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_) + : host1x{host1x_}, nvdec_processor(std::make_shared(host1x)), + vic_processor(std::make_unique(host1x, nvdec_processor)), + host1x_processor(std::make_unique(host1x)), + sync_manager(std::make_unique(host1x)) {} CDmaPusher::~CDmaPusher() = default; diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index de17c20821..83112dfce4 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h @@ -12,10 +12,9 @@ namespace Tegra { -class GPU; - namespace Host1x { class Control; +class Host1x; class Nvdec; class SyncptIncrManager; class Vic; @@ -91,7 +90,7 @@ enum class ThiMethod : u32 { class CDmaPusher { public: - explicit CDmaPusher(GPU& gpu_); + explicit CDmaPusher(Host1x::Host1x& host1x); ~CDmaPusher(); /// Process the command entry @@ -104,7 +103,7 @@ private: /// Write arguments value to the ThiRegisters member at the specified offset void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument); - GPU& gpu; + Host1x::Host1x& host1x; std::shared_ptr nvdec_processor; std::unique_ptr vic_processor; std::unique_ptr host1x_processor; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 1097db08ae..e05c9a3573 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -83,19 +83,11 @@ struct GPU::Impl { UNIMPLEMENTED(); } - void CreateHost1xChannel() { - if (host1x_channel) { - return; - } - host1x_channel = CreateChannel(0); - host1x_channel->memory_manager = std::make_shared(system); - InitChannel(*host1x_channel); - } - /// Binds a renderer to the GPU. void BindRenderer(std::unique_ptr renderer_) { renderer = std::move(renderer_); rasterizer = renderer->ReadRasterizer(); + host1x.MemoryManager().BindRasterizer(rasterizer); } /// Flush all current written commands into the host GPU for execution. @@ -173,12 +165,6 @@ struct GPU::Impl { return *current_channel->kepler_compute; } - /// Returns a reference to the GPU memory manager. - [[nodiscard]] Tegra::MemoryManager& MemoryManager() { - CreateHost1xChannel(); - return *host1x_channel->memory_manager; - } - /// Returns a reference to the GPU DMA pusher. [[nodiscard]] Tegra::DmaPusher& DmaPusher() { ASSERT(current_channel); @@ -299,7 +285,7 @@ struct GPU::Impl { } if (!cdma_pushers.contains(id)) { - cdma_pushers.insert_or_assign(id, std::make_unique(gpu)); + cdma_pushers.insert_or_assign(id, std::make_unique(host1x)); } // SubmitCommandBuffer would make the nvdec operations async, this is not currently working @@ -389,7 +375,6 @@ struct GPU::Impl { VideoCore::RasterizerInterface* rasterizer = nullptr; const bool use_nvdec; - std::shared_ptr host1x_channel; s32 new_channel_id{1}; /// Shader build notifier std::unique_ptr shader_notify; @@ -510,14 +495,6 @@ const Engines::KeplerCompute& GPU::KeplerCompute() const { return impl->KeplerCompute(); } -Tegra::MemoryManager& GPU::MemoryManager() { - return impl->MemoryManager(); -} - -const Tegra::MemoryManager& GPU::MemoryManager() const { - return impl->MemoryManager(); -} - Tegra::DmaPusher& GPU::DmaPusher() { return impl->DmaPusher(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index c1a5382572..f04edf5c49 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -153,12 +153,6 @@ public: /// Returns a reference to the KeplerCompute GPU engine. [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const; - /// Returns a reference to the GPU memory manager. - [[nodiscard]] Tegra::MemoryManager& MemoryManager(); - - /// Returns a const reference to the GPU memory manager. - [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const; - /// Returns a reference to the GPU DMA pusher. [[nodiscard]] Tegra::DmaPusher& DmaPusher(); diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index 70c47ae03d..42e7d6e4fb 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp @@ -6,11 +6,11 @@ #include #include "common/assert.h" #include "common/settings.h" -#include "video_core/gpu.h" #include "video_core/host1x/codecs/codec.h" #include "video_core/host1x/codecs/h264.h" #include "video_core/host1x/codecs/vp8.h" #include "video_core/host1x/codecs/vp9.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" extern "C" { @@ -73,10 +73,10 @@ void AVFrameDeleter(AVFrame* ptr) { av_frame_free(&ptr); } -Codec::Codec(GPU& gpu_, const Host1x::NvdecCommon::NvdecRegisters& regs) - : gpu(gpu_), state{regs}, h264_decoder(std::make_unique(gpu)), - vp8_decoder(std::make_unique(gpu)), - vp9_decoder(std::make_unique(gpu)) {} +Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) + : host1x(host1x_), state{regs}, h264_decoder(std::make_unique(host1x)), + vp8_decoder(std::make_unique(host1x)), + vp9_decoder(std::make_unique(host1x)) {} Codec::~Codec() { if (!initialized) { diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h index 117cb3ccd6..0d45fb7fe7 100644 --- a/src/video_core/host1x/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h @@ -21,7 +21,6 @@ extern "C" { } namespace Tegra { -class GPU; void AVFrameDeleter(AVFrame* ptr); using AVFramePtr = std::unique_ptr; @@ -32,9 +31,13 @@ class VP8; class VP9; } // namespace Decoder +namespace Host1x { +class Host1x; +} // namespace Host1x + class Codec { public: - explicit Codec(GPU& gpu, const Host1x::NvdecCommon::NvdecRegisters& regs); + explicit Codec(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs); ~Codec(); /// Initialize the codec, returning success or failure @@ -69,7 +72,7 @@ private: AVCodecContext* av_codec_ctx{nullptr}; AVBufferRef* av_gpu_decoder{nullptr}; - GPU& gpu; + Host1x::Host1x& host1x; const Host1x::NvdecCommon::NvdecRegisters& state; std::unique_ptr h264_decoder; std::unique_ptr vp8_decoder; diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index 95534bc85f..e87bd65fa5 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -5,8 +5,8 @@ #include #include "common/settings.h" -#include "video_core/gpu.h" #include "video_core/host1x/codecs/h264.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -24,19 +24,20 @@ constexpr std::array zig_zag_scan{ }; } // Anonymous namespace -H264::H264(GPU& gpu_) : gpu(gpu_) {} +H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} H264::~H264() = default; const std::vector& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame) { H264DecoderContext context; - gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); + host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, + sizeof(H264DecoderContext)); const s64 frame_number = context.h264_parameter_set.frame_number.Value(); if (!is_first_frame && frame_number != 0) { frame.resize(context.stream_len); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); return frame; } @@ -155,8 +156,8 @@ const std::vector& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegist frame.resize(encoded_header.size() + context.stream_len); std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, - frame.data() + encoded_header.size(), context.stream_len); + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, + frame.data() + encoded_header.size(), context.stream_len); return frame; } diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h index a98730474a..5cc86454e4 100644 --- a/src/video_core/host1x/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h @@ -11,7 +11,11 @@ #include "video_core/host1x/nvdec_common.h" namespace Tegra { -class GPU; + +namespace Host1x { +class Host1x; +} // namespace Host1x + namespace Decoder { class H264BitWriter { @@ -55,7 +59,7 @@ private: class H264 { public: - explicit H264(GPU& gpu); + explicit H264(Host1x::Host1x& host1x); ~H264(); /// Compose the H264 frame for FFmpeg decoding @@ -64,7 +68,7 @@ public: private: std::vector frame; - GPU& gpu; + Host1x::Host1x& host1x; struct H264ParameterSet { s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00 diff --git a/src/video_core/host1x/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp index aac026e17c..28fb12cb8e 100644 --- a/src/video_core/host1x/codecs/vp8.cpp +++ b/src/video_core/host1x/codecs/vp8.cpp @@ -3,18 +3,18 @@ #include -#include "video_core/gpu.h" #include "video_core/host1x/codecs/vp8.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { -VP8::VP8(GPU& gpu_) : gpu(gpu_) {} +VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {} VP8::~VP8() = default; const std::vector& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { VP8PictureInfo info; - gpu.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); + host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); const bool is_key_frame = info.key_frame == 1u; const auto bitstream_size = static_cast(info.vld_buffer_size); @@ -45,7 +45,7 @@ const std::vector& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegiste frame[9] = static_cast(((info.frame_height >> 8) & 0x3f)); } const u64 bitstream_offset = state.frame_bitstream_offset; - gpu.MemoryManager().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size); + host1x.MemoryManager().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size); return frame; } diff --git a/src/video_core/host1x/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h index a1dfa5f03d..5bf07ecab1 100644 --- a/src/video_core/host1x/codecs/vp8.h +++ b/src/video_core/host1x/codecs/vp8.h @@ -11,12 +11,16 @@ #include "video_core/host1x/nvdec_common.h" namespace Tegra { -class GPU; + +namespace Host1x { +class Host1x; +} // namespace Host1x + namespace Decoder { class VP8 { public: - explicit VP8(GPU& gpu); + explicit VP8(Host1x::Host1x& host1x); ~VP8(); /// Compose the VP8 frame for FFmpeg decoding @@ -25,7 +29,7 @@ public: private: std::vector frame; - GPU& gpu; + Host1x::Host1x& host1x; struct VP8PictureInfo { INSERT_PADDING_WORDS_NOINIT(14); diff --git a/src/video_core/host1x/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp index bc50c6ba41..667aadc6a9 100644 --- a/src/video_core/host1x/codecs/vp9.cpp +++ b/src/video_core/host1x/codecs/vp9.cpp @@ -4,8 +4,8 @@ #include // for std::copy #include #include "common/assert.h" -#include "video_core/gpu.h" #include "video_core/host1x/codecs/vp9.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -236,7 +236,7 @@ constexpr std::array map_lut{ } } // Anonymous namespace -VP9::VP9(GPU& gpu_) : gpu{gpu_} {} +VP9::VP9(Host1x::Host1x& host1x_) : host1x{host1x_} {} VP9::~VP9() = default; @@ -357,7 +357,7 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_ Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) { PictureInfo picture_info; - gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); + host1x.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); Vp9PictureInfo vp9_info = picture_info.Convert(); InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy); @@ -372,17 +372,17 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { EntropyProbs entropy; - gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); + host1x.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); entropy.Convert(dst); } Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { Vp9FrameContainer current_frame{}; { - gpu.SyncGuestHost(); + // gpu.SyncGuestHost(); epic, why? current_frame.info = GetVp9PictureInfo(state); current_frame.bit_stream.resize(current_frame.info.bitstream_size); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), current_frame.info.bitstream_size); } if (!next_frame.bit_stream.empty()) { diff --git a/src/video_core/host1x/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h index a425c0fa40..d4083e8d3e 100644 --- a/src/video_core/host1x/codecs/vp9.h +++ b/src/video_core/host1x/codecs/vp9.h @@ -12,7 +12,11 @@ #include "video_core/host1x/nvdec_common.h" namespace Tegra { -class GPU; + +namespace Host1x { +class Host1x; +} // namespace Host1x + namespace Decoder { /// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the @@ -106,7 +110,7 @@ private: class VP9 { public: - explicit VP9(GPU& gpu_); + explicit VP9(Host1x::Host1x& host1x); ~VP9(); VP9(const VP9&) = delete; @@ -176,7 +180,7 @@ private: [[nodiscard]] std::vector ComposeCompressedHeader(); [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); - GPU& gpu; + Host1x::Host1x& host1x; std::vector frame; std::array loop_filter_ref_deltas{}; diff --git a/src/video_core/host1x/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h index bb3d8df6e1..adad8ed7ed 100644 --- a/src/video_core/host1x/codecs/vp9_types.h +++ b/src/video_core/host1x/codecs/vp9_types.h @@ -9,7 +9,6 @@ #include "common/common_types.h" namespace Tegra { -class GPU; namespace Decoder { struct Vp9FrameDimensions { diff --git a/src/video_core/host1x/control.cpp b/src/video_core/host1x/control.cpp index b72b01aa33..a81c635aec 100644 --- a/src/video_core/host1x/control.cpp +++ b/src/video_core/host1x/control.cpp @@ -3,13 +3,12 @@ // Refer to the license.txt file included. #include "common/assert.h" -#include "video_core/gpu.h" #include "video_core/host1x/control.h" #include "video_core/host1x/host1x.h" namespace Tegra::Host1x { -Control::Control(GPU& gpu_) : gpu(gpu_) {} +Control::Control(Host1x& host1x_) : host1x(host1x_) {} Control::~Control() = default; @@ -29,7 +28,7 @@ void Control::ProcessMethod(Method method, u32 argument) { } void Control::Execute(u32 data) { - gpu.Host1x().GetSyncpointManager().WaitHost(data, syncpoint_value); + host1x.GetSyncpointManager().WaitHost(data, syncpoint_value); } } // namespace Tegra::Host1x diff --git a/src/video_core/host1x/control.h b/src/video_core/host1x/control.h index 04dac7d514..18a9b56c0e 100644 --- a/src/video_core/host1x/control.h +++ b/src/video_core/host1x/control.h @@ -8,10 +8,10 @@ #include "common/common_types.h" namespace Tegra { -class GPU; namespace Host1x { +class Host1x; class Nvdec; class Control { @@ -22,7 +22,7 @@ public: WaitSyncpt32 = 0x50, }; - explicit Control(GPU& gpu); + explicit Control(Host1x& host1x); ~Control(); /// Writes the method into the state, Invoke Execute() if encountered @@ -33,7 +33,7 @@ private: void Execute(u32 data); u32 syncpoint_value{}; - GPU& gpu; + Host1x& host1x; }; } // namespace Host1x diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp new file mode 100644 index 0000000000..eb00f48557 --- /dev/null +++ b/src/video_core/host1x/host1x.cpp @@ -0,0 +1,18 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv3 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "video_core/host1x/host1x.h" + +namespace Tegra { + +namespace Host1x { + +Host1x::Host1x(Core::System& system_) + : system{system_}, syncpoint_manager{}, memory_manager{system, 32, 12}, + allocator{std::make_unique>(1 << 12)} {} + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h index 2971be2861..e4b69d75aa 100644 --- a/src/video_core/host1x/host1x.h +++ b/src/video_core/host1x/host1x.h @@ -6,7 +6,13 @@ #include "common/common_types.h" +#include "common/address_space.h" #include "video_core/host1x/syncpoint_manager.h" +#include "video_core/memory_manager.h" + +namespace Core { +class System; +} // namespace Core namespace Tegra { @@ -14,7 +20,7 @@ namespace Host1x { class Host1x { public: - Host1x() : syncpoint_manager{} {} + Host1x(Core::System& system); SyncpointManager& GetSyncpointManager() { return syncpoint_manager; @@ -24,8 +30,27 @@ public: return syncpoint_manager; } + Tegra::MemoryManager& MemoryManager() { + return memory_manager; + } + + const Tegra::MemoryManager& MemoryManager() const { + return memory_manager; + } + + Common::FlatAllocator& Allocator() { + return *allocator; + } + + const Common::FlatAllocator& Allocator() const { + return *allocator; + } + private: + Core::System& system; SyncpointManager syncpoint_manager; + Tegra::MemoryManager memory_manager; + std::unique_ptr> allocator; }; } // namespace Host1x diff --git a/src/video_core/host1x/nvdec.cpp b/src/video_core/host1x/nvdec.cpp index 5f6decd0de..a4bd5b79ff 100644 --- a/src/video_core/host1x/nvdec.cpp +++ b/src/video_core/host1x/nvdec.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" #include "video_core/host1x/nvdec.h" namespace Tegra::Host1x { @@ -10,7 +10,8 @@ namespace Tegra::Host1x { #define NVDEC_REG_INDEX(field_name) \ (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64)) -Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique(gpu, state)) {} +Nvdec::Nvdec(Host1x& host1x_) + : host1x(host1x_), state{}, codec(std::make_unique(host1x, state)) {} Nvdec::~Nvdec() = default; diff --git a/src/video_core/host1x/nvdec.h b/src/video_core/host1x/nvdec.h index 41ba1f7a04..3949d51815 100644 --- a/src/video_core/host1x/nvdec.h +++ b/src/video_core/host1x/nvdec.h @@ -9,13 +9,14 @@ #include "video_core/host1x/codecs/codec.h" namespace Tegra { -class GPU; namespace Host1x { +class Host1x; + class Nvdec { public: - explicit Nvdec(GPU& gpu); + explicit Nvdec(Host1x& host1x); ~Nvdec(); /// Writes the method into the state, Invoke Execute() if encountered @@ -28,7 +29,7 @@ private: /// Invoke codec to decode a frame void Execute(); - GPU& gpu; + Host1x& host1x; NvdecCommon::NvdecRegisters state; std::unique_ptr codec; }; diff --git a/src/video_core/host1x/sync_manager.cpp b/src/video_core/host1x/sync_manager.cpp index 8694f77e2a..5ef9ea217b 100644 --- a/src/video_core/host1x/sync_manager.cpp +++ b/src/video_core/host1x/sync_manager.cpp @@ -3,14 +3,13 @@ #include #include "sync_manager.h" -#include "video_core/gpu.h" #include "video_core/host1x/host1x.h" #include "video_core/host1x/syncpoint_manager.h" namespace Tegra { namespace Host1x { -SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {} +SyncptIncrManager::SyncptIncrManager(Host1x& host1x_) : host1x(host1x_) {} SyncptIncrManager::~SyncptIncrManager() = default; void SyncptIncrManager::Increment(u32 id) { @@ -40,7 +39,7 @@ void SyncptIncrManager::IncrementAllDone() { if (!increments[done_count].complete) { break; } - auto& syncpoint_manager = gpu.Host1x().GetSyncpointManager(); + auto& syncpoint_manager = host1x.GetSyncpointManager(); syncpoint_manager.IncrementGuest(increments[done_count].syncpt_id); syncpoint_manager.IncrementHost(increments[done_count].syncpt_id); } diff --git a/src/video_core/host1x/sync_manager.h b/src/video_core/host1x/sync_manager.h index aba72d5c58..7bb77fa278 100644 --- a/src/video_core/host1x/sync_manager.h +++ b/src/video_core/host1x/sync_manager.h @@ -9,10 +9,10 @@ namespace Tegra { -class GPU; - namespace Host1x { +class Host1x; + struct SyncptIncr { u32 id; u32 class_id; @@ -25,7 +25,7 @@ struct SyncptIncr { class SyncptIncrManager { public: - explicit SyncptIncrManager(GPU& gpu); + explicit SyncptIncrManager(Host1x& host1x); ~SyncptIncrManager(); /// Add syncpoint id and increment all @@ -45,7 +45,7 @@ private: std::mutex increment_lock; u32 current_id{}; - GPU& gpu; + Host1x& host1x; }; } // namespace Host1x diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index a9422670a1..5d80398412 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -19,7 +19,7 @@ extern "C" { #include "common/logging/log.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" #include "video_core/host1x/nvdec.h" #include "video_core/host1x/vic.h" #include "video_core/memory_manager.h" @@ -49,8 +49,8 @@ union VicConfig { BitField<46, 14, u64_le> surface_height_minus1; }; -Vic::Vic(GPU& gpu_, std::shared_ptr nvdec_processor_) - : gpu(gpu_), +Vic::Vic(Host1x& host1x_, std::shared_ptr nvdec_processor_) + : host1x(host1x_), nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {} Vic::~Vic() = default; @@ -81,7 +81,7 @@ void Vic::Execute() { LOG_ERROR(Service_NVDRV, "VIC Luma address not set."); return; } - const VicConfig config{gpu.MemoryManager().Read(config_struct_address + 0x20)}; + const VicConfig config{host1x.MemoryManager().Read(config_struct_address + 0x20)}; const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); const auto* frame = frame_ptr.get(); if (!frame) { @@ -159,12 +159,12 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), converted_frame_buf_addr, block_height, 0, 0); - gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); + host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); } else { // send pitch linear frame const size_t linear_size = width * height * 4; - gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, - linear_size); + host1x.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, + linear_size); } } @@ -192,8 +192,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { luma_buffer[dst + x] = luma_src[src + x]; } } - gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), - luma_buffer.size()); + host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), + luma_buffer.size()); // Chroma const std::size_t half_height = frame_height / 2; @@ -234,8 +234,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { ASSERT(false); break; } - gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), - chroma_buffer.size()); + host1x.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), + chroma_buffer.size()); } } // namespace Host1x diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h index c51f8af7e9..2b78786e85 100644 --- a/src/video_core/host1x/vic.h +++ b/src/video_core/host1x/vic.h @@ -10,10 +10,10 @@ struct SwsContext; namespace Tegra { -class GPU; namespace Host1x { +class Host1x; class Nvdec; union VicConfig; @@ -28,7 +28,7 @@ public: SetOutputSurfaceChromaUnusedOffset = 0x1ca }; - explicit Vic(GPU& gpu, std::shared_ptr nvdec_processor); + explicit Vic(Host1x& host1x, std::shared_ptr nvdec_processor); ~Vic(); @@ -42,7 +42,7 @@ private: void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); - GPU& gpu; + Host1x& host1x; std::shared_ptr nvdec_processor; /// Avoid reallocation of the following buffers every frame, as their From 920429fde745b3bf6d33b6ca991628f64988f754 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 30 Jan 2022 23:13:46 +0100 Subject: [PATCH 070/245] NVDRV: Further refactors and eliminate old code. --- src/core/CMakeLists.txt | 2 - src/core/core.cpp | 11 ----- src/core/core.h | 10 ---- src/core/hardware_interrupt_manager.cpp | 32 ------------- src/core/hardware_interrupt_manager.h | 32 ------------- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 26 ++-------- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 2 - .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 7 ++- src/core/hle/service/nvdrv/nvdrv.cpp | 21 -------- src/core/hle/service/nvdrv/nvdrv.h | 7 --- .../hle/service/nvdrv/nvdrv_interface.cpp | 4 -- src/core/hle/service/nvdrv/nvdrv_interface.h | 2 - src/video_core/command_classes/host1x.cpp | 29 ----------- src/video_core/gpu.cpp | 48 ------------------- src/video_core/gpu.h | 9 ---- src/video_core/host1x/syncpoint_manager.cpp | 4 ++ .../renderer_opengl/gl_rasterizer.cpp | 4 -- .../renderer_vulkan/vk_rasterizer.cpp | 4 -- 18 files changed, 12 insertions(+), 242 deletions(-) delete mode 100644 src/core/hardware_interrupt_manager.cpp delete mode 100644 src/core/hardware_interrupt_manager.h delete mode 100644 src/video_core/command_classes/host1x.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3ef19f9c26..95302c419a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -138,8 +138,6 @@ add_library(core STATIC frontend/emu_window.h frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h - hardware_interrupt_manager.cpp - hardware_interrupt_manager.h hid/emulated_console.cpp hid/emulated_console.h hid/emulated_controller.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 13d02e75f5..1deeee1545 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -27,7 +27,6 @@ #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" -#include "core/hardware_interrupt_manager.h" #include "core/hid/hid_core.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" @@ -226,7 +225,6 @@ struct System::Impl { service_manager = std::make_shared(kernel); services = std::make_unique(service_manager, system); - interrupt_manager = std::make_unique(system); // Initialize time manager, which must happen after kernel is created time_manager.Initialize(); @@ -454,7 +452,6 @@ struct System::Impl { std::unique_ptr app_loader; std::unique_ptr gpu_core; std::unique_ptr host1x_core; - std::unique_ptr interrupt_manager; std::unique_ptr device_memory; std::unique_ptr audio_core; Core::Memory::Memory memory; @@ -680,14 +677,6 @@ const Tegra::Host1x::Host1x& System::Host1x() const { return *impl->host1x_core; } -Core::Hardware::InterruptManager& System::InterruptManager() { - return *impl->interrupt_manager; -} - -const Core::Hardware::InterruptManager& System::InterruptManager() const { - return *impl->interrupt_manager; -} - VideoCore::RendererBase& System::Renderer() { return impl->gpu_core->Renderer(); } diff --git a/src/core/core.h b/src/core/core.h index e4168a921f..7843cc8ad9 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -91,10 +91,6 @@ namespace Core::Timing { class CoreTiming; } -namespace Core::Hardware { -class InterruptManager; -} - namespace Core::HID { class HIDCore; } @@ -305,12 +301,6 @@ public: /// Provides a constant reference to the core timing instance. [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; - /// Provides a reference to the interrupt manager instance. - [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager(); - - /// Provides a constant reference to the interrupt manager instance. - [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const; - /// Provides a reference to the kernel instance. [[nodiscard]] Kernel::KernelCore& Kernel(); diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp deleted file mode 100644 index d08cc33159..0000000000 --- a/src/core/hardware_interrupt_manager.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_interrupt_manager.h" -#include "core/hle/service/nvdrv/nvdrv_interface.h" -#include "core/hle/service/sm/sm.h" - -namespace Core::Hardware { - -InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { - gpu_interrupt_event = Core::Timing::CreateEvent( - "GPUInterrupt", - [this](std::uintptr_t message, u64 time, - std::chrono::nanoseconds) -> std::optional { - auto nvdrv = system.ServiceManager().GetService("nvdrv"); - const u32 syncpt = static_cast(message >> 32); - const u32 value = static_cast(message); - nvdrv->SignalGPUInterruptSyncpt(syncpt, value); - return std::nullopt; - }); -} - -InterruptManager::~InterruptManager() = default; - -void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - const u64 msg = (static_cast(syncpoint_id) << 32ULL) | value; - system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg); -} - -} // namespace Core::Hardware diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h deleted file mode 100644 index 5665c59186..0000000000 --- a/src/core/hardware_interrupt_manager.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/common_types.h" - -namespace Core { -class System; -} - -namespace Core::Timing { -struct EventType; -} - -namespace Core::Hardware { - -class InterruptManager { -public: - explicit InterruptManager(Core::System& system); - ~InterruptManager(); - - void GPUInterruptSyncpt(u32 syncpoint_id, u32 value); - -private: - Core::System& system; - std::shared_ptr gpu_interrupt_event; -}; - -} // namespace Core::Hardware diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index ffe42d4235..076edb02f7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -77,12 +77,9 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& return NvResult::NotImplemented; } -void nvhost_ctrl::OnOpen(DeviceFD fd) { - events_interface.RegisterForSignal(this); -} -void nvhost_ctrl::OnClose(DeviceFD fd) { - events_interface.UnregisterForSignal(this); -} +void nvhost_ctrl::OnOpen(DeviceFD fd) {} + +void nvhost_ctrl::OnClose(DeviceFD fd) {} NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector& input, std::vector& output) { IocGetConfigParams params{}; @@ -395,21 +392,4 @@ u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { return 0; } -void nvhost_ctrl::SignalNvEvent(u32 syncpoint_id, u32 value) { - u64 signal_mask = events_mask; - while (signal_mask != 0) { - const u64 event_id = std::countr_zero(signal_mask); - signal_mask &= ~(1ULL << event_id); - auto& event = events[event_id]; - if (event.assigned_syncpt != syncpoint_id || event.assigned_value != value) { - continue; - } - if (event.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == - EventState::Waiting) { - event.kevent->GetWritableEvent().Signal(); - } - event.status.store(EventState::Signalled, std::memory_order_release); - } -} - } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 136a1e925e..f511c02961 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -56,8 +56,6 @@ public: }; static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); - void SignalNvEvent(u32 syncpoint_id, u32 value); - private: struct InternalEvent { // Mask representing registered events diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index db3e266ad6..3f981af5a1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -14,6 +14,7 @@ #include "video_core/control/channel_state.h" #include "video_core/engines/puller.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { namespace { @@ -31,7 +32,8 @@ nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, channel_state{system.GPU().AllocateChannel()} { channel_fence.id = syncpoint_manager.AllocateSyncpoint(); - channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); + channel_fence.value = + system_.Host1x().GetSyncpointManager().GetGuestSyncpointValue(channel_fence.id); sm_exception_breakpoint_int_report_event = events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); sm_exception_breakpoint_pause_report_event = @@ -189,7 +191,8 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector& input, std::vector lk(guard); - on_signal.push_back(device); -} - -void EventInterface::UnregisterForSignal(Devices::nvhost_ctrl* device) { - std::unique_lock lk(guard); - on_signal.remove(device); -} - -void EventInterface::Signal(u32 syncpoint_id, u32 value) { - std::unique_lock lk(guard); - for (auto* device : on_signal) { - device->SignalNvEvent(syncpoint_id, value); - } -} - Kernel::KEvent* EventInterface::CreateEvent(std::string name) { Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); return new_event; @@ -221,10 +204,6 @@ NvResult Module::Close(DeviceFD fd) { return NvResult::Success; } -void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - events_interface.Signal(syncpoint_id, value); -} - NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 1fe98cf32c..31c45236e6 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -49,11 +49,6 @@ public: EventInterface(Module& module_); ~EventInterface(); - void RegisterForSignal(Devices::nvhost_ctrl*); - void UnregisterForSignal(Devices::nvhost_ctrl*); - - void Signal(u32 syncpoint_id, u32 value); - Kernel::KEvent* CreateEvent(std::string name); void FreeEvent(Kernel::KEvent* event); @@ -96,8 +91,6 @@ public: /// Closes a device file descriptor and returns operation success. NvResult Close(DeviceFD fd); - void SignalSyncpt(const u32 syncpoint_id, const u32 value); - NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); private: diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index bd41205b88..5e50a04e8a 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -15,10 +15,6 @@ namespace Service::Nvidia { -void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - nvdrv->SignalSyncpt(syncpoint_id, value); -} - void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); IPC::ResponseBuilder rb{ctx, 4}; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index cbd37b52b6..cd58a4f35c 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -18,8 +18,6 @@ public: explicit NVDRV(Core::System& system_, std::shared_ptr nvdrv_, const char* name); ~NVDRV() override; - void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value); - private: void Open(Kernel::HLERequestContext& ctx); void Ioctl1(Kernel::HLERequestContext& ctx); diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp deleted file mode 100644 index 11855fe103..0000000000 --- a/src/video_core/command_classes/host1x.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "video_core/command_classes/host1x.h" -#include "video_core/gpu.h" - -Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {} - -Tegra::Host1x::~Host1x() = default; - -void Tegra::Host1x::ProcessMethod(Method method, u32 argument) { - switch (method) { - case Method::LoadSyncptPayload32: - syncpoint_value = argument; - break; - case Method::WaitSyncpt: - case Method::WaitSyncpt32: - Execute(argument); - break; - default: - UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast(method)); - break; - } -} - -void Tegra::Host1x::Execute(u32 data) { - gpu.WaitFence(data, syncpoint_value); -} diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e05c9a3573..a1d19b1c8a 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -14,7 +14,6 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" -#include "core/hardware_interrupt_manager.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/perf_stats.h" #include "video_core/cdma_pusher.h" @@ -36,8 +35,6 @@ namespace Tegra { -MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); - struct GPU::Impl { explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) : gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_}, @@ -197,30 +194,6 @@ struct GPU::Impl { return *shader_notify; } - /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. - void WaitFence(u32 syncpoint_id, u32 value) { - if (syncpoint_id == UINT32_MAX) { - return; - } - MICROPROFILE_SCOPE(GPU_wait); - host1x.GetSyncpointManager().WaitHost(syncpoint_id, value); - } - - void IncrementSyncPoint(u32 syncpoint_id) { - host1x.GetSyncpointManager().IncrementHost(syncpoint_id); - } - - [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const { - return host1x.GetSyncpointManager().GetHostSyncpointValue(syncpoint_id); - } - - void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - auto& syncpoint_manager = host1x.GetSyncpointManager(); - syncpoint_manager.RegisterHostAction(syncpoint_id, value, [this, syncpoint_id, value]() { - TriggerCpuInterrupt(syncpoint_id, value); - }); - } - [[nodiscard]] u64 GetTicks() const { // This values were reversed engineered by fincs from NVN // The gpu clock is reported in units of 385/625 nanoseconds @@ -322,11 +295,6 @@ struct GPU::Impl { gpu_thread.FlushAndInvalidateRegion(addr, size); } - void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const { - auto& interrupt_manager = system.InterruptManager(); - interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); - } - void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, Service::Nvidia::NvFence* fences, size_t num_fences) { size_t current_request_counter{}; @@ -524,22 +492,6 @@ void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, impl->RequestSwapBuffers(framebuffer, fences, num_fences); } -void GPU::WaitFence(u32 syncpoint_id, u32 value) { - impl->WaitFence(syncpoint_id, value); -} - -void GPU::IncrementSyncPoint(u32 syncpoint_id) { - impl->IncrementSyncPoint(syncpoint_id); -} - -u32 GPU::GetSyncpointValue(u32 syncpoint_id) const { - return impl->GetSyncpointValue(syncpoint_id); -} - -void GPU::RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - impl->RegisterSyncptInterrupt(syncpoint_id, value); -} - u64 GPU::GetTicks() const { return impl->GetTicks(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index f04edf5c49..655373b33c 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -171,15 +171,6 @@ public: /// Returns a const reference to the shader notifier. [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const; - /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. - void WaitFence(u32 syncpoint_id, u32 value); - - void IncrementSyncPoint(u32 syncpoint_id); - - [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const; - - void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); - [[nodiscard]] u64 GetTicks() const; [[nodiscard]] bool IsAsync() const; diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp index c606b8bd0d..825bd551e7 100644 --- a/src/video_core/host1x/syncpoint_manager.cpp +++ b/src/video_core/host1x/syncpoint_manager.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv3 or any later version // Refer to the license.txt file included. +#include "common/microprofile.h" #include "video_core/host1x/syncpoint_manager.h" namespace Tegra { namespace Host1x { +MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); + SyncpointManager::ActionHandle SyncpointManager::RegisterAction( std::atomic& syncpoint, std::list& action_storage, u32 expected_value, std::function& action) { @@ -58,6 +61,7 @@ void SyncpointManager::WaitGuest(u32 syncpoint_id, u32 expected_value) { } void SyncpointManager::WaitHost(u32 syncpoint_id, u32 expected_value) { + MICROPROFILE_SCOPE(GPU_wait); Wait(syncpoints_host[syncpoint_id], wait_host_cv, expected_value); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index f745fbf562..b572950a67 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -396,10 +396,6 @@ void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { } void RasterizerOpenGL::SignalSyncPoint(u32 value) { - if (!gpu.IsAsync()) { - gpu.IncrementSyncPoint(value); - return; - } fence_manager.SignalSyncPoint(value); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 50fdf5e18b..a95f682314 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -458,10 +458,6 @@ void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { } void RasterizerVulkan::SignalSyncPoint(u32 value) { - if (!gpu.IsAsync()) { - gpu.IncrementSyncPoint(value); - return; - } fence_manager.SignalSyncPoint(value); } From 98b5e236d4049d1a2a4c6413486b5015b7efe2c8 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 5 Feb 2022 12:30:19 +0100 Subject: [PATCH 071/245] Vulkan: Fix Scissor on Clears --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a95f682314..d7b57e0f3d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -247,8 +247,15 @@ void RasterizerVulkan::Clear() { } UpdateViewportsState(regs); + VkRect2D default_scissor; + default_scissor.offset.x = 0; + default_scissor.offset.y = 0; + default_scissor.extent.width = std::numeric_limits::max(); + default_scissor.extent.height = std::numeric_limits::max(); + VkClearRect clear_rect{ - .rect = GetScissorState(regs, 0, up_scale, down_shift), + .rect = regs.clear_flags.scissor ? GetScissorState(regs, 0, up_scale, down_shift) + : default_scissor, .baseArrayLayer = regs.clear_buffers.layer, .layerCount = 1, }; From 4d60410dd979fb688de7735d2b4b25a557bdeac7 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 5 Feb 2022 18:15:26 +0100 Subject: [PATCH 072/245] MemoryManager: initial multi paging system implementation. --- src/common/multi_level_page_table.inc | 3 + .../service/nvdrv/devices/nvhost_as_gpu.cpp | 45 +- .../hle/service/nvdrv/devices/nvhost_as_gpu.h | 1 + src/core/hle/service/nvdrv/devices/nvmap.cpp | 10 + src/video_core/memory_manager.cpp | 442 +++++++++++------- src/video_core/memory_manager.h | 57 ++- 6 files changed, 346 insertions(+), 212 deletions(-) diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc index 7fbcb908ac..9a68cad936 100644 --- a/src/common/multi_level_page_table.inc +++ b/src/common/multi_level_page_table.inc @@ -19,6 +19,9 @@ MultiLevelPageTable::MultiLevelPageTable(std::size_t address_space_bit std::size_t page_bits_) : address_space_bits{address_space_bits_}, first_level_bits{first_level_bits_}, page_bits{page_bits_} { + if (page_bits == 0) { + return; + } first_level_shift = address_space_bits - first_level_bits; first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr); alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index db2a6c3b2e..d95a883931 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -133,7 +133,8 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& const u64 end_big_pages{(vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits}; vm.big_page_allocator = std::make_unique(start_big_pages, end_big_pages); - gmmu = std::make_shared(system, 40, VM::PAGE_SIZE_BITS); + gmmu = std::make_shared(system, 40, vm.big_page_size_bits, + VM::PAGE_SIZE_BITS); system.GPU().InitAddressSpace(*gmmu); vm.initialised = true; @@ -189,6 +190,7 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector& input, std::vector< .size = size, .page_size = params.page_size, .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, + .big_pages = params.page_size != VM::YUZU_PAGESIZE, }; std::memcpy(output.data(), ¶ms, output.size()); @@ -209,7 +211,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) { // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state // Only FreeSpace can unmap them fully if (mapping->sparse_alloc) - gmmu->MapSparse(offset, mapping->size); + gmmu->MapSparse(offset, mapping->size, mapping->big_page); else gmmu->Unmap(offset, mapping->size); @@ -294,8 +296,9 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out return NvResult::BadValue; } + const bool use_big_pages = alloc->second.big_pages; if (!entry.handle) { - gmmu->MapSparse(virtual_address, size); + gmmu->MapSparse(virtual_address, size, use_big_pages); } else { auto handle{nvmap.GetHandle(entry.handle)}; if (!handle) { @@ -306,7 +309,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out handle->address + (static_cast(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; - gmmu->Map(virtual_address, cpu_address, size); + gmmu->Map(virtual_address, cpu_address, size, use_big_pages); } } @@ -345,7 +348,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector(params.offset + params.buffer_offset)}; VAddr cpu_address{mapping->ptr + params.buffer_offset}; - gmmu->Map(gpu_address, cpu_address, params.mapping_size); + gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page); return NvResult::Success; } catch ([[maybe_unused]] const std::out_of_range& e) { @@ -363,6 +366,17 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector(handle->address + params.buffer_offset)}; u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; + bool big_page{[&]() { + if (Common::IsAligned(handle->align, vm.big_page_size)) + return true; + else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) + return false; + else { + UNREACHABLE(); + return false; + } + }()}; + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { auto alloc{allocation_map.upper_bound(params.offset)}; @@ -372,23 +386,14 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorMap(params.offset, cpu_address, size); + const bool use_big_pages = alloc->second.big_pages && big_page; + gmmu->Map(params.offset, cpu_address, size, use_big_pages); - auto mapping{std::make_shared(cpu_address, params.offset, size, true, false, - alloc->second.sparse)}; + auto mapping{std::make_shared(cpu_address, params.offset, size, true, + use_big_pages, alloc->second.sparse)}; alloc->second.mappings.push_back(mapping); mapping_map[params.offset] = mapping; } else { - bool big_page{[&]() { - if (Common::IsAligned(handle->align, vm.big_page_size)) - return true; - else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) - return false; - else { - UNREACHABLE(); - return false; - } - }()}; auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; @@ -402,7 +407,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorMap(params.offset, cpu_address, size); + gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page); auto mapping{ std::make_shared(cpu_address, params.offset, size, false, big_page, false)}; @@ -439,7 +444,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vectorsparse_alloc) { - gmmu->MapSparse(params.offset, mapping->size); + gmmu->MapSparse(params.offset, mapping->size, mapping->big_page); } else { gmmu->Unmap(params.offset, mapping->size); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 1d27739e26..12e881f0db 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -177,6 +177,7 @@ private: std::list> mappings; u32 page_size; bool sparse; + bool big_pages; }; std::map> diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 279997e813..992c117f1a 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -9,6 +9,8 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h" @@ -136,6 +138,10 @@ NvResult nvmap::IocAlloc(const std::vector& input, std::vector& output) LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); return result; } + ASSERT(system.CurrentProcess() + ->PageTable() + .LockForDeviceAddressSpace(handle_description->address, handle_description->size) + .IsSuccess()); std::memcpy(output.data(), ¶ms, sizeof(params)); return result; } @@ -256,6 +262,10 @@ NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { } if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + ASSERT(system.CurrentProcess() + ->PageTable() + .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) + .IsSuccess()); params.address = freeInfo->address; params.size = static_cast(freeInfo->size); params.flags.raw = 0; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index b36067613f..836ece136d 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/device_memory.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/memory.h" @@ -14,40 +15,69 @@ #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" +#pragma optimize("", off) + namespace Tegra { std::atomic MemoryManager::unique_identifier_generator{}; -MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 page_bits_) - : system{system_}, address_space_bits{address_space_bits_}, page_bits{page_bits_}, entries{}, - page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits}, +MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_, + u64 page_bits_) + : system{system_}, memory{system.Memory()}, device_memory{system.DeviceMemory()}, + address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, + entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, + page_bits != big_page_bits ? page_bits : 0}, unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { address_space_size = 1ULL << address_space_bits; - allocate_start = address_space_bits > 32 ? 1ULL << 32 : 0; page_size = 1ULL << page_bits; page_mask = page_size - 1ULL; - const u64 page_table_bits = address_space_bits - cpu_page_bits; + big_page_size = 1ULL << big_page_bits; + big_page_mask = big_page_size - 1ULL; + const u64 page_table_bits = address_space_bits - page_bits; + const u64 big_page_table_bits = address_space_bits - big_page_bits; const u64 page_table_size = 1ULL << page_table_bits; + const u64 big_page_table_size = 1ULL << big_page_table_bits; page_table_mask = page_table_size - 1; + big_page_table_mask = big_page_table_size - 1; + big_entries.resize(big_page_table_size / 32, 0); + big_page_table_cpu.resize(big_page_table_size); + big_page_table_physical.resize(big_page_table_size); entries.resize(page_table_size / 32, 0); } MemoryManager::~MemoryManager() = default; +template MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const { - position = position >> page_bits; - const u64 entry_mask = entries[position / 32]; - const size_t sub_index = position % 32; - return static_cast((entry_mask >> (2 * sub_index)) & 0x03ULL); + if constexpr (is_big_page) { + position = position >> big_page_bits; + const u64 entry_mask = big_entries[position / 32]; + const size_t sub_index = position % 32; + return static_cast((entry_mask >> (2 * sub_index)) & 0x03ULL); + } else { + position = position >> page_bits; + const u64 entry_mask = entries[position / 32]; + const size_t sub_index = position % 32; + return static_cast((entry_mask >> (2 * sub_index)) & 0x03ULL); + } } +template void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { - position = position >> page_bits; - const u64 entry_mask = entries[position / 32]; - const size_t sub_index = position % 32; - entries[position / 32] = - (~(3ULL << sub_index * 2) & entry_mask) | (static_cast(entry) << sub_index * 2); + if constexpr (is_big_page) { + position = position >> big_page_bits; + const u64 entry_mask = big_entries[position / 32]; + const size_t sub_index = position % 32; + big_entries[position / 32] = + (~(3ULL << sub_index * 2) & entry_mask) | (static_cast(entry) << sub_index * 2); + } else { + position = position >> page_bits; + const u64 entry_mask = entries[position / 32]; + const size_t sub_index = position % 32; + entries[position / 32] = + (~(3ULL << sub_index * 2) & entry_mask) | (static_cast(entry) << sub_index * 2); + } } template @@ -59,48 +89,66 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp } for (u64 offset{}; offset < size; offset += page_size) { const GPUVAddr current_gpu_addr = gpu_addr + offset; - [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); - SetEntry(current_gpu_addr, entry_type); + [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); + SetEntry(current_gpu_addr, entry_type); if (current_entry_type != entry_type) { rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); } if constexpr (entry_type == EntryType::Mapped) { const VAddr current_cpu_addr = cpu_addr + offset; - const auto index = PageEntryIndex(current_gpu_addr); - const u32 sub_value = static_cast(current_cpu_addr >> 12ULL); - if (current_entry_type == entry_type && sub_value != page_table[index]) { - rasterizer->InvalidateRegion(static_cast(page_table[index]) << 12ULL, - page_size); - } - page_table[index] = static_cast(current_cpu_addr >> 12ULL); + const auto index = PageEntryIndex(current_gpu_addr); + const u32 sub_value = static_cast(current_cpu_addr >> cpu_page_bits); + page_table[index] = sub_value; } remaining_size -= page_size; } return gpu_addr; } +template +GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, + size_t size) { + u64 remaining_size{size}; + for (u64 offset{}; offset < size; offset += big_page_size) { + const GPUVAddr current_gpu_addr = gpu_addr + offset; + [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); + SetEntry(current_gpu_addr, entry_type); + if (current_entry_type != entry_type) { + rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); + } + if constexpr (entry_type == EntryType::Mapped) { + const VAddr current_cpu_addr = cpu_addr + offset; + const auto index = PageEntryIndex(current_gpu_addr); + const u32 sub_value = static_cast(current_cpu_addr >> cpu_page_bits); + big_page_table_cpu[index] = sub_value; + const PAddr phys_address = + device_memory.GetPhysicalAddr(memory.GetPointer(current_cpu_addr)); + big_page_table_physical[index] = static_cast(phys_address); + } + remaining_size -= big_page_size; + } + return gpu_addr; +} + void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; } -GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) { +GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, + bool is_big_pages) { + if (is_big_pages) [[likely]] { + return BigPageTableOp(gpu_addr, cpu_addr, size); + } return PageTableOp(gpu_addr, cpu_addr, size); } -GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size) { +GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) { + if (is_big_pages) [[likely]] { + return BigPageTableOp(gpu_addr, 0, size); + } return PageTableOp(gpu_addr, 0, size); } -GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) { - return Map(*FindFreeRange(size, align), cpu_addr, size); -} - -GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { - const std::optional gpu_addr = FindFreeRange(size, 1, true); - ASSERT(gpu_addr); - return Map(*gpu_addr, cpu_addr, size); -} - void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { if (size == 0) { return; @@ -115,61 +163,24 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { rasterizer->UnmapMemory(*cpu_addr, map_size); } + BigPageTableOp(gpu_addr, 0, size); PageTableOp(gpu_addr, 0, size); } -std::optional MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { - for (u64 offset{}; offset < size; offset += page_size) { - if (GetEntry(gpu_addr + offset) != EntryType::Free) { +std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { + if (GetEntry(gpu_addr) != EntryType::Mapped) [[unlikely]] { + if (GetEntry(gpu_addr) != EntryType::Mapped) { return std::nullopt; } + + const VAddr cpu_addr_base = static_cast(page_table[PageEntryIndex(gpu_addr)]) + << cpu_page_bits; + return cpu_addr_base + (gpu_addr & page_mask); } - return PageTableOp(gpu_addr, 0, size); -} - -GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) { - return *AllocateFixed(*FindFreeRange(size, align), size); -} - -std::optional MemoryManager::FindFreeRange(std::size_t size, std::size_t align, - bool start_32bit_address) const { - if (!align) { - align = page_size; - } else { - align = Common::AlignUp(align, page_size); - } - - u64 available_size{}; - GPUVAddr gpu_addr{start_32bit_address ? 0 : allocate_start}; - while (gpu_addr + available_size < address_space_size) { - if (GetEntry(gpu_addr + available_size) == EntryType::Free) { - available_size += page_size; - - if (available_size >= size) { - return gpu_addr; - } - } else { - gpu_addr += available_size + page_size; - available_size = 0; - - const auto remainder{gpu_addr % align}; - if (remainder) { - gpu_addr = (gpu_addr - remainder) + align; - } - } - } - - return std::nullopt; -} - -std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { - if (GetEntry(gpu_addr) != EntryType::Mapped) { - return std::nullopt; - } - - const VAddr cpu_addr_base = static_cast(page_table[PageEntryIndex(gpu_addr)]) << 12ULL; - return cpu_addr_base + (gpu_addr & page_mask); + const VAddr cpu_addr_base = + static_cast(big_page_table_cpu[PageEntryIndex(gpu_addr)]) << cpu_page_bits; + return cpu_addr_base + (gpu_addr & big_page_mask); } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const { @@ -225,7 +236,7 @@ u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) { return {}; } - return system.Memory().GetPointer(*address); + return memory.GetPointer(*address); } const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { @@ -234,98 +245,161 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { return {}; } - return system.Memory().GetPointer(*address); + return memory.GetPointer(*address); } -void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, - bool is_safe) const { +#pragma inline_recursion(on) + +template +inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, + FuncMapped&& func_mapped, FuncReserved&& func_reserved, + FuncUnmapped&& func_unmapped) const { + u64 used_page_size; + u64 used_page_mask; + u64 used_page_bits; + if constexpr (is_big_pages) { + used_page_size = big_page_size; + used_page_mask = big_page_mask; + used_page_bits = big_page_bits; + } else { + used_page_size = page_size; + used_page_mask = page_mask; + used_page_bits = page_bits; + } std::size_t remaining_size{size}; - std::size_t page_index{gpu_src_addr >> page_bits}; - std::size_t page_offset{gpu_src_addr & page_mask}; + std::size_t page_index{gpu_src_addr >> used_page_bits}; + std::size_t page_offset{gpu_src_addr & used_page_mask}; + GPUVAddr current_address = gpu_src_addr; while (remaining_size > 0) { const std::size_t copy_amount{ - std::min(static_cast(page_size) - page_offset, remaining_size)}; - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr) { - const auto src_addr{*page_addr + page_offset}; - if (is_safe) { - // Flush must happen on the rasterizer interface, such that memory is always - // synchronous when it is read (even when in asynchronous GPU mode). - // Fixes Dead Cells title menu. - rasterizer->FlushRegion(src_addr, copy_amount); - } - system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); - } else { - std::memset(dest_buffer, 0, copy_amount); + std::min(static_cast(used_page_size) - page_offset, remaining_size)}; + auto entry = GetEntry(current_address); + if (entry == EntryType::Mapped) [[likely]] { + func_mapped(page_index, page_offset, copy_amount); + } else if (entry == EntryType::Reserved) { + func_reserved(page_index, page_offset, copy_amount); + } else [[unlikely]] { + func_unmapped(page_index, page_offset, copy_amount); } - page_index++; page_offset = 0; - dest_buffer = static_cast(dest_buffer) + copy_amount; remaining_size -= copy_amount; + current_address += copy_amount; } } +template +void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, + std::size_t size) const { + auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { + std::memset(dest_buffer, 0, copy_amount); + dest_buffer = static_cast(dest_buffer) + copy_amount; + }; + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + } + memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); + dest_buffer = static_cast(dest_buffer) + copy_amount; + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + } + memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); + // u8* physical = device_memory.GetPointer(big_page_table_physical[page_index] + offset); + // std::memcpy(dest_buffer, physical, copy_amount); + dest_buffer = static_cast(dest_buffer) + copy_amount; + }; + auto read_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, set_to_zero, set_to_zero); + }; + MemoryOperation(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages); +} + void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { - ReadBlockImpl(gpu_src_addr, dest_buffer, size, true); + ReadBlockImpl(gpu_src_addr, dest_buffer, size); } void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, const std::size_t size) const { - ReadBlockImpl(gpu_src_addr, dest_buffer, size, false); + ReadBlockImpl(gpu_src_addr, dest_buffer, size); } -void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, - bool is_safe) { - std::size_t remaining_size{size}; - std::size_t page_index{gpu_dest_addr >> page_bits}; - std::size_t page_offset{gpu_dest_addr & page_mask}; - - while (remaining_size > 0) { - const std::size_t copy_amount{ - std::min(static_cast(page_size) - page_offset, remaining_size)}; - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr) { - const auto dest_addr{*page_addr + page_offset}; - - if (is_safe) { - // Invalidate must happen on the rasterizer interface, such that memory is always - // synchronous when it is written (even when in asynchronous GPU mode). - rasterizer->InvalidateRegion(dest_addr, copy_amount); - } - system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); - } - - page_index++; - page_offset = 0; +template +void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, + std::size_t size) { + auto just_advance = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { src_buffer = static_cast(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } + }; + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + } + memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); + src_buffer = static_cast(src_buffer) + copy_amount; + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + } + memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); + /*u8* physical = + device_memory.GetPointer(big_page_table_physical[page_index] << cpu_page_bits) + offset; + std::memcpy(physical, src_buffer, copy_amount);*/ + src_buffer = static_cast(src_buffer) + copy_amount; + }; + auto write_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, just_advance, just_advance); + }; + MemoryOperation(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages); } void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { - WriteBlockImpl(gpu_dest_addr, src_buffer, size, true); + WriteBlockImpl(gpu_dest_addr, src_buffer, size); } void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { - WriteBlockImpl(gpu_dest_addr, src_buffer, size, false); + WriteBlockImpl(gpu_dest_addr, src_buffer, size); } void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { - size_t remaining_size{size}; - size_t page_index{gpu_addr >> page_bits}; - size_t page_offset{gpu_addr & page_mask}; - while (remaining_size > 0) { - const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; - if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { - rasterizer->FlushRegion(*page_addr + page_offset, num_bytes); - } - ++page_index; - page_offset = 0; - remaining_size -= num_bytes; - } + auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) {}; + + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + }; + auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, do_nothing, do_nothing); + }; + MemoryOperation(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); } void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { @@ -348,7 +422,7 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { } bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { - size_t page_index{gpu_addr >> page_bits}; + size_t page_index{gpu_addr >> big_page_bits}; const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; std::optional old_page_addr{}; while (page_index != page_last) { @@ -371,7 +445,7 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons size_t page_index{gpu_addr >> page_bits}; const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; while (page_index < page_last) { - if (GetEntry(page_index << page_bits) == EntryType::Free) { + if (GetEntry(page_index << page_bits) == EntryType::Free) { return false; } ++page_index; @@ -379,47 +453,63 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons return true; } +#pragma inline_recursion(on) + std::vector> MemoryManager::GetSubmappedRange( GPUVAddr gpu_addr, std::size_t size) const { std::vector> result{}; - size_t page_index{gpu_addr >> page_bits}; - size_t remaining_size{size}; - size_t page_offset{gpu_addr & page_mask}; std::optional> last_segment{}; std::optional old_page_addr{}; - const auto extend_size = [this, &last_segment, &page_index, &page_offset](std::size_t bytes) { - if (!last_segment) { - const GPUVAddr new_base_addr = (page_index << page_bits) + page_offset; - last_segment = {new_base_addr, bytes}; - } else { - last_segment->second += bytes; - } - }; - const auto split = [&last_segment, &result] { + const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { if (last_segment) { result.push_back(*last_segment); last_segment = std::nullopt; } }; - while (remaining_size > 0) { - const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (!page_addr || *page_addr == 0) { - split(); - } else if (old_page_addr) { - if (*old_page_addr + page_size != *page_addr) { - split(); + const auto extend_size_big = [this, &split, &old_page_addr, + &last_segment](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if (old_page_addr) { + if (*old_page_addr != cpu_addr_base) { + split(0, 0, 0); } - extend_size(num_bytes); - } else { - extend_size(num_bytes); } - ++page_index; - page_offset = 0; - remaining_size -= num_bytes; - old_page_addr = page_addr; - } - split(); + old_page_addr = {cpu_addr_base + copy_amount}; + if (!last_segment) { + const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset; + last_segment = {new_base_addr, copy_amount}; + } else { + last_segment->second += copy_amount; + } + }; + const auto extend_size_short = [this, &split, &old_page_addr, + &last_segment](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if (old_page_addr) { + if (*old_page_addr != cpu_addr_base) { + split(0, 0, 0); + } + } + old_page_addr = {cpu_addr_base + copy_amount}; + if (!last_segment) { + const GPUVAddr new_base_addr = (page_index << page_bits) + offset; + last_segment = {new_base_addr, copy_amount}; + } else { + last_segment->second += copy_amount; + } + }; + auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, extend_size_short, split, split); + }; + MemoryOperation(gpu_addr, size, extend_size_big, split, do_short_pages); + split(0, 0, 0); return result; } diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 56604ef3e8..9c388a06e8 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -10,21 +10,26 @@ #include "common/common_types.h" #include "common/multi_level_page_table.h" +#include "common/virtual_buffer.h" namespace VideoCore { class RasterizerInterface; } namespace Core { +class DeviceMemory; +namespace Memory { +class Memory; +} // namespace Memory class System; -} +} // namespace Core namespace Tegra { class MemoryManager final { public: explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40, - u64 page_bits_ = 16); + u64 big_page_bits_ = 16, u64 page_bits_ = 12); ~MemoryManager(); size_t GetID() const { @@ -93,12 +98,8 @@ public: std::vector> GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const; - GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size); - GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size); - [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); - [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size); - [[nodiscard]] std::optional AllocateFixed(GPUVAddr gpu_addr, std::size_t size); - [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align); + GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, bool is_big_pages = true); + GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); void Unmap(GPUVAddr gpu_addr, std::size_t size); void FlushRegion(GPUVAddr gpu_addr, size_t size) const; @@ -107,26 +108,42 @@ private: [[nodiscard]] std::optional FindFreeRange(std::size_t size, std::size_t align, bool start_32bit_address = false) const; - void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, - bool is_safe) const; - void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, - bool is_safe); + template + inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, + FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; + template + void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; + + template + void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); + + template [[nodiscard]] inline std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { - return (gpu_addr >> page_bits) & page_table_mask; + if constexpr (is_big_page) { + return (gpu_addr >> big_page_bits) & big_page_table_mask; + } else { + return (gpu_addr >> page_bits) & page_table_mask; + } } Core::System& system; + Core::Memory::Memory& memory; + Core::DeviceMemory& device_memory; const u64 address_space_bits; const u64 page_bits; u64 address_space_size; - u64 allocate_start; u64 page_size; u64 page_mask; u64 page_table_mask; static constexpr u64 cpu_page_bits{12}; + const u64 big_page_bits; + u64 big_page_size; + u64 big_page_mask; + u64 big_page_table_mask; + VideoCore::RasterizerInterface* rasterizer = nullptr; enum class EntryType : u64 { @@ -136,15 +153,23 @@ private: }; std::vector entries; + std::vector big_entries; template GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size); - EntryType GetEntry(size_t position) const; + template + GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size); - void SetEntry(size_t position, EntryType entry); + template + inline EntryType GetEntry(size_t position) const; + + template + inline void SetEntry(size_t position, EntryType entry); Common::MultiLevelPageTable page_table; + Common::VirtualBuffer big_page_table_cpu; + Common::VirtualBuffer big_page_table_physical; const size_t unique_identifier; From bc8b3d225eda388f0603830cbff8357893abb0f9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 6 Feb 2022 01:16:11 +0100 Subject: [PATCH 073/245] VideoCore: Refactor fencing system. --- .../service/nvdrv/devices/nvdisp_disp0.cpp | 5 +- .../hle/service/nvdrv/devices/nvdisp_disp0.h | 3 +- src/core/hle/service/nvflinger/nvflinger.cpp | 15 +-- src/video_core/buffer_cache/buffer_cache.h | 13 +++ src/video_core/dma_pusher.cpp | 3 - src/video_core/engines/maxwell_3d.cpp | 24 ++++- src/video_core/engines/puller.cpp | 39 +++++--- src/video_core/fence_manager.h | 96 ++++++++----------- src/video_core/gpu.cpp | 17 ++-- src/video_core/gpu.h | 4 +- src/video_core/gpu_thread.cpp | 2 +- src/video_core/rasterizer_interface.h | 7 +- .../renderer_opengl/gl_fence_manager.cpp | 13 +-- .../renderer_opengl/gl_fence_manager.h | 6 +- .../renderer_opengl/gl_rasterizer.cpp | 22 ++--- .../renderer_opengl/gl_rasterizer.h | 5 +- .../renderer_vulkan/vk_fence_manager.cpp | 15 +-- .../renderer_vulkan/vk_fence_manager.h | 6 +- .../renderer_vulkan/vk_rasterizer.cpp | 21 ++-- .../renderer_vulkan/vk_rasterizer.h | 5 +- 20 files changed, 154 insertions(+), 167 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index e6a976714f..18c5324a9a 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -40,7 +40,8 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, - const Common::Rectangle& crop_rect) { + const Common::Rectangle& crop_rect, + std::array& fences, u32 num_fences) { const VAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", @@ -50,7 +51,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form stride, format, transform, crop_rect}; system.GetPerfStats().EndSystemFrame(); - system.GPU().RequestSwapBuffers(&framebuffer, nullptr, 0); + system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 1ca9b2e74a..04217ab129 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -38,7 +38,8 @@ public: /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, - const Common::Rectangle& crop_rect); + const Common::Rectangle& crop_rect, + std::array& fences, u32 num_fences); Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index aa112021d2..4658f1e8b8 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -269,17 +269,6 @@ void NVFlinger::Compose() { return; // We are likely shutting down } - auto& syncpoint_manager = system.Host1x().GetSyncpointManager(); - const auto& multi_fence = buffer.fence; - guard->unlock(); - for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { - const auto& fence = multi_fence.fences[fence_id]; - syncpoint_manager.WaitGuest(fence.id, fence.value); - } - guard->lock(); - - MicroProfileFlip(); - // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based // on which display we're drawing (Default, Internal, External, etc) @@ -293,8 +282,10 @@ void NVFlinger::Compose() { nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), - static_cast(buffer.transform), crop_rect); + static_cast(buffer.transform), crop_rect, + buffer.fence.fences, buffer.fence.num_fences); + MicroProfileFlip(); guard->lock(); swap_interval = buffer.swap_interval; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 6b6764d721..e55cac0d65 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -826,6 +826,19 @@ void BufferCache

::CommitAsyncFlushesHigh() { const bool is_accuracy_normal = Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::Normal; + auto it = committed_ranges.begin(); + while (it != committed_ranges.end()) { + auto& current_intervals = *it; + auto next_it = std::next(it); + while (next_it != committed_ranges.end()) { + for (auto& interval : *next_it) { + current_intervals.subtract(interval); + } + next_it++; + } + it++; + } + boost::container::small_vector, 1> downloads; u64 total_size_bytes = 0; u64 largest_copy = 0; diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index b01f04d0c9..9835e3ac1e 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -24,8 +24,6 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128, void DmaPusher::DispatchCalls() { MICROPROFILE_SCOPE(DispatchCalls); - gpu.SyncGuestHost(); - dma_pushbuffer_subindex = 0; dma_state.is_last_call = true; @@ -36,7 +34,6 @@ void DmaPusher::DispatchCalls() { } } gpu.FlushCommands(); - gpu.SyncGuestHost(); gpu.OnCommandListEnd(); } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 3a4646289c..950c70dcd9 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -242,6 +242,9 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume return; case MAXWELL3D_REG_INDEX(fragment_barrier): return rasterizer->FragmentBarrier(); + case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache): + rasterizer->InvalidateGPUCache(); + return rasterizer->WaitForIdle(); case MAXWELL3D_REG_INDEX(tiled_cache_barrier): return rasterizer->TiledCacheBarrier(); } @@ -472,10 +475,25 @@ void Maxwell3D::ProcessQueryGet() { switch (regs.query.query_get.operation) { case Regs::QueryOperation::Release: - if (regs.query.query_get.fence == 1) { - rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); + if (regs.query.query_get.fence == 1 || regs.query.query_get.short_query != 0) { + const GPUVAddr sequence_address{regs.query.QueryAddress()}; + const u32 payload = regs.query.query_sequence; + std::function operation([this, sequence_address, payload] { + memory_manager.Write(sequence_address, payload); + }); + rasterizer->SignalFence(std::move(operation)); } else { - StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); + struct LongQueryResult { + u64_le value; + u64_le timestamp; + }; + const GPUVAddr sequence_address{regs.query.QueryAddress()}; + const u32 payload = regs.query.query_sequence; + std::function operation([this, sequence_address, payload] { + LongQueryResult query_result{payload, system.GPU().GetTicks()}; + memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result)); + }); + rasterizer->SignalFence(std::move(operation)); } break; case Regs::QueryOperation::Acquire: diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 8c17639e43..dd9494efab 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -79,12 +79,15 @@ void Puller::ProcessSemaphoreTriggerMethod() { u64 timestamp; }; - Block block{}; - block.sequence = regs.semaphore_sequence; - // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of - // CoreTiming - block.timestamp = gpu.GetTicks(); - memory_manager.WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, sizeof(block)); + const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; + const u32 payload = regs.semaphore_sequence; + std::function operation([this, sequence_address, payload] { + Block block{}; + block.sequence = payload; + block.timestamp = gpu.GetTicks(); + memory_manager.WriteBlock(sequence_address, &block, sizeof(block)); + }); + rasterizer->SignalFence(std::move(operation)); } else { do { const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; @@ -94,6 +97,7 @@ void Puller::ProcessSemaphoreTriggerMethod() { regs.acquire_active = true; regs.acquire_mode = false; if (word != regs.acquire_value) { + rasterizer->ReleaseFences(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } @@ -101,11 +105,13 @@ void Puller::ProcessSemaphoreTriggerMethod() { regs.acquire_active = true; regs.acquire_mode = true; if (word < regs.acquire_value) { + rasterizer->ReleaseFences(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } } else if (op == GpuSemaphoreOperation::AcquireMask) { - if (word & regs.semaphore_sequence == 0) { + if (word && regs.semaphore_sequence == 0) { + rasterizer->ReleaseFences(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } @@ -117,16 +123,23 @@ void Puller::ProcessSemaphoreTriggerMethod() { } void Puller::ProcessSemaphoreRelease() { - rasterizer->SignalSemaphore(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release); + const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; + const u32 payload = regs.semaphore_release; + std::function operation([this, sequence_address, payload] { + memory_manager.Write(sequence_address, payload); + }); + rasterizer->SignalFence(std::move(operation)); } void Puller::ProcessSemaphoreAcquire() { - const u32 word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); + u32 word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); const auto value = regs.semaphore_acquire; - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - if (word != value) { + while (word != value) { regs.acquire_active = true; regs.acquire_value = value; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + rasterizer->ReleaseFences(); + word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); // TODO(kemathe73) figure out how to do the acquire_timeout regs.acquire_mode = false; regs.acquire_source = false; @@ -147,9 +160,9 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { case BufferMethods::SemaphoreAddressHigh: case BufferMethods::SemaphoreAddressLow: case BufferMethods::SemaphoreSequencePayload: - case BufferMethods::WrcacheFlush: case BufferMethods::SyncpointPayload: break; + case BufferMethods::WrcacheFlush: case BufferMethods::RefCnt: rasterizer->SignalReference(); break; @@ -173,7 +186,7 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { } case BufferMethods::MemOpB: { // Implement this better. - rasterizer->SyncGuestHost(); + rasterizer->InvalidateGPUCache(); break; } case BufferMethods::MemOpC: diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 03a70e5e01..c390ac91bc 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include @@ -19,28 +21,7 @@ namespace VideoCommon { class FenceBase { public: - explicit FenceBase(u32 payload_, bool is_stubbed_) - : address{}, payload{payload_}, is_semaphore{false}, is_stubbed{is_stubbed_} {} - - explicit FenceBase(u8* address_, u32 payload_, bool is_stubbed_) - : address{address_}, payload{payload_}, is_semaphore{true}, is_stubbed{is_stubbed_} {} - - u8* GetAddress() const { - return address; - } - - u32 GetPayload() const { - return payload; - } - - bool IsSemaphore() const { - return is_semaphore; - } - -private: - u8* address; - u32 payload; - bool is_semaphore; + explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {} protected: bool is_stubbed; @@ -60,31 +41,28 @@ public: buffer_cache.AccumulateFlushes(); } - void SignalSemaphore(u8* addr, u32 value) { + void SyncOperation(std::function&& func) { + uncommitted_operations.emplace_back(std::move(func)); + } + + void SignalFence(std::function&& func) { TryReleasePendingFences(); const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); - TFence new_fence = CreateFence(addr, value, !should_flush); + uncommitted_operations.emplace_back(std::move(func)); + CommitOperations(); + TFence new_fence = CreateFence(!should_flush); fences.push(new_fence); QueueFence(new_fence); if (should_flush) { rasterizer.FlushCommands(); } - rasterizer.SyncGuestHost(); } void SignalSyncPoint(u32 value) { syncpoint_manager.IncrementGuest(value); - TryReleasePendingFences(); - const bool should_flush = ShouldFlush(); - CommitAsyncFlushes(); - TFence new_fence = CreateFence(value, !should_flush); - fences.push(new_fence); - QueueFence(new_fence); - if (should_flush) { - rasterizer.FlushCommands(); - } - rasterizer.SyncGuestHost(); + std::function func([this, value] { syncpoint_manager.IncrementHost(value); }); + SignalFence(std::move(func)); } void WaitPendingFences() { @@ -94,12 +72,10 @@ public: WaitFence(current_fence); } PopAsyncFlushes(); - if (current_fence->IsSemaphore()) { - char* address = reinterpret_cast(current_fence->GetAddress()); - auto payload = current_fence->GetPayload(); - std::memcpy(address, &payload, sizeof(payload)); - } else { - syncpoint_manager.IncrementHost(current_fence->GetPayload()); + auto operations = std::move(pending_operations.front()); + pending_operations.pop_front(); + for (auto& operation : operations) { + operation(); } PopFence(); } @@ -114,11 +90,9 @@ protected: virtual ~FenceManager() = default; - /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is + /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is /// true - virtual TFence CreateFence(u32 value, bool is_stubbed) = 0; - /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true - virtual TFence CreateFence(u8* addr, u32 value, bool is_stubbed) = 0; + virtual TFence CreateFence(bool is_stubbed) = 0; /// Queues a fence into the backend if the fence isn't stubbed. virtual void QueueFence(TFence& fence) = 0; /// Notifies that the backend fence has been signaled/reached in host GPU. @@ -141,12 +115,10 @@ private: return; } PopAsyncFlushes(); - if (current_fence->IsSemaphore()) { - char* address = reinterpret_cast(current_fence->GetAddress()); - const auto payload = current_fence->GetPayload(); - std::memcpy(address, &payload, sizeof(payload)); - } else { - syncpoint_manager.IncrementHost(current_fence->GetPayload()); + auto operations = std::move(pending_operations.front()); + pending_operations.pop_front(); + for (auto& operation : operations) { + operation(); } PopFence(); } @@ -165,16 +137,20 @@ private: } void PopAsyncFlushes() { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - texture_cache.PopAsyncFlushes(); - buffer_cache.PopAsyncFlushes(); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.PopAsyncFlushes(); + buffer_cache.PopAsyncFlushes(); + } query_cache.PopAsyncFlushes(); } void CommitAsyncFlushes() { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - texture_cache.CommitAsyncFlushes(); - buffer_cache.CommitAsyncFlushes(); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.CommitAsyncFlushes(); + buffer_cache.CommitAsyncFlushes(); + } query_cache.CommitAsyncFlushes(); } @@ -183,7 +159,13 @@ private: fences.pop(); } + void CommitOperations() { + pending_operations.emplace_back(std::move(uncommitted_operations)); + } + std::queue fences; + std::deque> uncommitted_operations; + std::deque>> pending_operations; DelayedDestructionRing delayed_destruction_ring; }; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index a1d19b1c8a..d7a3dd96ba 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -93,16 +93,13 @@ struct GPU::Impl { } /// Synchronizes CPU writes with Host GPU memory. - void SyncGuestHost() { - rasterizer->SyncGuestHost(); + void InvalidateGPUCache() { + rasterizer->InvalidateGPUCache(); } /// Signal the ending of command list. void OnCommandListEnd() { - if (is_async) { - // This command only applies to asynchronous GPU mode - gpu_thread.OnCommandListEnd(); - } + gpu_thread.OnCommandListEnd(); } /// Request a host GPU memory flush from the CPU. @@ -296,7 +293,7 @@ struct GPU::Impl { } void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, - Service::Nvidia::NvFence* fences, size_t num_fences) { + std::array& fences, size_t num_fences) { size_t current_request_counter{}; { std::unique_lock lk(request_swap_mutex); @@ -412,8 +409,8 @@ void GPU::FlushCommands() { impl->FlushCommands(); } -void GPU::SyncGuestHost() { - impl->SyncGuestHost(); +void GPU::InvalidateGPUCache() { + impl->InvalidateGPUCache(); } void GPU::OnCommandListEnd() { @@ -488,7 +485,7 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { } void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, - Service::Nvidia::NvFence* fences, size_t num_fences) { + std::array& fences, size_t num_fences) { impl->RequestSwapBuffers(framebuffer, fences, num_fences); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 655373b33c..0a4a8b14fa 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -110,7 +110,7 @@ public: /// Flush all current written commands into the host GPU for execution. void FlushCommands(); /// Synchronizes CPU writes with Host GPU memory. - void SyncGuestHost(); + void InvalidateGPUCache(); /// Signal the ending of command list. void OnCommandListEnd(); @@ -180,7 +180,7 @@ public: void RendererFrameEndNotify(); void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, - Service::Nvidia::NvFence* fences, size_t num_fences); + std::array& fences, size_t num_fences); /// Performs any additional setup necessary in order to begin GPU emulation. /// This can be used to launch any necessary threads and register any necessary diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 2c03545bf1..1bd477011e 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -98,7 +98,7 @@ void ThreadManager::FlushRegion(VAddr addr, u64 size) { } void ThreadManager::TickGPU() { - PushCommand(GPUTickCommand(), true); + PushCommand(GPUTickCommand()); } void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 5362aafb67..cb07f3d38b 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -62,7 +62,10 @@ public: virtual void DisableGraphicsUniformBuffer(size_t stage, u32 index) = 0; /// Signal a GPU based semaphore as a fence - virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0; + virtual void SignalFence(std::function&& func) = 0; + + /// Send an operation to be done after a certain amount of flushes. + virtual void SyncOperation(std::function&& func) = 0; /// Signal a GPU based syncpoint as a fence virtual void SignalSyncPoint(u32 value) = 0; @@ -89,7 +92,7 @@ public: virtual void OnCPUWrite(VAddr addr, u64 size) = 0; /// Sync memory between guest and host. - virtual void SyncGuestHost() = 0; + virtual void InvalidateGPUCache() = 0; /// Unmap memory range virtual void UnmapMemory(VAddr addr, u64 size) = 0; diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp index c76446b605..91463f854f 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.cpp +++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp @@ -10,10 +10,7 @@ namespace OpenGL { -GLInnerFence::GLInnerFence(u32 payload_, bool is_stubbed_) : FenceBase{payload_, is_stubbed_} {} - -GLInnerFence::GLInnerFence(u8* address_, u32 payload_, bool is_stubbed_) - : FenceBase{address_, payload_, is_stubbed_} {} +GLInnerFence::GLInnerFence(bool is_stubbed_) : FenceBase{is_stubbed_} {} GLInnerFence::~GLInnerFence() = default; @@ -48,12 +45,8 @@ FenceManagerOpenGL::FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterize BufferCache& buffer_cache_, QueryCache& query_cache_) : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_} {} -Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) { - return std::make_shared(value, is_stubbed); -} - -Fence FenceManagerOpenGL::CreateFence(u8* addr, u32 value, bool is_stubbed) { - return std::make_shared(addr, value, is_stubbed); +Fence FenceManagerOpenGL::CreateFence(bool is_stubbed) { + return std::make_shared(is_stubbed); } void FenceManagerOpenGL::QueueFence(Fence& fence) { diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h index fced8d0029..f1446e732a 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.h +++ b/src/video_core/renderer_opengl/gl_fence_manager.h @@ -16,8 +16,7 @@ namespace OpenGL { class GLInnerFence : public VideoCommon::FenceBase { public: - explicit GLInnerFence(u32 payload_, bool is_stubbed_); - explicit GLInnerFence(u8* address_, u32 payload_, bool is_stubbed_); + explicit GLInnerFence(bool is_stubbed_); ~GLInnerFence(); void Queue(); @@ -40,8 +39,7 @@ public: QueryCache& query_cache); protected: - Fence CreateFence(u32 value, bool is_stubbed) override; - Fence CreateFence(u8* addr, u32 value, bool is_stubbed) override; + Fence CreateFence(bool is_stubbed) override; void QueueFence(Fence& fence) override; bool IsFenceSignaled(Fence& fence) const override; void WaitFence(Fence& fence) override; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index b572950a67..6ebd6cff94 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -358,7 +358,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { } } -void RasterizerOpenGL::SyncGuestHost() { +void RasterizerOpenGL::InvalidateGPUCache() { MICROPROFILE_SCOPE(OpenGL_CacheManagement); shader_cache.SyncGuestHost(); { @@ -386,13 +386,12 @@ void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { } } -void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { - if (!gpu.IsAsync()) { - gpu_memory->Write(addr, value); - return; - } - auto paddr = gpu_memory->GetPointer(addr); - fence_manager.SignalSemaphore(paddr, value); +void RasterizerOpenGL::SignalFence(std::function&& func) { + fence_manager.SignalFence(std::move(func)); +} + +void RasterizerOpenGL::SyncOperation(std::function&& func) { + fence_manager.SyncOperation(std::move(func)); } void RasterizerOpenGL::SignalSyncPoint(u32 value) { @@ -400,16 +399,10 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) { } void RasterizerOpenGL::SignalReference() { - if (!gpu.IsAsync()) { - return; - } fence_manager.SignalOrdering(); } void RasterizerOpenGL::ReleaseFences() { - if (!gpu.IsAsync()) { - return; - } fence_manager.WaitPendingFences(); } @@ -426,6 +419,7 @@ void RasterizerOpenGL::WaitForIdle() { } void RasterizerOpenGL::FragmentBarrier() { + glTextureBarrier(); glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d469075a13..fe0ba979ae 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -80,10 +80,11 @@ public: bool MustFlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override; - void SyncGuestHost() override; + void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; - void SignalSemaphore(GPUVAddr addr, u32 value) override; + void SignalFence(std::function&& func) override; + void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp index 301cbbabe8..0214b103a7 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp @@ -11,11 +11,8 @@ namespace Vulkan { -InnerFence::InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_) - : FenceBase{payload_, is_stubbed_}, scheduler{scheduler_} {} - -InnerFence::InnerFence(Scheduler& scheduler_, u8* address_, u32 payload_, bool is_stubbed_) - : FenceBase{address_, payload_, is_stubbed_}, scheduler{scheduler_} {} +InnerFence::InnerFence(Scheduler& scheduler_, bool is_stubbed_) + : FenceBase{is_stubbed_}, scheduler{scheduler_} {} InnerFence::~InnerFence() = default; @@ -48,12 +45,8 @@ FenceManager::FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::G : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_}, scheduler{scheduler_} {} -Fence FenceManager::CreateFence(u32 value, bool is_stubbed) { - return std::make_shared(scheduler, value, is_stubbed); -} - -Fence FenceManager::CreateFence(u8* addr, u32 value, bool is_stubbed) { - return std::make_shared(scheduler, addr, value, is_stubbed); +Fence FenceManager::CreateFence(bool is_stubbed) { + return std::make_shared(scheduler, is_stubbed); } void FenceManager::QueueFence(Fence& fence) { diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h index ea9e88052d..7fe2afcd91 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.h +++ b/src/video_core/renderer_vulkan/vk_fence_manager.h @@ -25,8 +25,7 @@ class Scheduler; class InnerFence : public VideoCommon::FenceBase { public: - explicit InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_); - explicit InnerFence(Scheduler& scheduler_, u8* address_, u32 payload_, bool is_stubbed_); + explicit InnerFence(Scheduler& scheduler_, bool is_stubbed_); ~InnerFence(); void Queue(); @@ -50,8 +49,7 @@ public: QueryCache& query_cache, const Device& device, Scheduler& scheduler); protected: - Fence CreateFence(u32 value, bool is_stubbed) override; - Fence CreateFence(u8* addr, u32 value, bool is_stubbed) override; + Fence CreateFence(bool is_stubbed) override; void QueueFence(Fence& fence) override; bool IsFenceSignaled(Fence& fence) const override; void WaitFence(Fence& fence) override; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d7b57e0f3d..a35e41199c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -428,7 +428,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { } } -void RasterizerVulkan::SyncGuestHost() { +void RasterizerVulkan::InvalidateGPUCache() { pipeline_cache.SyncGuestHost(); { std::scoped_lock lock{buffer_cache.mutex}; @@ -455,13 +455,12 @@ void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { } } -void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { - if (!gpu.IsAsync()) { - gpu_memory->Write(addr, value); - return; - } - auto paddr = gpu_memory->GetPointer(addr); - fence_manager.SignalSemaphore(paddr, value); +void RasterizerVulkan::SignalFence(std::function&& func) { + fence_manager.SignalFence(std::move(func)); +} + +void RasterizerVulkan::SyncOperation(std::function&& func) { + fence_manager.SyncOperation(std::move(func)); } void RasterizerVulkan::SignalSyncPoint(u32 value) { @@ -469,16 +468,10 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) { } void RasterizerVulkan::SignalReference() { - if (!gpu.IsAsync()) { - return; - } fence_manager.SignalOrdering(); } void RasterizerVulkan::ReleaseFences() { - if (!gpu.IsAsync()) { - return; - } fence_manager.WaitPendingFences(); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index c836158b87..fb9e83e8f3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -76,10 +76,11 @@ public: bool MustFlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override; - void SyncGuestHost() override; + void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; - void SignalSemaphore(GPUVAddr addr, u32 value) override; + void SignalFence(std::function&& func) override; + void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; From 5caa150e9a08d006a484c0b06a8f3fb603487d64 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 6 Feb 2022 01:56:38 +0100 Subject: [PATCH 074/245] OpenGL: Fix TickWork --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 6ebd6cff94..02bb177155 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -215,6 +215,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { if (!pipeline) { return; } + + gpu.TickWork(); + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); @@ -272,6 +275,7 @@ void RasterizerOpenGL::DispatchCompute() { if (!pipeline) { return; } + pipeline->SetEngine(kepler_compute, gpu_memory); pipeline->Configure(); const auto& qmd{kepler_compute->launch_description}; glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); From 359f22b808f54de9f19e40a8f93604ee7e4bac80 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 6 Feb 2022 18:51:07 +0100 Subject: [PATCH 075/245] MemoryManager: Finish up the initial implementation. --- src/video_core/memory_manager.cpp | 177 ++++++++++++++++++++++-------- src/video_core/memory_manager.h | 11 +- 2 files changed, 138 insertions(+), 50 deletions(-) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 836ece136d..e1a8b5391d 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -15,8 +15,6 @@ #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" -#pragma optimize("", off) - namespace Tegra { std::atomic MemoryManager::unique_identifier_generator{}; @@ -42,7 +40,7 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_entries.resize(big_page_table_size / 32, 0); big_page_table_cpu.resize(big_page_table_size); - big_page_table_physical.resize(big_page_table_size); + big_page_continous.resize(big_page_table_size / continous_bits, 0); entries.resize(page_table_size / 32, 0); } @@ -80,6 +78,19 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { } } +inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { + const u64 entry_mask = big_page_continous[big_page_index / continous_bits]; + const size_t sub_index = big_page_index % continous_bits; + return ((entry_mask >> sub_index) & 0x1ULL) != 0; +} + +inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value) { + const u64 continous_mask = big_page_continous[big_page_index / continous_bits]; + const size_t sub_index = big_page_index % continous_bits; + big_page_continous[big_page_index / continous_bits] = + (~(1ULL << sub_index) & continous_mask) | (value ? 1ULL << sub_index : 0); +} + template GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size) { @@ -121,9 +132,19 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr const auto index = PageEntryIndex(current_gpu_addr); const u32 sub_value = static_cast(current_cpu_addr >> cpu_page_bits); big_page_table_cpu[index] = sub_value; - const PAddr phys_address = - device_memory.GetPhysicalAddr(memory.GetPointer(current_cpu_addr)); - big_page_table_physical[index] = static_cast(phys_address); + const bool is_continous = ([&] { + uintptr_t base_ptr{ + reinterpret_cast(memory.GetPointer(current_cpu_addr))}; + for (VAddr start_cpu = current_cpu_addr + page_size; + start_cpu < current_cpu_addr + big_page_size; start_cpu += page_size) { + base_ptr += page_size; + if (base_ptr != reinterpret_cast(memory.GetPointer(start_cpu))) { + return false; + } + } + return true; + })(); + SetBigPageContinous(index, is_continous); } remaining_size -= big_page_size; } @@ -248,12 +269,17 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { return memory.GetPointer(*address); } +#ifdef _MSC_VER // no need for gcc / clang but msvc's compiler is more conservative with inlining. #pragma inline_recursion(on) +#endif template inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const { + static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v; + static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v; + static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v; u64 used_page_size; u64 used_page_mask; u64 used_page_bits; @@ -276,11 +302,31 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si std::min(static_cast(used_page_size) - page_offset, remaining_size)}; auto entry = GetEntry(current_address); if (entry == EntryType::Mapped) [[likely]] { - func_mapped(page_index, page_offset, copy_amount); + if constexpr (BOOL_BREAK_MAPPED) { + if (func_mapped(page_index, page_offset, copy_amount)) { + return; + } + } else { + func_mapped(page_index, page_offset, copy_amount); + } + } else if (entry == EntryType::Reserved) { - func_reserved(page_index, page_offset, copy_amount); + if constexpr (BOOL_BREAK_RESERVED) { + if (func_reserved(page_index, page_offset, copy_amount)) { + return; + } + } else { + func_reserved(page_index, page_offset, copy_amount); + } + } else [[unlikely]] { - func_unmapped(page_index, page_offset, copy_amount); + if constexpr (BOOL_BREAK_UNMAPPED) { + if (func_unmapped(page_index, page_offset, copy_amount)) { + return; + } + } else { + func_unmapped(page_index, page_offset, copy_amount); + } } page_index++; page_offset = 0; @@ -303,7 +349,8 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, if constexpr (is_safe) { rasterizer->FlushRegion(cpu_addr_base, copy_amount); } - memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(dest_buffer, physical, copy_amount); dest_buffer = static_cast(dest_buffer) + copy_amount; }; auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { @@ -312,9 +359,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, if constexpr (is_safe) { rasterizer->FlushRegion(cpu_addr_base, copy_amount); } - memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); - // u8* physical = device_memory.GetPointer(big_page_table_physical[page_index] + offset); - // std::memcpy(dest_buffer, physical, copy_amount); + if (!IsBigPageContinous(page_index)) { + memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); + } else { + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(dest_buffer, physical, copy_amount); + } dest_buffer = static_cast(dest_buffer) + copy_amount; }; auto read_short_pages = [&](std::size_t page_index, std::size_t offset, @@ -347,7 +397,8 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe if constexpr (is_safe) { rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); } - memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(physical, src_buffer, copy_amount); src_buffer = static_cast(src_buffer) + copy_amount; }; auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { @@ -356,10 +407,12 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe if constexpr (is_safe) { rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); } - memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); - /*u8* physical = - device_memory.GetPointer(big_page_table_physical[page_index] << cpu_page_bits) + offset; - std::memcpy(physical, src_buffer, copy_amount);*/ + if (!IsBigPageContinous(page_index)) { + memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); + } else { + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(physical, src_buffer, copy_amount); + } src_buffer = static_cast(src_buffer) + copy_amount; }; auto write_short_pages = [&](std::size_t page_index, std::size_t offset, @@ -413,48 +466,80 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std } bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { - const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; - if (!cpu_addr) { + if (GetEntry(gpu_addr) == EntryType::Mapped) [[likely]] { + size_t page_index = gpu_addr >> big_page_bits; + if (IsBigPageContinous(page_index)) [[likely]] { + const std::size_t page{(page_index & big_page_mask) + size}; + return page <= big_page_size; + } + const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; + return page <= Core::Memory::YUZU_PAGESIZE; + } + if (GetEntry(gpu_addr) != EntryType::Mapped) { return false; } - const std::size_t page{(*cpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; + const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; return page <= Core::Memory::YUZU_PAGESIZE; } bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { - size_t page_index{gpu_addr >> big_page_bits}; - const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; std::optional old_page_addr{}; - while (page_index != page_last) { - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (!page_addr || *page_addr == 0) { - return false; + bool result{true}; + auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + std::size_t copy_amount) { + result = false; + return true; + }; + auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + result = false; + return true; } - if (old_page_addr) { - if (*old_page_addr + page_size != *page_addr) { - return false; - } + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + result = false; + return true; } - old_page_addr = page_addr; - ++page_index; - } - return true; + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, short_check, fail, fail); + return !result; + }; + MemoryOperation(gpu_addr, size, big_check, fail, check_short_pages); + return result; } bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const { - size_t page_index{gpu_addr >> page_bits}; - const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; - while (page_index < page_last) { - if (GetEntry(page_index << page_bits) == EntryType::Free) { - return false; - } - ++page_index; - } - return true; + std::optional old_page_addr{}; + bool result{true}; + auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { + result = false; + return true; + }; + auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { return false; }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, pass, pass, fail); + return !result; + }; + MemoryOperation(gpu_addr, size, pass, fail, check_short_pages); + return result; } -#pragma inline_recursion(on) - std::vector> MemoryManager::GetSubmappedRange( GPUVAddr gpu_addr, std::size_t size) const { std::vector> result{}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 9c388a06e8..8f8877a923 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -105,9 +105,6 @@ public: void FlushRegion(GPUVAddr gpu_addr, size_t size) const; private: - [[nodiscard]] std::optional FindFreeRange(std::size_t size, std::size_t align, - bool start_32bit_address = false) const; - template inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; @@ -127,6 +124,9 @@ private: } } + inline bool IsBigPageContinous(size_t big_page_index) const; + inline void SetBigPageContinous(size_t big_page_index, bool value); + Core::System& system; Core::Memory::Memory& memory; Core::DeviceMemory& device_memory; @@ -169,7 +169,10 @@ private: Common::MultiLevelPageTable page_table; Common::VirtualBuffer big_page_table_cpu; - Common::VirtualBuffer big_page_table_physical; + + std::vector big_page_continous; + + constexpr static size_t continous_bits = 64; const size_t unique_identifier; From a283eda320ea1a4df5e6370528eb1b2ec64e466c Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 6 Mar 2022 11:03:48 +0100 Subject: [PATCH 076/245] Shader Decompiler: Fix dangerous behavior of invalid iterator insertion. --- .../frontend/maxwell/structured_control_flow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index 578bc8c1b7..ce42475d46 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp @@ -964,9 +964,9 @@ private: demote_endif_node.type = Type::EndIf; demote_endif_node.data.end_if.merge = return_block_it->data.block; - asl.insert(return_block_it, demote_endif_node); - asl.insert(return_block_it, demote_node); - asl.insert(return_block_it, demote_if_node); + const auto next_it_1 = asl.insert(return_block_it, demote_endif_node); + const auto next_it_2 = asl.insert(next_it_1, demote_node); + asl.insert(next_it_2, demote_if_node); } ObjectPool& stmt_pool; From ba34cf0a691bc73ae6b2d8db6019b1f10d22dde5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 6 Mar 2022 19:54:40 +0100 Subject: [PATCH 077/245] Shader Decompiler: Check for shift when deriving composite samplers. --- src/shader_recompiler/ir_opt/texture_pass.cpp | 34 +++++++++++++++++-- src/shader_recompiler/shader_info.h | 4 +++ .../renderer_opengl/gl_compute_pipeline.cpp | 5 +-- .../renderer_opengl/gl_graphics_pipeline.cpp | 5 +-- .../renderer_vulkan/vk_compute_pipeline.cpp | 4 +-- .../renderer_vulkan/vk_graphics_pipeline.cpp | 5 +-- 6 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 597112ba47..4bad811c22 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -19,8 +19,10 @@ namespace { struct ConstBufferAddr { u32 index; u32 offset; + u32 shift_left; u32 secondary_index; u32 secondary_offset; + u32 secondary_shift_left; IR::U32 dynamic_offset; u32 count; bool has_secondary; @@ -182,6 +184,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { switch (inst->GetOpcode()) { default: return std::nullopt; + case IR::Opcode::BitwiseXor32: case IR::Opcode::BitwiseOr32: { std::optional lhs{Track(inst->Arg(0))}; std::optional rhs{Track(inst->Arg(1))}; @@ -194,19 +197,33 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { if (lhs->count > 1 || rhs->count > 1) { return std::nullopt; } - if (lhs->index > rhs->index || lhs->offset > rhs->offset) { + if (lhs->shift_left > 0 || lhs->index > rhs->index || lhs->offset > rhs->offset) { std::swap(lhs, rhs); } return ConstBufferAddr{ .index = lhs->index, .offset = lhs->offset, + .shift_left = lhs->shift_left, .secondary_index = rhs->index, .secondary_offset = rhs->offset, + .secondary_shift_left = rhs->shift_left, .dynamic_offset = {}, .count = 1, .has_secondary = true, }; } + case IR::Opcode::ShiftLeftLogical32: { + const IR::Value shift{inst->Arg(1)}; + if (!shift.IsImmediate()) { + return std::nullopt; + } + std::optional lhs{Track(inst->Arg(0))}; + if (lhs) { + lhs->shift_left = shift.U32(); + } + return lhs; + break; + } case IR::Opcode::GetCbufU32x2: case IR::Opcode::GetCbufU32: break; @@ -222,8 +239,10 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { return ConstBufferAddr{ .index = index.U32(), .offset = offset.U32(), + .shift_left = 0, .secondary_index = 0, .secondary_offset = 0, + .secondary_shift_left = 0, .dynamic_offset = {}, .count = 1, .has_secondary = false, @@ -247,8 +266,10 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { return ConstBufferAddr{ .index = index.U32(), .offset = base_offset, + .shift_left = 0, .secondary_index = 0, .secondary_offset = 0, + .secondary_shift_left = 0, .dynamic_offset = dynamic_offset, .count = 8, .has_secondary = false, @@ -267,8 +288,10 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { addr = ConstBufferAddr{ .index = env.TextureBoundBuffer(), .offset = inst.Arg(0).U32(), + .shift_left = 0, .secondary_index = 0, .secondary_offset = 0, + .secondary_shift_left = 0, .dynamic_offset = {}, .count = 1, .has_secondary = false, @@ -284,8 +307,9 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; - const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)}; - const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)}; + const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; + const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) + << cbuf.secondary_shift_left}; return env.ReadTextureType(lhs_raw | rhs_raw); } @@ -487,8 +511,10 @@ void TexturePass(Environment& env, IR::Program& program) { .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, + .shift_left = cbuf.shift_left, .secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_offset = cbuf.secondary_offset, + .secondary_shift_left = cbuf.secondary_shift_left, .count = cbuf.count, .size_shift = DESCRIPTOR_SIZE_SHIFT, }); @@ -499,8 +525,10 @@ void TexturePass(Environment& env, IR::Program& program) { .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, + .shift_left = cbuf.shift_left, .secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_offset = cbuf.secondary_offset, + .secondary_shift_left = cbuf.secondary_shift_left, .count = cbuf.count, .size_shift = DESCRIPTOR_SIZE_SHIFT, }); diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f5690805c4..cc596da4f9 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -61,8 +61,10 @@ struct TextureBufferDescriptor { bool has_secondary; u32 cbuf_index; u32 cbuf_offset; + u32 shift_left; u32 secondary_cbuf_index; u32 secondary_cbuf_offset; + u32 secondary_shift_left; u32 count; u32 size_shift; }; @@ -85,8 +87,10 @@ struct TextureDescriptor { bool has_secondary; u32 cbuf_index; u32 cbuf_offset; + u32 shift_left; u32 secondary_cbuf_index; u32 secondary_cbuf_offset; + u32 secondary_shift_left; u32 count; u32 size_shift; }; diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index 26b51f442b..26d0660048 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp @@ -100,8 +100,9 @@ void ComputePipeline::Configure() { const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + secondary_offset}; - const u32 lhs_raw{gpu_memory->Read(addr)}; - const u32 rhs_raw{gpu_memory->Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory->Read(separate_addr) + << desc.secondary_shift_left}; return TexturePair(lhs_raw | rhs_raw, via_header_index); } } diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index c877d77926..41493a7da2 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -312,8 +312,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + second_offset}; - const u32 lhs_raw{gpu_memory->Read(addr)}; - const u32 rhs_raw{gpu_memory->Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory->Read(separate_addr) + << desc.secondary_shift_left}; const u32 raw{lhs_raw | rhs_raw}; return TexturePair(raw, via_header_index); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 6447210e27..7906e11a88 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -126,8 +126,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + secondary_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory.Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory.Read(separate_addr) << desc.secondary_shift_left}; return TexturePair(lhs_raw | rhs_raw, via_header_index); } } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 1e993185f9..f47786f48f 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -314,8 +314,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + second_offset}; - const u32 lhs_raw{gpu_memory->Read(addr)}; - const u32 rhs_raw{gpu_memory->Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory->Read(separate_addr) + << desc.secondary_shift_left}; const u32 raw{lhs_raw | rhs_raw}; return TexturePair(raw, via_header_index); } From 3d02143476cbc92450587c4e56eafe1cb76cd9ad Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 26 Mar 2022 13:40:42 +0100 Subject: [PATCH 078/245] Shader Decompiler: implement better tracking for Vulkan samplers. --- src/shader_recompiler/ir_opt/texture_pass.cpp | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 4bad811c22..0726d4d218 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -174,20 +174,41 @@ bool IsTextureInstruction(const IR::Inst& inst) { return IndexedInstruction(inst) != IR::Opcode::Void; } -std::optional TryGetConstBuffer(const IR::Inst* inst); +std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env); -std::optional Track(const IR::Value& value) { - return IR::BreadthFirstSearch(value, TryGetConstBuffer); +std::optional Track(const IR::Value& value, Environment& env) { + return IR::BreadthFirstSearch( + value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); }); } -std::optional TryGetConstBuffer(const IR::Inst* inst) { +std::optional TryGetConstant(IR::Value& value, Environment& env) { + const IR::Inst* inst = value.InstRecursive(); + if (inst->GetOpcode() != IR::Opcode::GetCbufU32) { + return std::nullopt; + } + const IR::Value index{inst->Arg(0)}; + const IR::Value offset{inst->Arg(1)}; + if (!index.IsImmediate()) { + return std::nullopt; + } + if (!offset.IsImmediate()) { + return std::nullopt; + } + const auto index_number = index.U32(); + if (index_number != 1) { + return std::nullopt; + } + const auto offset_number = offset.U32(); + return env.ReadCbufValue(index_number, offset_number); +} + +std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env) { switch (inst->GetOpcode()) { default: return std::nullopt; - case IR::Opcode::BitwiseXor32: case IR::Opcode::BitwiseOr32: { - std::optional lhs{Track(inst->Arg(0))}; - std::optional rhs{Track(inst->Arg(1))}; + std::optional lhs{Track(inst->Arg(0), env)}; + std::optional rhs{Track(inst->Arg(1), env)}; if (!lhs || !rhs) { return std::nullopt; } @@ -217,13 +238,42 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { if (!shift.IsImmediate()) { return std::nullopt; } - std::optional lhs{Track(inst->Arg(0))}; + std::optional lhs{Track(inst->Arg(0), env)}; if (lhs) { lhs->shift_left = shift.U32(); } return lhs; break; } + case IR::Opcode::BitwiseAnd32: { + IR::Value op1{inst->Arg(0)}; + IR::Value op2{inst->Arg(1)}; + if (op1.IsImmediate()) { + std::swap(op1, op2); + } + if (!op2.IsImmediate() && !op1.IsImmediate()) { + do { + auto try_index = TryGetConstant(op1, env); + if (try_index) { + op1 = op2; + op2 = IR::Value{*try_index}; + break; + } + auto try_index_2 = TryGetConstant(op2, env); + if (try_index_2) { + op2 = IR::Value{*try_index_2}; + break; + } + return std::nullopt; + } while (false); + } + std::optional lhs{Track(op1, env)}; + if (lhs) { + lhs->shift_left = std::countr_zero(op2.U32()); + } + return lhs; + break; + } case IR::Opcode::GetCbufU32x2: case IR::Opcode::GetCbufU32: break; @@ -279,7 +329,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { ConstBufferAddr addr; if (IsBindless(inst)) { - const std::optional track_addr{Track(inst.Arg(0))}; + const std::optional track_addr{Track(inst.Arg(0), env)}; if (!track_addr) { throw NotImplementedException("Failed to track bindless texture constant buffer"); } From 5a568b1655f0d721891083da19e2da2614796389 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 19 Feb 2022 14:18:02 +0100 Subject: [PATCH 079/245] MemoryManager: Fix errors popping out. --- src/core/memory.cpp | 9 +++++++++ src/core/memory.h | 1 + src/video_core/memory_manager.cpp | 12 ++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 34ad7cadd3..2ac792566e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -551,6 +551,11 @@ struct Memory::Impl { []() {}); } + [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const { + return GetPointerImpl( + vaddr, []() {}, []() {}); + } + /** * Reads a particular data type out of memory at the given virtual address. * @@ -686,6 +691,10 @@ u8* Memory::GetPointer(VAddr vaddr) { return impl->GetPointer(vaddr); } +u8* Memory::GetPointerSilent(VAddr vaddr) { + return impl->GetPointerSilent(vaddr); +} + const u8* Memory::GetPointer(VAddr vaddr) const { return impl->GetPointer(vaddr); } diff --git a/src/core/memory.h b/src/core/memory.h index a11ff8766b..81eac448b4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -114,6 +114,7 @@ public: * If the address is not valid, nullptr will be returned. */ u8* GetPointer(VAddr vaddr); + u8* GetPointerSilent(VAddr vaddr); template T* GetPointer(VAddr vaddr) { diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index e1a8b5391d..0f97db2720 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -134,11 +134,15 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr big_page_table_cpu[index] = sub_value; const bool is_continous = ([&] { uintptr_t base_ptr{ - reinterpret_cast(memory.GetPointer(current_cpu_addr))}; + reinterpret_cast(memory.GetPointerSilent(current_cpu_addr))}; + if (base_ptr == 0) { + return false; + } for (VAddr start_cpu = current_cpu_addr + page_size; start_cpu < current_cpu_addr + big_page_size; start_cpu += page_size) { base_ptr += page_size; - if (base_ptr != reinterpret_cast(memory.GetPointer(start_cpu))) { + auto next_ptr = reinterpret_cast(memory.GetPointerSilent(start_cpu)); + if (next_ptr == 0 || base_ptr != next_ptr) { return false; } } @@ -359,7 +363,7 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, if constexpr (is_safe) { rasterizer->FlushRegion(cpu_addr_base, copy_amount); } - if (!IsBigPageContinous(page_index)) { + if (!IsBigPageContinous(page_index)) [[unlikely]] { memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); } else { u8* physical = memory.GetPointer(cpu_addr_base); @@ -407,7 +411,7 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe if constexpr (is_safe) { rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); } - if (!IsBigPageContinous(page_index)) { + if (!IsBigPageContinous(page_index)) [[unlikely]] { memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); } else { u8* physical = memory.GetPointer(cpu_addr_base); From 7cfa28a6664fa79b5b12341a93e995d74cb0deef Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 2 Apr 2022 22:38:58 +0200 Subject: [PATCH 080/245] Memory Manager: ensure safety of GPU to CPU address. --- src/video_core/memory_manager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 0f97db2720..4e52ce0fd2 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -193,6 +193,9 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { + if (gpu_addr >= address_space_size) [[unlikely]] { + return std::nullopt; + } if (GetEntry(gpu_addr) != EntryType::Mapped) [[unlikely]] { if (GetEntry(gpu_addr) != EntryType::Mapped) { return std::nullopt; From b2099fbdcc3cb213aec8f836033fb02c4b6bbd09 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 26 Mar 2022 13:43:00 +0100 Subject: [PATCH 081/245] Maxwell3D: Add small_index_2 --- src/video_core/engines/maxwell_3d.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 950c70dcd9..add1ccebe3 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -219,6 +219,8 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume regs.index_array.count = regs.small_index_2.count; regs.index_array.first = regs.small_index_2.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; From f5fd6b5c8674fcf64a3e70809ee0a34d3a95beb6 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 14 Aug 2022 02:36:36 -0700 Subject: [PATCH 082/245] DMA & InlineToMemory Engines Rework. --- src/common/algorithm.h | 8 + src/video_core/buffer_cache/buffer_cache.h | 4 +- src/video_core/engines/engine_upload.cpp | 46 +++- src/video_core/engines/engine_upload.h | 6 +- src/video_core/engines/kepler_compute.cpp | 13 +- src/video_core/engines/kepler_memory.cpp | 13 +- src/video_core/engines/maxwell_3d.cpp | 5 +- src/video_core/engines/maxwell_dma.cpp | 91 ++++--- src/video_core/engines/maxwell_dma.h | 6 + src/video_core/host1x/vic.cpp | 5 +- src/video_core/memory_manager.cpp | 91 +++++++ src/video_core/memory_manager.h | 6 + src/video_core/rasterizer_interface.h | 2 +- .../renderer_opengl/gl_rasterizer.cpp | 2 +- .../renderer_opengl/gl_rasterizer.h | 2 +- .../renderer_vulkan/vk_compute_pass.cpp | 2 - .../renderer_vulkan/vk_rasterizer.cpp | 2 +- .../renderer_vulkan/vk_rasterizer.h | 2 +- src/video_core/texture_cache/util.cpp | 1 - src/video_core/textures/decoders.cpp | 225 ++++++------------ src/video_core/textures/decoders.h | 33 +-- 21 files changed, 323 insertions(+), 242 deletions(-) diff --git a/src/common/algorithm.h b/src/common/algorithm.h index 9ddfd637b6..055dca142d 100644 --- a/src/common/algorithm.h +++ b/src/common/algorithm.h @@ -24,4 +24,12 @@ template > return first != last && !comp(value, *first) ? first : last; } +template +T FoldRight(T initial_value, Func&& func, Args&&... args) { + T value{initial_value}; + const auto high_func = [&value, &func](T x) { value = func(value, x); }; + (std::invoke(high_func, std::forward(args)), ...); + return value; +} + } // namespace Common diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e55cac0d65..359c11d6f2 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -126,7 +126,7 @@ public: void DownloadMemory(VAddr cpu_addr, u64 size); - bool InlineMemory(VAddr dest_address, size_t copy_size, std::span inlined_buffer); + bool InlineMemory(VAddr dest_address, size_t copy_size, std::span inlined_buffer); void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size); @@ -1685,7 +1685,7 @@ void BufferCache

::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, template bool BufferCache

::InlineMemory(VAddr dest_address, size_t copy_size, - std::span inlined_buffer) { + std::span inlined_buffer) { const bool is_dirty = IsRegionRegistered(dest_address, copy_size); if (!is_dirty) { return false; diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp index 6ff5b1eca7..a348192346 100644 --- a/src/video_core/engines/engine_upload.cpp +++ b/src/video_core/engines/engine_upload.cpp @@ -3,6 +3,7 @@ #include +#include "common/algorithm.h" #include "common/assert.h" #include "video_core/engines/engine_upload.h" #include "video_core/memory_manager.h" @@ -34,21 +35,48 @@ void State::ProcessData(const u32 data, const bool is_last_call) { if (!is_last_call) { return; } + ProcessData(inner_buffer); +} + +void State::ProcessData(const u32* data, size_t num_data) { + std::span read_buffer(reinterpret_cast(data), num_data * sizeof(u32)); + ProcessData(read_buffer); +} + +void State::ProcessData(std::span read_buffer) { const GPUVAddr address{regs.dest.Address()}; if (is_linear) { - rasterizer->AccelerateInlineToMemory(address, copy_size, inner_buffer); + if (regs.line_count == 1) { + rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer); + } else { + for (u32 line = 0; line < regs.line_count; ++line) { + const GPUVAddr dest_line = address + static_cast(line) * regs.dest.pitch; + memory_manager.WriteBlockUnsafe( + dest_line, read_buffer.data() + static_cast(line) * regs.line_length_in, + regs.line_length_in); + } + memory_manager.InvalidateRegion(address, regs.dest.pitch * regs.line_count); + } } else { - UNIMPLEMENTED_IF(regs.dest.z != 0); - UNIMPLEMENTED_IF(regs.dest.depth != 1); - UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 0); - UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 0); + u32 width = regs.dest.width; + u32 x_elements = regs.line_length_in; + u32 x_offset = regs.dest.x; + const u32 bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + width, x_elements, x_offset, static_cast(address)); + width >>= bpp_shift; + x_elements >>= bpp_shift; + x_offset >>= bpp_shift; + const u32 bytes_per_pixel = 1U << bpp_shift; const std::size_t dst_size = Tegra::Texture::CalculateSize( - true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 0); + true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, + regs.dest.BlockHeight(), regs.dest.BlockDepth()); tmp_buffer.resize(dst_size); memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); - Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, regs.dest.y, - regs.dest.BlockHeight(), copy_size, inner_buffer.data(), - tmp_buffer.data()); + Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, + regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, + x_elements, regs.line_count, regs.dest.BlockHeight(), + regs.dest.BlockDepth(), regs.line_length_in); memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); } } diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 94ff3314a7..f08f6e36aa 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "common/bit_field.h" #include "common/common_types.h" @@ -33,7 +34,7 @@ struct Registers { u32 width; u32 height; u32 depth; - u32 z; + u32 layer; u32 x; u32 y; @@ -62,11 +63,14 @@ public: void ProcessExec(bool is_linear_); void ProcessData(u32 data, bool is_last_call); + void ProcessData(const u32* data, size_t num_data); /// Binds a rasterizer to this engine. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); private: + void ProcessData(std::span read_buffer); + u32 write_offset = 0; u32 copy_size = 0; std::vector inner_buffer; diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 5db254d948..7c50bdbe09 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -36,8 +36,6 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal } case KEPLER_COMPUTE_REG_INDEX(data_upload): { upload_state.ProcessData(method_argument, is_last_call); - if (is_last_call) { - } break; } case KEPLER_COMPUTE_REG_INDEX(launch): @@ -50,8 +48,15 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { - for (std::size_t i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + switch (method) { + case KEPLER_COMPUTE_REG_INDEX(data_upload): + upload_state.ProcessData(base_start, static_cast(amount)); + return; + default: + for (std::size_t i = 0; i < amount; i++) { + CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + } + break; } } diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index e2b0295427..a3fbab1e52 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -33,8 +33,6 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call } case KEPLERMEMORY_REG_INDEX(data): { upload_state.ProcessData(method_argument, is_last_call); - if (is_last_call) { - } break; } } @@ -42,8 +40,15 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { - for (std::size_t i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + switch (method) { + case KEPLERMEMORY_REG_INDEX(data): + upload_state.ProcessData(base_start, static_cast(amount)); + return; + default: + for (std::size_t i = 0; i < amount; i++) { + CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + } + break; } } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index add1ccebe3..632052c53c 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -239,8 +239,6 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume return upload_state.ProcessExec(regs.exec_upload.linear != 0); case MAXWELL3D_REG_INDEX(data_upload): upload_state.ProcessData(argument, is_last_call); - if (is_last_call) { - } return; case MAXWELL3D_REG_INDEX(fragment_barrier): return rasterizer->FragmentBarrier(); @@ -316,6 +314,9 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: ProcessCBMultiData(base_start, amount); break; + case MAXWELL3D_REG_INDEX(data_upload): + upload_state.ProcessData(base_start, static_cast(amount)); + return; default: for (std::size_t i = 0; i < amount; i++) { CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 0efe582824..a12a95ce27 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/algorithm.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -54,8 +55,6 @@ void MaxwellDMA::Launch() { const LaunchDMA& launch = regs.launch_dma; ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); - ASSERT(regs.dst_params.origin.x == 0); - ASSERT(regs.dst_params.origin.y == 0); const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; @@ -121,12 +120,13 @@ void MaxwellDMA::CopyPitchToPitch() { void MaxwellDMA::CopyBlockLinearToPitch() { UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0); - UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0); UNIMPLEMENTED_IF(regs.src_params.layer != 0); + const bool is_remapping = regs.launch_dma.remap_enable != 0; + // Optimized path for micro copies. const size_t dst_size = static_cast(regs.pitch_out) * regs.line_count; - if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X && + if (!is_remapping && dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X && regs.src_params.height > GOB_SIZE_Y) { FastCopyBlockLinearToPitch(); return; @@ -134,10 +134,27 @@ void MaxwellDMA::CopyBlockLinearToPitch() { // Deswizzle the input and copy it over. UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); - const u32 bytes_per_pixel = - regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1; const Parameters& src_params = regs.src_params; - const u32 width = src_params.width; + + const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; + const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1; + + const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size; + + u32 width = src_params.width; + u32 x_elements = regs.line_length_in; + u32 x_offset = src_params.origin.x; + u32 bpp_shift = 0U; + if (!is_remapping) { + bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + width, x_elements, x_offset, static_cast(regs.offset_in)); + width >>= bpp_shift; + x_elements >>= bpp_shift; + x_offset >>= bpp_shift; + } + + const u32 bytes_per_pixel = base_bpp << bpp_shift; const u32 height = src_params.height; const u32 depth = src_params.depth; const u32 block_height = src_params.block_size.height; @@ -155,30 +172,46 @@ void MaxwellDMA::CopyBlockLinearToPitch() { memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); - UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, - block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(), - read_buffer.data()); + UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, + src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, + regs.pitch_out); memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::CopyPitchToBlockLinear() { UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); + UNIMPLEMENTED_IF(regs.dst_params.layer != 0); UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); + const bool is_remapping = regs.launch_dma.remap_enable != 0; + const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; + const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1; + const auto& dst_params = regs.dst_params; - const u32 bytes_per_pixel = - regs.launch_dma.remap_enable ? regs.pitch_in / regs.line_length_in : 1; - const u32 width = dst_params.width; + + const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size; + + u32 width = dst_params.width; + u32 x_elements = regs.line_length_in; + u32 x_offset = dst_params.origin.x; + u32 bpp_shift = 0U; + if (!is_remapping) { + bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + width, x_elements, x_offset, static_cast(regs.offset_out)); + width >>= bpp_shift; + x_elements >>= bpp_shift; + x_offset >>= bpp_shift; + } + + const u32 bytes_per_pixel = base_bpp << bpp_shift; const u32 height = dst_params.height; const u32 depth = dst_params.depth; const u32 block_height = dst_params.block_size.height; const u32 block_depth = dst_params.block_size.depth; const size_t dst_size = CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); - const size_t dst_layer_size = - CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth); - const size_t src_size = static_cast(regs.pitch_in) * regs.line_count; if (read_buffer.size() < src_size) { @@ -188,32 +221,23 @@ void MaxwellDMA::CopyPitchToBlockLinear() { write_buffer.resize(dst_size); } + memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); if (Settings::IsGPULevelExtreme()) { - memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); } else { - memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size); memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); } // If the input is linear and the output is tiled, swizzle the input and copy it over. - if (regs.dst_params.block_size.depth > 0) { - ASSERT(dst_params.layer == 0); - SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height, - bytes_per_pixel, block_height, block_depth, dst_params.origin.x, - dst_params.origin.y, write_buffer.data(), read_buffer.data()); - } else { - SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel, - write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(), - block_height, dst_params.origin.x, dst_params.origin.y); - } + SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, + dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, + regs.pitch_in); memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::FastCopyBlockLinearToPitch() { - const u32 bytes_per_pixel = - regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1; + const u32 bytes_per_pixel = 1U; const size_t src_size = GOB_SIZE; const size_t dst_size = static_cast(regs.pitch_out) * regs.line_count; u32 pos_x = regs.src_params.origin.x; @@ -239,9 +263,10 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); } - UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, - bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y, - write_buffer.data(), read_buffer.data()); + UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, regs.src_params.width, + regs.src_params.height, 1, pos_x, pos_y, regs.line_length_in, regs.line_count, + regs.src_params.block_size.height, regs.src_params.block_size.depth, + regs.pitch_out); memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); } diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 074bac92c4..9c5d567a67 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -189,10 +189,16 @@ public: BitField<4, 3, Swizzle> dst_y; BitField<8, 3, Swizzle> dst_z; BitField<12, 3, Swizzle> dst_w; + BitField<0, 12, u32> dst_components_raw; BitField<16, 2, u32> component_size_minus_one; BitField<20, 2, u32> num_src_components_minus_one; BitField<24, 2, u32> num_dst_components_minus_one; }; + + Swizzle GetComponent(size_t i) { + const u32 raw = dst_components_raw; + return static_cast((raw >> (i * 3)) & 0x7); + } }; static_assert(sizeof(RemapConst) == 12); diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index 5d80398412..b9ac415295 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -156,8 +156,9 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { const u32 block_height = static_cast(config.block_linear_height_log2); const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); luma_buffer.resize(size); - Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), - converted_frame_buf_addr, block_height, 0, 0); + std::span frame_buff(converted_frame_buf_addr, 4 * width * height); + Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, + 0, 0, width, height, block_height, 0, width * 4); host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); } else { diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 4e52ce0fd2..4a692448ea 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -462,6 +462,97 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { MemoryOperation(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); } +bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { + bool result = false; + auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { return false; }; + + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); + return result; + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); + return result; + }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, do_nothing, do_nothing); + return result; + }; + MemoryOperation(gpu_addr, size, mapped_big, do_nothing, check_short_pages); + return result; +} + +size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const { + std::optional old_page_addr{}; + size_t range_so_far = 0; + bool result{false}; + auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + std::size_t copy_amount) { + result = true; + return true; + }; + auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + result = true; + return true; + } + range_so_far += copy_amount; + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + return true; + } + range_so_far += copy_amount; + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, short_check, fail, fail); + return result; + }; + MemoryOperation(gpu_addr, size, big_check, fail, check_short_pages); + return range_so_far; +} + +void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { + auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) {}; + + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + }; + auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, do_nothing, do_nothing); + }; + MemoryOperation(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages); +} + void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { std::vector tmp_buffer(size); ReadBlock(gpu_src_addr, tmp_buffer.data(), size); diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 8f8877a923..9c08edc20d 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -104,6 +104,12 @@ public: void FlushRegion(GPUVAddr gpu_addr, size_t size) const; + void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const; + + bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const; + + size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; + private: template inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index cb07f3d38b..d2d40884c2 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -129,7 +129,7 @@ public: [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0; virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) = 0; + std::span memory) = 0; /// Attempt to use a faster method to display the framebuffer to screen [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 02bb177155..c2d80605d2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -476,7 +476,7 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() } void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) { + std::span memory) { auto cpu_addr = gpu_memory->GpuToCpuAddress(address); if (!cpu_addr) [[unlikely]] { gpu_memory->WriteBlock(address, memory.data(), copy_size); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index fe0ba979ae..45131b7852 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -99,7 +99,7 @@ public: const Tegra::Engines::Fermi2D::Config& copy_config) override; Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) override; + std::span memory) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index f17a5ccd6e..241d7573ed 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -26,8 +26,6 @@ namespace Vulkan { -using Tegra::Texture::SWIZZLE_TABLE; - namespace { constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a35e41199c..acfd5da7d6 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -548,7 +548,7 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() } void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) { + std::span memory) { auto cpu_addr = gpu_memory->GpuToCpuAddress(address); if (!cpu_addr) [[unlikely]] { gpu_memory->WriteBlock(address, memory.data(), copy_size); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index fb9e83e8f3..4cde3c983a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -95,7 +95,7 @@ public: const Tegra::Engines::Fermi2D::Config& copy_config) override; Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) override; + std::span memory) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index bea1c27d06..1223df5a0a 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -517,7 +517,6 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block; UNIMPLEMENTED_IF(info.tile_width_spacing > 0); - UNIMPLEMENTED_IF(copy.image_offset.x != 0); UNIMPLEMENTED_IF(copy.image_offset.y != 0); UNIMPLEMENTED_IF(copy.image_offset.z != 0); diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 913f8ebcb3..fcc636e0b9 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -89,6 +89,69 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 } } +template +void SwizzleSubrectImpl(std::span output, std::span input, u32 width, u32 height, + u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 num_lines, + u32 block_height, u32 block_depth, u32 pitch_linear) { + // The origin of the transformation can be configured here, leave it as zero as the current API + // doesn't expose it. + static constexpr u32 origin_z = 0; + + // We can configure here a custom pitch + // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch. + const u32 pitch = pitch_linear; + const u32 stride = Common::AlignUpLog2(width * BYTES_PER_PIXEL, GOB_SIZE_X_SHIFT); + + const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); + const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); + const u32 slice_size = + Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size; + + const u32 block_height_mask = (1U << block_height) - 1; + const u32 block_depth_mask = (1U << block_depth) - 1; + const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth; + + u32 unprocessed_lines = num_lines; + u32 extent_y = std::min(num_lines, height - origin_y); + + for (u32 slice = 0; slice < depth; ++slice) { + const u32 z = slice + origin_z; + const u32 offset_z = (z >> block_depth) * slice_size + + ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); + const u32 lines_in_y = std::min(unprocessed_lines, extent_y); + for (u32 line = 0; line < lines_in_y; ++line) { + const u32 y = line + origin_y; + const u32 swizzled_y = pdep(y); + + const u32 block_y = y >> GOB_SIZE_Y_SHIFT; + const u32 offset_y = (block_y >> block_height) * block_size + + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); + + u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); + for (u32 column = 0; column < extent_x; + ++column, incrpdep(swizzled_x)) { + const u32 x = (column + origin_x) * BYTES_PER_PIXEL; + const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; + + const u32 base_swizzled_offset = offset_z + offset_y + offset_x; + const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); + + const u32 unswizzled_offset = + slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; + + u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; + const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; + + std::memcpy(dst, src, BYTES_PER_PIXEL); + } + } + unprocessed_lines -= lines_in_y; + if (unprocessed_lines == 0) { + return; + } + } +} + template void Swizzle(std::span output, std::span input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { @@ -111,97 +174,6 @@ void Swizzle(std::span output, std::span input, u32 bytes_per_pixe } } -template -void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit, - u32 offset_x, u32 offset_y) { - const u32 block_height = 1U << block_height_bit; - const u32 image_width_in_gobs = - (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X; - for (u32 line = 0; line < subrect_height; ++line) { - const u32 dst_y = line + offset_y; - const u32 gob_address_y = - (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + - ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; - - const u32 swizzled_y = pdep(dst_y); - u32 swizzled_x = pdep(offset_x * BYTES_PER_PIXEL); - for (u32 x = 0; x < subrect_width; - ++x, incrpdep(swizzled_x)) { - const u32 dst_x = x + offset_x; - const u32 gob_address = - gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; - const u32 swizzled_offset = gob_address + (swizzled_x | swizzled_y); - const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; - - const u8* const source_line = unswizzled_data + unswizzled_offset; - u8* const dest_addr = swizzled_data + swizzled_offset; - std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL); - } - } -} - -template -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height, - u32 origin_x, u32 origin_y, u8* output, const u8* input) { - const u32 stride = width * BYTES_PER_PIXEL; - const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; - const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); - - const u32 block_height_mask = (1U << block_height) - 1; - const u32 x_shift = GOB_SIZE_SHIFT + block_height; - - for (u32 line = 0; line < line_count; ++line) { - const u32 src_y = line + origin_y; - const u32 swizzled_y = pdep(src_y); - - const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT; - const u32 src_offset_y = (block_y >> block_height) * block_size + - ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - - u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); - for (u32 column = 0; column < line_length_in; - ++column, incrpdep(swizzled_x)) { - const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; - const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; - - const u32 swizzled_offset = src_offset_y + src_offset_x + (swizzled_x | swizzled_y); - const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; - - std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); - } - } -} - -template -void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output, - const u8* input) { - UNIMPLEMENTED_IF(origin_x > 0); - UNIMPLEMENTED_IF(origin_y > 0); - - const u32 stride = width * BYTES_PER_PIXEL; - const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; - const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); - - const u32 block_height_mask = (1U << block_height) - 1; - const u32 x_shift = static_cast(GOB_SIZE_SHIFT) + block_height + block_depth; - - for (u32 line = 0; line < line_count; ++line) { - const u32 swizzled_y = pdep(line); - const u32 block_y = line / GOB_SIZE_Y; - const u32 dst_offset_y = - (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE; - - u32 swizzled_x = 0; - for (u32 x = 0; x < line_length_in; ++x, incrpdep(swizzled_x)) { - const u32 dst_offset = - ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + (swizzled_x | swizzled_y); - const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; - std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); - } - } -} } // Anonymous namespace void UnswizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, @@ -218,15 +190,15 @@ void SwizzleTexture(std::span output, std::span input, u32 bytes_p stride_alignment); } -void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, - u32 block_height_bit, u32 offset_x, u32 offset_y) { +void SwizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, u32 width, + u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y, + u32 block_height, u32 block_depth, u32 pitch_linear) { switch (bytes_per_pixel) { #define BPP_CASE(x) \ case x: \ - return SwizzleSubrect(subrect_width, subrect_height, source_pitch, swizzled_width, \ - swizzled_data, unswizzled_data, block_height_bit, offset_x, \ - offset_y); + return SwizzleSubrectImpl(output, input, width, height, depth, origin_x, \ + origin_y, extent_x, extent_y, block_height, \ + block_depth, pitch_linear); BPP_CASE(1) BPP_CASE(2) BPP_CASE(3) @@ -241,13 +213,15 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 } } -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, - u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { +void UnswizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, + u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, + u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear) { switch (bytes_per_pixel) { #define BPP_CASE(x) \ case x: \ - return UnswizzleSubrect(line_length_in, line_count, pitch, width, block_height, \ - origin_x, origin_y, output, input); + return SwizzleSubrectImpl(output, input, width, height, depth, origin_x, \ + origin_y, extent_x, extent_y, block_height, \ + block_depth, pitch_linear); BPP_CASE(1) BPP_CASE(2) BPP_CASE(3) @@ -262,55 +236,6 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, } } -void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, - u32 origin_y, u8* output, const u8* input) { - switch (bytes_per_pixel) { -#define BPP_CASE(x) \ - case x: \ - return SwizzleSliceToVoxel(line_length_in, line_count, pitch, width, height, \ - block_height, block_depth, origin_x, origin_y, output, \ - input); - BPP_CASE(1) - BPP_CASE(2) - BPP_CASE(3) - BPP_CASE(4) - BPP_CASE(6) - BPP_CASE(8) - BPP_CASE(12) - BPP_CASE(16) -#undef BPP_CASE - default: - ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); - } -} - -void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, - const u32 block_height_bit, const std::size_t copy_size, const u8* source_data, - u8* swizzle_data) { - const u32 block_height = 1U << block_height_bit; - const u32 image_width_in_gobs{(width + GOB_SIZE_X - 1) / GOB_SIZE_X}; - std::size_t count = 0; - for (std::size_t y = dst_y; y < height && count < copy_size; ++y) { - const std::size_t gob_address_y = - (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + - ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; - const u32 swizzled_y = pdep(static_cast(y)); - u32 swizzled_x = pdep(dst_x); - for (std::size_t x = dst_x; x < width && count < copy_size; - ++x, incrpdep(swizzled_x)) { - const std::size_t gob_address = - gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height; - const std::size_t swizzled_offset = gob_address + (swizzled_x | swizzled_y); - const u8* source_line = source_data + count; - u8* dest_addr = swizzle_data + swizzled_offset; - count++; - - *dest_addr = *source_line; - } - } -} - std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth) { if (tiled) { diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 31a11708f0..e704076923 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -40,7 +40,6 @@ constexpr SwizzleTable MakeSwizzleTable() { } return table; } -constexpr SwizzleTable SWIZZLE_TABLE = MakeSwizzleTable(); /// Unswizzles a block linear texture into linear memory. void UnswizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, @@ -57,34 +56,14 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height u32 block_height, u32 block_depth); /// Copies an untiled subrectangle into a tiled surface. -void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, - u32 block_height_bit, u32 offset_x, u32 offset_y); +void SwizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, u32 width, + u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y, + u32 block_height, u32 block_depth, u32 pitch_linear); /// Copies a tiled subrectangle into a linear surface. -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, - u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input); - -/// @brief Swizzles a 2D array of pixels into a 3D texture -/// @param line_length_in Number of pixels per line -/// @param line_count Number of lines -/// @param pitch Number of bytes per line -/// @param width Width of the swizzled texture -/// @param height Height of the swizzled texture -/// @param bytes_per_pixel Number of bytes used per pixel -/// @param block_height Block height shift -/// @param block_depth Block depth shift -/// @param origin_x Column offset in pixels of the swizzled texture -/// @param origin_y Row offset in pixels of the swizzled texture -/// @param output Pointer to the pixels of the swizzled texture -/// @param input Pointer to the 2D array of pixels used as input -/// @pre input and output points to an array large enough to hold the number of bytes used -void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, - u32 origin_y, u8* output, const u8* input); - -void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height, - std::size_t copy_size, const u8* source_data, u8* swizzle_data); +void UnswizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, + u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, + u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear); /// Obtains the offset of the gob for positions 'dst_x' & 'dst_y' u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height, From 98317f2b7799360ee4e5e55cbbb123bce72fbf2c Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 3 Apr 2022 02:18:03 +0200 Subject: [PATCH 083/245] Decoders: Improve overall speed. --- src/video_core/textures/decoders.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index fcc636e0b9..52d067a2dc 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -35,7 +35,7 @@ void incrpdep(u32& value) { template void SwizzleImpl(std::span output, std::span input, u32 width, u32 height, u32 depth, - u32 block_height, u32 block_depth, u32 stride_alignment) { + u32 block_height, u32 block_depth, u32 stride) { // The origin of the transformation can be configured here, leave it as zero as the current API // doesn't expose it. static constexpr u32 origin_x = 0; @@ -45,7 +45,6 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 // We can configure here a custom pitch // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch. const u32 pitch = width * BYTES_PER_PIXEL; - const u32 stride = Common::AlignUpLog2(width, stride_alignment) * BYTES_PER_PIXEL; const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); @@ -179,15 +178,23 @@ void Swizzle(std::span output, std::span input, u32 bytes_per_pixe void UnswizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { + const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel; + const u32 new_bpp = std::min(4U, static_cast(std::countr_zero(width * bytes_per_pixel))); + width = (width * bytes_per_pixel) >> new_bpp; + bytes_per_pixel = 1U << new_bpp; Swizzle(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); + stride); } void SwizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { + const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel; + const u32 new_bpp = std::min(4U, static_cast(std::countr_zero(width * bytes_per_pixel))); + width = (width * bytes_per_pixel) >> new_bpp; + bytes_per_pixel = 1U << new_bpp; Swizzle(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); + stride); } void SwizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, u32 width, From b59ca4df0cf45f1c17555ae3029bab234d4241ac Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 6 Mar 2022 14:52:35 +0100 Subject: [PATCH 084/245] Buffer Cache: Basic fixes. --- src/video_core/buffer_cache/buffer_cache.h | 37 +++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 359c11d6f2..6eb6724759 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -350,7 +350,7 @@ private: void NotifyBufferDeletion(); - [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; + [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, bool is_written = false) const; [[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size, PixelFormat format); @@ -725,7 +725,7 @@ void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, const auto& cbufs = maxwell3d->state.shader_stages[stage]; const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; - storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr); + storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, is_written); } template @@ -765,7 +765,7 @@ void BufferCache

::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, const auto& cbufs = launch_desc.const_buffer_config; const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset; - compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr); + compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr, is_written); } template @@ -1242,16 +1242,19 @@ void BufferCache

::BindHostComputeTextureBuffers() { template void BufferCache

::DoUpdateGraphicsBuffers(bool is_indexed) { - if (is_indexed) { - UpdateIndexBuffer(); - } - UpdateVertexBuffers(); - UpdateTransformFeedbackBuffers(); - for (size_t stage = 0; stage < NUM_STAGES; ++stage) { - UpdateUniformBuffers(stage); - UpdateStorageBuffers(stage); - UpdateTextureBuffers(stage); - } + do { + has_deleted_buffers = false; + if (is_indexed) { + UpdateIndexBuffer(); + } + UpdateVertexBuffers(); + UpdateTransformFeedbackBuffers(); + for (size_t stage = 0; stage < NUM_STAGES; ++stage) { + UpdateUniformBuffers(stage); + UpdateStorageBuffers(stage); + UpdateTextureBuffers(stage); + } + } while (has_deleted_buffers); } template @@ -1557,6 +1560,8 @@ BufferId BufferCache

::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); + auto& new_buffer = slot_buffers[new_buffer_id]; + runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } @@ -1831,16 +1836,18 @@ void BufferCache

::NotifyBufferDeletion() { } template -typename BufferCache

::Binding BufferCache

::StorageBufferBinding(GPUVAddr ssbo_addr) const { +typename BufferCache

::Binding BufferCache

::StorageBufferBinding(GPUVAddr ssbo_addr, + bool is_written) const { const GPUVAddr gpu_addr = gpu_memory->Read(ssbo_addr); const u32 size = gpu_memory->Read(ssbo_addr + 8); const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr || size == 0) { return NULL_BINDING; } + const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); const Binding binding{ .cpu_addr = *cpu_addr, - .size = size, + .size = is_written ? size : static_cast(cpu_end - *cpu_addr), .buffer_id = BufferId{}, }; return binding; From a9ca39f8591532ba6d37f7a3e068d5eefe416464 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 7 Feb 2022 07:52:04 +0100 Subject: [PATCH 085/245] NVDRV: Further improvements. --- src/core/hle/service/nvdrv/core/container.cpp | 8 +- src/core/hle/service/nvdrv/core/container.h | 8 +- src/core/hle/service/nvdrv/core/nvmap.cpp | 7 +- src/core/hle/service/nvdrv/core/nvmap.h | 7 +- .../service/nvdrv/core/syncpoint_manager.cpp | 112 +++++++++++++--- .../service/nvdrv/core/syncpoint_manager.h | 124 ++++++++++++------ .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 18 ++- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 59 ++++----- .../hle/service/nvdrv/devices/nvhost_gpu.h | 19 ++- .../service/nvdrv/devices/nvhost_nvdec.cpp | 2 +- .../nvdrv/devices/nvhost_nvdec_common.cpp | 15 +-- .../nvdrv/devices/nvhost_nvdec_common.h | 6 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 2 +- src/video_core/engines/maxwell_3d.cpp | 18 +-- src/video_core/engines/maxwell_dma.cpp | 18 ++- src/video_core/engines/puller.cpp | 18 +-- 16 files changed, 280 insertions(+), 161 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index fbd66f0016..4175d3d9c3 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -1,7 +1,7 @@ -// Copyright 2021 yuzu emulator team -// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index da75d74ff7..e069ade4e5 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -1,7 +1,7 @@ -// Copyright 2021 yuzu emulator team -// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 9acec7ba66..86d825af96 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -1,6 +1,7 @@ -// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #include "common/alignment.h" #include "common/assert.h" diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index 5acdc961e6..4f37dcf437 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -1,6 +1,7 @@ -// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index 61e00448c9..b34481b486 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -1,5 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #include "common/assert.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" @@ -7,32 +9,108 @@ namespace Service::Nvidia::NvCore { -SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} +SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} { + constexpr u32 VBlank0SyncpointId{26}; + constexpr u32 VBlank1SyncpointId{27}; + + // Reserve both vblank syncpoints as client managed as they use Continuous Mode + // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode + // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660 + ReserveSyncpoint(VBlank0SyncpointId, true); + ReserveSyncpoint(VBlank1SyncpointId, true); + + for (u32 syncpointId : channel_syncpoints) { + if (syncpointId) { + ReserveSyncpoint(syncpointId, false); + } + } +} SyncpointManager::~SyncpointManager() = default; -u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { - syncpoints[syncpoint_id].min = host1x.GetSyncpointManager().GetHostSyncpointValue(syncpoint_id); - return GetSyncpointMin(syncpoint_id); +u32 SyncpointManager::ReserveSyncpoint(u32 id, bool clientManaged) { + if (syncpoints.at(id).reserved) { + UNREACHABLE_MSG("Requested syncpoint is in use"); + return 0; + } + + syncpoints.at(id).reserved = true; + syncpoints.at(id).interfaceManaged = clientManaged; + + return id; } -u32 SyncpointManager::AllocateSyncpoint() { - for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) { - if (!syncpoints[syncpoint_id].is_allocated) { - syncpoints[syncpoint_id].is_allocated = true; - return syncpoint_id; +u32 SyncpointManager::FindFreeSyncpoint() { + for (u32 i{1}; i < syncpoints.size(); i++) { + if (!syncpoints[i].reserved) { + return i; } } - ASSERT_MSG(false, "No more available syncpoints!"); - return {}; + UNREACHABLE_MSG("Failed to find a free syncpoint!"); + return 0; } -u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { - for (u32 index = 0; index < value; ++index) { - syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed); +u32 SyncpointManager::AllocateSyncpoint(bool clientManaged) { + std::lock_guard lock(reservation_lock); + return ReserveSyncpoint(FindFreeSyncpoint(), clientManaged); +} + +bool SyncpointManager::IsSyncpointAllocated(u32 id) { + return (id <= SyncpointCount) && syncpoints[id].reserved; +} + +bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) { + const SyncpointInfo& syncpoint{syncpoints.at(id)}; + + if (!syncpoint.reserved) { + UNREACHABLE(); + return 0; } - return GetSyncpointMax(syncpoint_id); + // If the interface manages counters then we don't keep track of the maximum value as it handles + // sanity checking the values then + if (syncpoint.interfaceManaged) { + return static_cast(syncpoint.counterMin - threshold) >= 0; + } else { + return (syncpoint.counterMax - threshold) >= (syncpoint.counterMin - threshold); + } +} + +u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { + if (!syncpoints.at(id).reserved) { + UNREACHABLE(); + return 0; + } + + return syncpoints.at(id).counterMax += amount; +} + +u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { + if (!syncpoints.at(id).reserved) { + UNREACHABLE(); + return 0; + } + + return syncpoints.at(id).counterMin; +} + +u32 SyncpointManager::UpdateMin(u32 id) { + if (!syncpoints.at(id).reserved) { + UNREACHABLE(); + return 0; + } + + syncpoints.at(id).counterMin = host1x.GetSyncpointManager().GetHostSyncpointValue(id); + return syncpoints.at(id).counterMin; +} + +NvFence SyncpointManager::GetSyncpointFence(u32 id) { + if (!syncpoints.at(id).reserved) { + UNREACHABLE(); + return NvFence{}; + } + + return {.id = static_cast(id), .value = syncpoints.at(id).counterMax}; } } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h index f332edc6e4..bfc8ba84b0 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -1,10 +1,13 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors +// (https://github.com/skyline-emu/) +// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 +// or any later version Refer to the license.txt file included. #pragma once #include #include +#include #include "common/common_types.h" #include "core/hle/service/nvdrv/nvdata.h" @@ -19,68 +22,111 @@ class Host1x; namespace Service::Nvidia::NvCore { +enum class ChannelType : u32 { + MsEnc = 0, + VIC = 1, + GPU = 2, + NvDec = 3, + Display = 4, + NvJpg = 5, + TSec = 6, + Max = 7 +}; + +/** + * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached + * versions of the HW syncpoints which are intermittently synced + * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them + * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c + */ class SyncpointManager final { public: explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); ~SyncpointManager(); /** - * Returns true if the specified syncpoint is expired for the given value. - * @param syncpoint_id Syncpoint ID to check. - * @param value Value to check against the specified syncpoint. - * @returns True if the specified syncpoint is expired for the given value, otherwise False. + * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints */ - bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const { - return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value); + bool IsSyncpointAllocated(u32 id); + + /** + * @brief Finds a free syncpoint and reserves it + * @return The ID of the reserved syncpoint + */ + u32 AllocateSyncpoint(bool clientManaged); + + /** + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259 + */ + bool HasSyncpointExpired(u32 id, u32 threshold); + + bool IsFenceSignalled(NvFence fence) { + return HasSyncpointExpired(fence.id, fence.value); } /** - * Gets the lower bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the lower bound for. - * @returns The lower bound for the specified syncpoint. + * @brief Atomically increments the maximum value of a syncpoint by the given amount + * @return The new max value of the syncpoint */ - u32 GetSyncpointMin(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed); - } + u32 IncrementSyncpointMaxExt(u32 id, u32 amount); /** - * Gets the uper bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the upper bound for. - * @returns The upper bound for the specified syncpoint. + * @return The minimum value of the syncpoint */ - u32 GetSyncpointMax(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed); - } + u32 ReadSyncpointMinValue(u32 id); /** - * Refreshes the minimum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be refreshed. - * @returns The new syncpoint minimum value. + * @brief Synchronises the minimum value of the syncpoint to with the GPU + * @return The new minimum value of the syncpoint */ - u32 RefreshSyncpoint(u32 syncpoint_id); + u32 UpdateMin(u32 id); /** - * Allocates a new syncoint. - * @returns The syncpoint ID for the newly allocated syncpoint. + * @return A fence that will be signalled once this syncpoint hits its maximum value */ - u32 AllocateSyncpoint(); + NvFence GetSyncpointFence(u32 id); - /** - * Increases the maximum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be increased. - * @param value Value to increase the specified syncpoint by. - * @returns The new syncpoint maximum value. - */ - u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value); + static constexpr std::array(ChannelType::Max)> channel_syncpoints{ + 0x0, // `MsEnc` is unimplemented + 0xC, // `VIC` + 0x0, // `GPU` syncpoints are allocated per-channel instead + 0x36, // `NvDec` + 0x0, // `Display` is unimplemented + 0x37, // `NvJpg` + 0x0, // `TSec` is unimplemented + }; //!< Maps each channel ID to a constant syncpoint private: - struct Syncpoint { - std::atomic min; - std::atomic max; - std::atomic is_allocated; + /** + * @note reservation_lock should be locked when calling this + */ + u32 ReserveSyncpoint(u32 id, bool clientManaged); + + /** + * @return The ID of the first free syncpoint + */ + u32 FindFreeSyncpoint(); + + struct SyncpointInfo { + std::atomic counterMin; //!< The least value the syncpoint can be (The value it was + //!< when it was last synchronized with host1x) + std::atomic counterMax; //!< The maximum value the syncpoint can reach according to the + //!< current usage + bool interfaceManaged; //!< If the syncpoint is managed by a host1x client interface, a + //!< client interface is a HW block that can handle host1x + //!< transactions on behalf of a host1x client (Which would otherwise + //!< need to be manually synced using PIO which is synchronous and + //!< requires direct cooperation of the CPU) + bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved + //!< value }; - std::array syncpoints{}; + constexpr static std::size_t SyncpointCount{192}; + std::array syncpoints{}; + std::mutex reservation_lock; Tegra::Host1x::Host1x& host1x; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 076edb02f7..a84e4d4251 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -112,17 +112,23 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector } if (params.fence.value == 0) { - params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id); + if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) { + LOG_WARNING(Service_NVDRV, + "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); + } else { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); + } return NvResult::Success; } - if (syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) { - params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id); + if (syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); return NvResult::Success; } - if (const auto new_value = syncpoint_manager.RefreshSyncpoint(fence_id); - syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) { + if (const auto new_value = syncpoint_manager.UpdateMin(fence_id); + syncpoint_manager.IsFenceSignalled(params.fence)) { params.value.raw = new_value; return NvResult::Success; } @@ -296,7 +302,7 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v EventState::Waiting) { auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle); - syncpoint_manager.RefreshSyncpoint(event.assigned_syncpt); + syncpoint_manager.UpdateMin(event.assigned_syncpt); event.wait_handle = {}; } event.fails++; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 3f981af5a1..c2cc09993f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -31,9 +31,7 @@ nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, : nvdevice{system_}, events_interface{events_interface_}, core{core_}, syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, channel_state{system.GPU().AllocateChannel()} { - channel_fence.id = syncpoint_manager.AllocateSyncpoint(); - channel_fence.value = - system_.Host1x().GetSyncpointManager().GetGuestSyncpointValue(channel_fence.id); + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); sm_exception_breakpoint_int_report_event = events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); sm_exception_breakpoint_pause_report_event = @@ -191,10 +189,8 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector& input, std::vector BuildWaitCommandList(NvFence fence) { }; } -static std::vector BuildIncrementCommandList(NvFence fence, - u32 add_increment) { +static std::vector BuildIncrementCommandList(NvFence fence) { std::vector result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; - for (u32 count = 0; count < add_increment; ++count) { + for (u32 count = 0; count < 2; ++count) { result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing)); result.emplace_back( @@ -239,14 +234,12 @@ static std::vector BuildIncrementCommandList(NvFence fence return result; } -static std::vector BuildIncrementWithWfiCommandList(NvFence fence, - u32 add_increment) { +static std::vector BuildIncrementWithWfiCommandList(NvFence fence) { std::vector result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; - const std::vector increment{ - BuildIncrementCommandList(fence, add_increment)}; + const std::vector increment{BuildIncrementCommandList(fence)}; result.insert(result.end(), increment.begin(), increment.end()); @@ -260,35 +253,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector auto& gpu = system.GPU(); + std::scoped_lock lock(channel_mutex); + const auto bind_id = channel_state->bind_id; - params.fence_out.id = channel_fence.id; + auto& flags = params.flags; - if (params.flags.add_wait.Value() && - !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { - gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); - } + if (flags.fence_wait.Value()) { + if (flags.increment_value.Value()) { + return NvResult::BadParameter; + } - if (params.flags.add_increment.Value() || params.flags.increment.Value()) { - const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; - params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( - params.fence_out.id, params.AddIncrementValue() + increment_value); - } else { - params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); + if (!syncpoint_manager.IsFenceSignalled(params.fence)) { + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)}); + } } gpu.PushGPUEntries(bind_id, std::move(entries)); + params.fence.id = channel_syncpoint; - if (params.flags.add_increment.Value()) { - if (params.flags.suppress_wfi) { - gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildIncrementCommandList( - params.fence_out, params.AddIncrementValue())}); + u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + + (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; + params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); + + if (flags.fence_increment.Value()) { + if (flags.suppress_wfi.Value()) { + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementCommandList(params.fence)}); } else { - gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildIncrementWithWfiCommandList( - params.fence_out, params.AddIncrementValue())}); + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)}); } } + flags.raw = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 3a65ed06db..1e4ecd55b3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -163,17 +163,13 @@ private: u32_le num_entries{}; // number of fence objects being submitted union { u32_le raw; - BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list - BitField<1, 1, u32_le> add_increment; // append an increment to the list - BitField<2, 1, u32_le> new_hw_format; // mostly ignored - BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt - BitField<8, 1, u32_le> increment; // increment the returned fence + BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list + BitField<1, 1, u32_le> fence_increment; // append an increment to the list + BitField<2, 1, u32_le> new_hw_format; // mostly ignored + BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt + BitField<8, 1, u32_le> increment_value; // increment the returned fence } flags; - NvFence fence_out{}; // returned new fence object for others to wait on - - u32 AddIncrementValue() const { - return flags.add_increment.Value() << 1; - } + NvFence fence{}; // returned new fence object for others to wait on }; static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), "IoctlSubmitGpfifo is incorrect size"); @@ -213,7 +209,8 @@ private: NvCore::SyncpointManager& syncpoint_manager; NvCore::NvMap& nvmap; std::shared_ptr channel_state; - NvFence channel_fence; + u32 channel_syncpoint; + std::mutex channel_mutex; // Events Kernel::KEvent* sm_exception_breakpoint_int_report_event; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 00947ea199..5e3820085a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices { u32 nvhost_nvdec::next_id{}; nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core) - : nvhost_nvdec_common{system_, core} {} + : nvhost_nvdec_common{system_, core, NvCore::ChannelType::NvDec} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index b17589aa3e..008092dbb3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -48,9 +48,10 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s std::unordered_map nvhost_nvdec_common::fd_to_id{}; -nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_) - : nvdevice{system_}, core{core_}, - syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} {} +nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, + NvCore::ChannelType channel_type_) + : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, + nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {} nvhost_nvdec_common::~nvhost_nvdec_common() = default; NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { @@ -88,7 +89,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector& input, for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i]; fence_thresholds[i] = - syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); + syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); } } for (const auto& cmd_buffer : command_buffers) { @@ -116,10 +117,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector& input, std::ve std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); - if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { - device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); - } - params.value = device_syncpoints[params.param]; + const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast(channel_type)]}; + params.value = id; std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); return NvResult::Success; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 53029af6ad..51bb7c2cb6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -6,6 +6,7 @@ #include #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia { @@ -13,14 +14,14 @@ namespace Service::Nvidia { namespace NvCore { class Container; class NvMap; -class SyncpointManager; } // namespace NvCore namespace Devices { class nvhost_nvdec_common : public nvdevice { public: - explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core); + explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core, + NvCore::ChannelType channel_type); ~nvhost_nvdec_common() override; protected: @@ -121,6 +122,7 @@ protected: NvCore::Container& core; NvCore::SyncpointManager& syncpoint_manager; NvCore::NvMap& nvmap; + NvCore::ChannelType channel_type; std::array device_syncpoints{}; }; }; // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index c89ff6b27d..490e399f4c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -12,7 +12,7 @@ namespace Service::Nvidia::Devices { u32 nvhost_vic::next_id{}; nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core) - : nvhost_nvdec_common{system_, core} {} + : nvhost_nvdec_common{system_, core, NvCore::ChannelType::VIC} {} nvhost_vic::~nvhost_vic() = default; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 632052c53c..3c6e44a252 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -453,18 +453,10 @@ void Maxwell3D::ProcessFirmwareCall4() { } void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { - struct LongQueryResult { - u64_le value; - u64_le timestamp; - }; - static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size"); const GPUVAddr sequence_address{regs.query.QueryAddress()}; if (long_query) { - // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast - // GPU, this command may actually take a while to complete in real hardware due to GPU - // wait queues. - LongQueryResult query_result{payload, system.GPU().GetTicks()}; - memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result)); + memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); + memory_manager.Write(sequence_address, payload); } else { memory_manager.Write(sequence_address, static_cast(payload)); } @@ -493,10 +485,10 @@ void Maxwell3D::ProcessQueryGet() { const GPUVAddr sequence_address{regs.query.QueryAddress()}; const u32 payload = regs.query.query_sequence; std::function operation([this, sequence_address, payload] { - LongQueryResult query_result{payload, system.GPU().GetTicks()}; - memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result)); + memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); + memory_manager.Write(sequence_address, payload); }); - rasterizer->SignalFence(std::move(operation)); + rasterizer->SyncOperation(std::move(operation)); } break; case Regs::QueryOperation::Acquire: diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index a12a95ce27..bcffd1862d 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -274,16 +274,24 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { void MaxwellDMA::ReleaseSemaphore() { const auto type = regs.launch_dma.semaphore_type; const GPUVAddr address = regs.semaphore.address; + const u32 payload = regs.semaphore.payload; switch (type) { case LaunchDMA::SemaphoreType::NONE: break; - case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: - memory_manager.Write(address, regs.semaphore.payload); + case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: { + std::function operation( + [this, address, payload] { memory_manager.Write(address, payload); }); + rasterizer->SignalFence(std::move(operation)); break; - case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: - memory_manager.Write(address, static_cast(regs.semaphore.payload)); - memory_manager.Write(address + 8, system.GPU().GetTicks()); + } + case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: { + std::function operation([this, address, payload] { + memory_manager.Write(address + sizeof(u64), system.GPU().GetTicks()); + memory_manager.Write(address, payload); + }); + rasterizer->SignalFence(std::move(operation)); break; + } default: ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast(type.Value())); } diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index dd9494efab..c3ed11c13e 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -59,6 +59,7 @@ void Puller::ProcessFenceActionMethod() { case Puller::FenceOperation::Acquire: // UNIMPLEMENTED_MSG("Channel Scheduling pending."); // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); + rasterizer->ReleaseFences(); break; case Puller::FenceOperation::Increment: rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); @@ -73,19 +74,11 @@ void Puller::ProcessSemaphoreTriggerMethod() { const auto op = static_cast(regs.semaphore_trigger & semaphoreOperationMask); if (op == GpuSemaphoreOperation::WriteLong) { - struct Block { - u32 sequence; - u32 zeros = 0; - u64 timestamp; - }; - const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const u32 payload = regs.semaphore_sequence; std::function operation([this, sequence_address, payload] { - Block block{}; - block.sequence = payload; - block.timestamp = gpu.GetTicks(); - memory_manager.WriteBlock(sequence_address, &block, sizeof(block)); + memory_manager.Write(sequence_address + sizeof(u64), gpu.GetTicks()); + memory_manager.Write(sequence_address, payload); }); rasterizer->SignalFence(std::move(operation)); } else { @@ -98,7 +91,6 @@ void Puller::ProcessSemaphoreTriggerMethod() { regs.acquire_mode = false; if (word != regs.acquire_value) { rasterizer->ReleaseFences(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } } else if (op == GpuSemaphoreOperation::AcquireGequal) { @@ -106,13 +98,11 @@ void Puller::ProcessSemaphoreTriggerMethod() { regs.acquire_mode = true; if (word < regs.acquire_value) { rasterizer->ReleaseFences(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } } else if (op == GpuSemaphoreOperation::AcquireMask) { if (word && regs.semaphore_sequence == 0) { rasterizer->ReleaseFences(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } } else { @@ -128,7 +118,7 @@ void Puller::ProcessSemaphoreRelease() { std::function operation([this, sequence_address, payload] { memory_manager.Write(sequence_address, payload); }); - rasterizer->SignalFence(std::move(operation)); + rasterizer->SyncOperation(std::move(operation)); } void Puller::ProcessSemaphoreAcquire() { From 8bb604b3bec4792ee642d865f49690e1ea181a44 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 26 Mar 2022 14:03:02 +0100 Subject: [PATCH 086/245] VideoCore: Add option to dump the macros. --- src/video_core/macro/macro.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index 43f8b59049..f61d5998ec 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -8,6 +8,7 @@ #include +#include #include "common/assert.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" From 770e19f51a0767c8fc2b204d9d50fa504a3c4e44 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 13 Apr 2022 16:20:34 +0200 Subject: [PATCH 087/245] Buffer Cache: Deduce vertex array limit from memory layout when limit is the highest possible. --- src/video_core/buffer_cache/buffer_cache.h | 10 +++++++--- src/video_core/memory_manager.cpp | 2 +- src/video_core/memory_manager.h | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 6eb6724759..2e616cee4d 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1316,12 +1316,16 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { const GPUVAddr gpu_addr_begin = array.StartAddress(); const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); - const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); - const u32 size = address_size; // TODO: Analyze stride and number of vertices - if (array.enable == 0 || size == 0 || !cpu_addr) { + u32 address_size = static_cast( + std::min(gpu_addr_end - gpu_addr_begin, static_cast(std::numeric_limits::max()))); + if (array.enable == 0 || address_size == 0 || !cpu_addr) { vertex_buffers[index] = NULL_BINDING; return; } + if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { + address_size = gpu_memory->MaxContinousRange(gpu_addr_begin, address_size); + } + const u32 size = address_size; // TODO: Analyze stride and number of vertices vertex_buffers[index] = Binding{ .cpu_addr = *cpu_addr, .size = size, diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 4a692448ea..3cb2d9224b 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -193,7 +193,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { - if (gpu_addr >= address_space_size) [[unlikely]] { + if (!IsWithinGPUAddressRange(gpu_addr)) [[unlikely]] { return std::nullopt; } if (GetEntry(gpu_addr) != EntryType::Mapped) [[unlikely]] { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 9c08edc20d..ae4fd98dfa 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -110,6 +110,10 @@ public: size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; + bool IsWithinGPUAddressRange(GPUVAddr gpu_addr) const { + return gpu_addr < address_space_size; + } + private: template inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, From fd7afda1e802fffe843ac28811470432949fe7ee Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 13 Apr 2022 20:19:06 +0200 Subject: [PATCH 088/245] VideoCore: Implement formats needed for N64 emulation. --- src/video_core/renderer_opengl/maxwell_to_gl.h | 2 +- src/video_core/renderer_vulkan/maxwell_to_vk.cpp | 2 +- src/video_core/renderer_vulkan/vk_texture_cache.cpp | 2 +- src/video_core/surface.h | 8 ++++---- src/video_core/texture_cache/format_lookup_table.cpp | 2 +- src/video_core/texture_cache/formatter.h | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index dfe7f26ca2..0044212365 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -87,7 +87,7 @@ constexpr std::array FORMAT_TAB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM - {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM + {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // G4R4_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 6703b8e688..e7104d377c 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -184,7 +184,7 @@ struct FormatTuple { {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM - {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM + {VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index caca79d79f..8d2728cd78 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -592,7 +592,7 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::array BLOCK_WIDTH_TABLE = {{ 4, // BC3_SRGB 4, // BC7_SRGB 1, // A4B4G4R4_UNORM - 1, // R4G4_UNORM + 1, // G4R4_UNORM 4, // ASTC_2D_4X4_SRGB 8, // ASTC_2D_8X8_SRGB 8, // ASTC_2D_8X5_SRGB @@ -323,7 +323,7 @@ constexpr std::array BLOCK_HEIGHT_TABLE = {{ 4, // BC3_SRGB 4, // BC7_SRGB 1, // A4B4G4R4_UNORM - 1, // R4G4_UNORM + 1, // G4R4_UNORM 4, // ASTC_2D_4X4_SRGB 8, // ASTC_2D_8X8_SRGB 5, // ASTC_2D_8X5_SRGB @@ -428,7 +428,7 @@ constexpr std::array BITS_PER_BLOCK_TABLE = {{ 128, // BC3_SRGB 128, // BC7_UNORM 16, // A4B4G4R4_UNORM - 8, // R4G4_UNORM + 8, // G4R4_UNORM 128, // ASTC_2D_4X4_SRGB 128, // ASTC_2D_8X8_SRGB 128, // ASTC_2D_8X5_SRGB diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index c71694d2ab..ad935d3860 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -63,7 +63,7 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, case Hash(TextureFormat::A4B4G4R4, UNORM): return PixelFormat::A4B4G4R4_UNORM; case Hash(TextureFormat::G4R4, UNORM): - return PixelFormat::R4G4_UNORM; + return PixelFormat::G4R4_UNORM; case Hash(TextureFormat::A5B5G5R1, UNORM): return PixelFormat::A5B5G5R1_UNORM; case Hash(TextureFormat::R8, UNORM): diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 6881e4c901..acc8547159 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h @@ -153,8 +153,8 @@ struct fmt::formatter : fmt::formatter Date: Wed, 13 Apr 2022 21:02:55 +0200 Subject: [PATCH 089/245] General: Fix compilation for GCC --- src/common/address_space.h | 10 +++-- src/common/algorithm.h | 2 +- src/common/bit_field.h | 13 +++---- src/common/multi_level_page_table.cpp | 4 +- src/common/multi_level_page_table.inc | 2 +- src/core/hle/service/nvdrv/core/nvmap.cpp | 38 ++++++++++++------- src/core/hle/service/nvdrv/core/nvmap.h | 1 + .../service/nvdrv/devices/nvhost_as_gpu.cpp | 3 ++ .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 8 ++-- .../service/nvdrv/devices/nvhost_nvdec.cpp | 4 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 4 +- src/core/hle/service/nvdrv/devices/nvmap.cpp | 2 +- src/core/hle/service/nvdrv/nvdrv.h | 1 + src/core/hle/service/vi/vi.cpp | 1 + src/shader_recompiler/ir_opt/texture_pass.cpp | 2 +- src/video_core/buffer_cache/buffer_cache.h | 3 +- 16 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/common/address_space.h b/src/common/address_space.h index fd2f32b7d3..8e13935aff 100644 --- a/src/common/address_space.h +++ b/src/common/address_space.h @@ -22,7 +22,8 @@ struct EmptyStruct {}; */ template -requires AddressSpaceValid class FlatAddressSpaceMap { +requires AddressSpaceValid +class FlatAddressSpaceMap { private: std::function unmapCallback{}; //!< Callback called when the mappings in an region have changed @@ -40,8 +41,8 @@ protected: Block() = default; - Block(VaType virt, PaType phys, ExtraBlockInfo extraInfo) - : virt(virt), phys(phys), extraInfo(extraInfo) {} + Block(VaType virt_, PaType phys_, ExtraBlockInfo extraInfo_) + : virt(virt_), phys(phys_), extraInfo(extraInfo_) {} constexpr bool Valid() { return virt != UnmappedVa; @@ -102,7 +103,8 @@ public: * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block */ template -requires AddressSpaceValid class FlatAllocator +requires AddressSpaceValid +class FlatAllocator : public FlatAddressSpaceMap { private: using Base = FlatAddressSpaceMap; diff --git a/src/common/algorithm.h b/src/common/algorithm.h index 055dca142d..c27c9241d9 100644 --- a/src/common/algorithm.h +++ b/src/common/algorithm.h @@ -27,7 +27,7 @@ template > template T FoldRight(T initial_value, Func&& func, Args&&... args) { T value{initial_value}; - const auto high_func = [&value, &func](T x) { value = func(value, x); }; + const auto high_func = [&value, &func](U x) { value = func(value, x); }; (std::invoke(high_func, std::forward(args)), ...); return value; } diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 368b7b98c9..7e1df62b1c 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -127,14 +127,11 @@ public: } } - BitField(T val) { - Assign(val); - } - - BitField& operator=(T val) { - Assign(val); - return *this; - } + // This constructor and assignment operator might be considered ambiguous: + // Would they initialize the storage or just the bitfield? + // Hence, delete them. Use the Assign method to set bitfield values! + BitField(T val) = delete; + BitField& operator=(T val) = delete; constexpr BitField() noexcept = default; diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp index aed04d0b59..3a7a75aa71 100644 --- a/src/common/multi_level_page_table.cpp +++ b/src/common/multi_level_page_table.cpp @@ -1,8 +1,6 @@ #include "common/multi_level_page_table.inc" namespace Common { -template class Common::MultiLevelPageTable; -template class Common::MultiLevelPageTable; -template class Common::MultiLevelPageTable; +template class Common::MultiLevelPageTable; template class Common::MultiLevelPageTable; } // namespace Common diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc index 9a68cad936..4def6dba85 100644 --- a/src/common/multi_level_page_table.inc +++ b/src/common/multi_level_page_table.inc @@ -30,7 +30,7 @@ MultiLevelPageTable::MultiLevelPageTable(std::size_t address_space_bit #ifdef _WIN32 void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)}; #else - void* base{mmap(nullptr, alloc_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; + void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; if (base == MAP_FAILED) { base = nullptr; diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 86d825af96..b02dbb9c98 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -13,7 +13,8 @@ using Core::Memory::YUZU_PAGESIZE; namespace Service::Nvidia::NvCore { -NvMap::Handle::Handle(u64 size, Id id) : size(size), aligned_size(size), orig_size(size), id(id) { +NvMap::Handle::Handle(u64 size_, Id id_) + : size(size_), aligned_size(size), orig_size(size), id(id_) { flags.raw = 0; } @@ -21,19 +22,21 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) std::scoped_lock lock(mutex); // Handles cannot be allocated twice - if (allocated) + if (allocated) { return NvResult::AccessDenied; + } flags = pFlags; kind = pKind; align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; // This flag is only applicable for handles with an address passed - if (pAddress) - flags.keep_uncached_after_free = 0; - else + if (pAddress) { + flags.keep_uncached_after_free.Assign(0); + } else { LOG_CRITICAL(Service_NVDRV, "Mapping nvmap handles without a CPU side address is unimplemented!"); + } size = Common::AlignUp(size, YUZU_PAGESIZE); aligned_size = Common::AlignUp(size, align); @@ -48,17 +51,19 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) NvResult NvMap::Handle::Duplicate(bool internal_session) { // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) - if (!allocated) [[unlikely]] + if (!allocated) [[unlikely]] { return NvResult::BadValue; + } std::scoped_lock lock(mutex); // If we internally use FromId the duplication tracking of handles won't work accurately due to // us not implementing per-process handle refs. - if (internal_session) + if (internal_session) { internal_dupes++; - else + } else { dupes++; + } return NvResult::Success; } @@ -92,8 +97,9 @@ bool NvMap::TryRemoveHandle(const Handle& handle_description) { std::scoped_lock lock(handles_lock); auto it{handles.find(handle_description.id)}; - if (it != handles.end()) + if (it != handles.end()) { handles.erase(it); + } return true; } else { @@ -102,8 +108,9 @@ bool NvMap::TryRemoveHandle(const Handle& handle_description) { } NvResult NvMap::CreateHandle(u64 size, std::shared_ptr& result_out) { - if (!size) [[unlikely]] + if (!size) [[unlikely]] { return NvResult::BadValue; + } u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; auto handle_description{std::make_shared(size, id)}; @@ -133,8 +140,9 @@ VAddr NvMap::GetHandleAddress(Handle::Id handle) { u32 NvMap::PinHandle(NvMap::Handle::Id handle) { auto handle_description{GetHandle(handle)}; - if (!handle_description) [[unlikely]] + if (!handle_description) [[unlikely]] { return 0; + } std::scoped_lock lock(handle_description->mutex); if (!handle_description->pins) { @@ -183,8 +191,9 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { void NvMap::UnpinHandle(Handle::Id handle) { auto handle_description{GetHandle(handle)}; - if (!handle_description) + if (!handle_description) { return; + } std::scoped_lock lock(handle_description->mutex); if (--handle_description->pins < 0) { @@ -226,12 +235,13 @@ std::optional NvMap::FreeHandle(Handle::Id handle, bool interna // Try to remove the shared ptr to the handle from the map, if nothing else is using the // handle then it will now be freed when `handle_description` goes out of scope - if (TryRemoveHandle(*handle_description)) + if (TryRemoveHandle(*handle_description)) { LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); - else + } else { LOG_DEBUG(Service_NVDRV, "Tried to free nvmap handle: {} but didn't as it still has duplicates", handle); + } freeInfo = { .address = handle_description->address, diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index 4f37dcf437..1082bb58df 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -5,6 +5,7 @@ #pragma once +#include #include #include #include diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index d95a883931..d1beefba6a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -188,6 +188,7 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector& input, std::vector< allocation_map[params.offset] = { .size = size, + .mappings{}, .page_size = params.page_size, .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, .big_pages = params.page_size != VM::YUZU_PAGESIZE, @@ -474,11 +475,13 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { VaRegion{ .offset = vm.small_page_allocator->vaStart << VM::PAGE_SIZE_BITS, .page_size = VM::YUZU_PAGESIZE, + ._pad0_{}, .pages = vm.small_page_allocator->vaLimit - vm.small_page_allocator->vaStart, }, VaRegion{ .offset = vm.big_page_allocator->vaStart << vm.big_page_size_bits, .page_size = vm.big_page_size, + ._pad0_{}, .pages = vm.big_page_allocator->vaLimit - vm.big_page_allocator->vaStart, }, }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index a84e4d4251..7fffb8e48c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -204,12 +204,12 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector event.wait_handle = host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() { - auto& event = events[slot]; - if (event.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + auto& event_ = events[slot]; + if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == EventState::Waiting) { - event.kevent->GetWritableEvent().Signal(); + event_.kevent->GetWritableEvent().Signal(); } - event.status.store(EventState::Signalled, std::memory_order_release); + event_.status.store(EventState::Signalled, std::memory_order_release); }); return NvResult::Timeout; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 5e3820085a..fed5370394 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -12,8 +12,8 @@ namespace Service::Nvidia::Devices { u32 nvhost_nvdec::next_id{}; -nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core) - : nvhost_nvdec_common{system_, core, NvCore::ChannelType::NvDec} {} +nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 490e399f4c..2e4ff988cd 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -11,8 +11,8 @@ namespace Service::Nvidia::Devices { u32 nvhost_vic::next_id{}; -nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core) - : nvhost_nvdec_common{system_, core, NvCore::ChannelType::VIC} {} +nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {} nvhost_vic::~nvhost_vic() = default; diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 992c117f1a..f84fc8c379 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -269,7 +269,7 @@ NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { params.address = freeInfo->address; params.size = static_cast(freeInfo->size); params.flags.raw = 0; - params.flags.map_uncached = freeInfo->was_uncached; + params.flags.map_uncached.Assign(freeInfo->was_uncached); } else { // This is possible when there's internel dups or other duplicates. } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 31c45236e6..b262547539 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -6,6 +6,7 @@ #pragma once #include +#include #include #include #include diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index f083811ec9..9c917cacfe 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -58,6 +58,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); class NativeWindow final { public: constexpr explicit NativeWindow(u32 id_) : id{id_} {} + constexpr explicit NativeWindow(const NativeWindow& other) = default; private: const u32 magic = 2; diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 0726d4d218..e8be58357b 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -269,7 +269,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme } std::optional lhs{Track(op1, env)}; if (lhs) { - lhs->shift_left = std::countr_zero(op2.U32()); + lhs->shift_left = static_cast(std::countr_zero(op2.U32())); } return lhs; break; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 2e616cee4d..8e26b3f953 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1323,7 +1323,8 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { return; } if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { - address_size = gpu_memory->MaxContinousRange(gpu_addr_begin, address_size); + address_size = + static_cast(gpu_memory->MaxContinousRange(gpu_addr_begin, address_size)); } const u32 size = address_size; // TODO: Analyze stride and number of vertices vertex_buffers[index] = Binding{ From 8fd1d769fe1c079d3a3f1ddc531233eabf0dd7ba Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 15 Apr 2022 13:43:27 +0200 Subject: [PATCH 090/245] ImageBase: Basic fixes. --- src/video_core/texture_cache/image_base.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index f61e09ac72..91512022fe 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp @@ -7,6 +7,7 @@ #include #include "common/common_types.h" +#include "common/div_ceil.h" #include "video_core/surface.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/image_base.h" @@ -182,10 +183,6 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i }; const bool is_lhs_compressed = lhs_block.width > 1 || lhs_block.height > 1; const bool is_rhs_compressed = rhs_block.width > 1 || rhs_block.height > 1; - if (is_lhs_compressed && is_rhs_compressed) { - LOG_ERROR(HW_GPU, "Compressed to compressed image aliasing is not implemented"); - return; - } const s32 lhs_mips = lhs.info.resources.levels; const s32 rhs_mips = rhs.info.resources.levels; const s32 num_mips = std::min(lhs_mips - base->level, rhs_mips); @@ -199,12 +196,12 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i Extent3D lhs_size = MipSize(lhs.info.size, base->level + mip_level); Extent3D rhs_size = MipSize(rhs.info.size, mip_level); if (is_lhs_compressed) { - lhs_size.width /= lhs_block.width; - lhs_size.height /= lhs_block.height; + lhs_size.width = Common::DivCeil(lhs_size.width, lhs_block.width); + lhs_size.height = Common::DivCeil(lhs_size.height, lhs_block.height); } if (is_rhs_compressed) { - rhs_size.width /= rhs_block.width; - rhs_size.height /= rhs_block.height; + rhs_size.width = Common::DivCeil(rhs_size.width, rhs_block.width); + rhs_size.height = Common::DivCeil(rhs_size.height, rhs_block.height); } const Extent3D copy_size{ .width = std::min(lhs_size.width, rhs_size.width), From ada09778d97d39d83353ca54d0d6c9abd5eefc60 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 15 Apr 2022 12:29:49 +0200 Subject: [PATCH 091/245] Vulkan Texture Cache: Limit render area to the max width/height of the targets. --- .../renderer_vulkan/vk_texture_cache.cpp | 30 ++++++++++++++----- .../renderer_vulkan/vk_texture_cache.h | 5 ++-- src/video_core/texture_cache/render_targets.h | 1 + src/video_core/texture_cache/texture_cache.h | 2 ++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8d2728cd78..305ad8aeee 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1474,13 +1474,14 @@ bool Image::BlitScaleHelper(bool scale_up) { }; const VkExtent2D extent{ .width = std::max(scaled_width, info.size.width), - .height = std::max(scaled_height, info.size.width), + .height = std::max(scaled_height, info.size.height), }; auto* view_ptr = blit_view.get(); if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) { if (!blit_framebuffer) { - blit_framebuffer = std::make_unique(*runtime, view_ptr, nullptr, extent); + blit_framebuffer = + std::make_unique(*runtime, view_ptr, nullptr, extent, scale_up); } const auto color_view = blit_view->Handle(Shader::TextureType::Color2D); @@ -1488,7 +1489,8 @@ bool Image::BlitScaleHelper(bool scale_up) { src_region, operation, BLIT_OPERATION); } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (!blit_framebuffer) { - blit_framebuffer = std::make_unique(*runtime, nullptr, view_ptr, extent); + blit_framebuffer = + std::make_unique(*runtime, nullptr, view_ptr, extent, scale_up); } runtime->blit_image_helper.BlitDepthStencil(blit_framebuffer.get(), blit_view->DepthView(), blit_view->StencilView(), dst_region, @@ -1756,34 +1758,42 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers{color_buffer}; - CreateFramebuffer(runtime, color_buffers, depth_buffer); + CreateFramebuffer(runtime, color_buffers, depth_buffer, is_rescaled); } Framebuffer::~Framebuffer() = default; void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, std::span color_buffers, - ImageView* depth_buffer) { + ImageView* depth_buffer, bool is_rescaled) { std::vector attachments; RenderPassKey renderpass_key{}; s32 num_layers = 1; + const auto& resolution = runtime.resolution; + + u32 width = 0; + u32 height = 0; for (size_t index = 0; index < NUM_RT; ++index) { const ImageView* const color_buffer = color_buffers[index]; if (!color_buffer) { renderpass_key.color_formats[index] = PixelFormat::Invalid; continue; } + width = std::max(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) + : color_buffer->size.width); + height = std::max(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) + : color_buffer->size.height); attachments.push_back(color_buffer->RenderTarget()); renderpass_key.color_formats[index] = color_buffer->format; num_layers = std::max(num_layers, color_buffer->range.extent.layers); @@ -1794,6 +1804,10 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } const size_t num_colors = attachments.size(); if (depth_buffer) { + width = std::max(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) + : depth_buffer->size.width); + height = std::max(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) + : depth_buffer->size.height); attachments.push_back(depth_buffer->RenderTarget()); renderpass_key.depth_format = depth_buffer->format; num_layers = std::max(num_layers, depth_buffer->range.extent.layers); @@ -1810,6 +1824,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, renderpass_key.samples = samples; renderpass = runtime.render_pass_cache.Get(renderpass_key); + render_area.width = std::min(render_area.width, width); + render_area.height = std::min(render_area.height, height); num_color_buffers = static_cast(num_colors); framebuffer = runtime.device.GetLogical().CreateFramebuffer({ diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 69f06ee7b7..0b7ac0df16 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -268,7 +268,7 @@ public: ImageView* depth_buffer, const VideoCommon::RenderTargets& key); explicit Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer, - ImageView* depth_buffer, VkExtent2D extent); + ImageView* depth_buffer, VkExtent2D extent, bool is_rescaled); ~Framebuffer(); @@ -279,7 +279,8 @@ public: Framebuffer& operator=(Framebuffer&&) = default; void CreateFramebuffer(TextureCacheRuntime& runtime, - std::span color_buffers, ImageView* depth_buffer); + std::span color_buffers, ImageView* depth_buffer, + bool is_rescaled = false); [[nodiscard]] VkFramebuffer Handle() const noexcept { return *framebuffer; diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h index da8ffe9ece..1efbd65079 100644 --- a/src/video_core/texture_cache/render_targets.h +++ b/src/video_core/texture_cache/render_targets.h @@ -26,6 +26,7 @@ struct RenderTargets { ImageViewId depth_buffer_id{}; std::array draw_buffers{}; Extent2D size{}; + bool is_rescaled{}; }; } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 66de41f047..9a835cefce 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -357,6 +357,7 @@ void TextureCache

::UpdateRenderTargets(bool is_clear) { (maxwell3d->regs.render_area.width * up_scale) >> down_shift, (maxwell3d->regs.render_area.height * up_scale) >> down_shift, }; + render_targets.is_rescaled = is_rescaling; flags[Dirty::DepthBiasGlobal] = true; } @@ -1962,6 +1963,7 @@ std::pair TextureCache

::RenderTargetFromImage( .color_buffer_ids = {color_view_id}, .depth_buffer_id = depth_view_id, .size = {extent.width >> samples_x, extent.height >> samples_y}, + .is_rescaled = is_rescaled, }); return {framebuffer_id, view_id}; } From 8d774e7415fac1153d8944baa2cc250cc4831107 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 18 Apr 2022 21:07:21 +0200 Subject: [PATCH 092/245] NvDec: Fix regressions. --- .../hle/service/nvdrv/core/syncpoint_manager.cpp | 6 ++++++ .../hle/service/nvdrv/core/syncpoint_manager.h | 5 +++++ .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 1 + .../nvdrv/devices/nvhost_nvdec_common.cpp | 16 ++++++++++++---- .../service/nvdrv/devices/nvhost_nvdec_common.h | 3 +++ src/core/hle/service/nvdrv/nvdrv.cpp | 5 ++++- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index b34481b486..fc4ff3c2fa 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -55,6 +55,12 @@ u32 SyncpointManager::AllocateSyncpoint(bool clientManaged) { return ReserveSyncpoint(FindFreeSyncpoint(), clientManaged); } +void SyncpointManager::FreeSyncpoint(u32 id) { + std::lock_guard lock(reservation_lock); + ASSERT(syncpoints.at(id).reserved); + syncpoints.at(id).reserved = false; +} + bool SyncpointManager::IsSyncpointAllocated(u32 id) { return (id <= SyncpointCount) && syncpoints[id].reserved; } diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h index bfc8ba84b0..da456f2062 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -84,6 +84,11 @@ public: */ u32 UpdateMin(u32 id); + /** + * @brief Frees the usage of a syncpoint. + */ + void FreeSyncpoint(u32 id); + /** * @return A fence that will be signalled once this syncpoint hits its maximum value */ diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index c2cc09993f..908e60191d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -43,6 +43,7 @@ nvhost_gpu::~nvhost_gpu() { events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); events_interface.FreeEvent(error_notifier_event); + syncpoint_manager.FreeSyncpoint(channel_syncpoint); } NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 008092dbb3..fe83423d53 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -51,8 +51,12 @@ std::unordered_map nvhost_nvdec_common::fd_to_id{}; nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, NvCore::ChannelType channel_type_) : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, - nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {} -nvhost_nvdec_common::~nvhost_nvdec_common() = default; + nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); +} +nvhost_nvdec_common::~nvhost_nvdec_common() { + syncpoint_manager.FreeSyncpoint(channel_syncpoint); +} NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { IoctlSetNvmapFD params{}; @@ -117,8 +121,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector& input, std::ve std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); - const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast(channel_type)]}; - params.value = id; + // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast(channel_type)]}; + params.value = channel_syncpoint; std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); return NvResult::Success; @@ -176,4 +180,8 @@ Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { return nullptr; } +void nvhost_nvdec_common::Reset() { + fd_to_id.clear(); +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 51bb7c2cb6..4046b0e13f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -24,6 +24,8 @@ public: NvCore::ChannelType channel_type); ~nvhost_nvdec_common() override; + static void Reset(); + protected: struct IoctlSetNvmapFD { s32_le nvmap_fd{}; @@ -117,6 +119,7 @@ protected: Kernel::KEvent* QueryEvent(u32 event_id) override; static std::unordered_map fd_to_id; + u32 channel_syncpoint; s32_le nvmap_fd{}; u32_le submit_timeout{}; NvCore::Container& core; diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 1be51e4018..20bf24ec8c 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -18,6 +18,7 @@ #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" +#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvmap.h" @@ -101,7 +102,9 @@ Module::Module(Core::System& system) }; } -Module::~Module() = default; +Module::~Module() { + Devices::nvhost_nvdec_common::Reset(); +} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { From cdce7f781bafabc364e61fa5cabf938349c9b82e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 18 Apr 2022 23:21:02 +0200 Subject: [PATCH 093/245] Vulkan Swapchain: Overall improvements. --- .../renderer_vulkan/renderer_vulkan.cpp | 2 +- src/video_core/renderer_vulkan/vk_swapchain.cpp | 15 ++++++++++++--- src/yuzu/main.cpp | 6 ++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index d12669c9d4..51d9e8f68c 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -147,7 +147,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { const auto recreate_swapchain = [&] { if (!has_been_recreated) { has_been_recreated = true; - scheduler.WaitWorker(); + scheduler.Finish(); } const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); swapchain.Create(layout.width, layout.height, is_srgb); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index a69ae7725e..706d9ba746 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -36,7 +36,8 @@ VkPresentModeKHR ChooseSwapPresentMode(vk::Span modes) { // Mailbox (triple buffering) doesn't lock the application like fifo (vsync), // prefer it if vsync option is not selected const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); - if (found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { + if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Borderless && + found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { return VK_PRESENT_MODE_MAILBOX_KHR; } if (!Settings::values.use_speed_limit.GetValue()) { @@ -156,8 +157,16 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u3 present_mode = ChooseSwapPresentMode(present_modes); u32 requested_image_count{capabilities.minImageCount + 1}; - if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { - requested_image_count = capabilities.maxImageCount; + // Ensure Tripple buffering if possible. + if (capabilities.maxImageCount > 0) { + if (requested_image_count > capabilities.maxImageCount) { + requested_image_count = capabilities.maxImageCount; + } else { + requested_image_count = + std::max(requested_image_count, std::min(3U, capabilities.maxImageCount)); + } + } else { + requested_image_count = std::max(requested_image_count, 3U); } VkSwapchainCreateInfoKHR swapchain_ci{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c63ce3a308..4146ebc2c1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #ifdef __APPLE__ @@ -3451,9 +3452,10 @@ void GMainWindow::UpdateStatusBar() { } if (!Settings::values.use_speed_limit) { game_fps_label->setText( - tr("Game: %1 FPS (Unlocked)").arg(results.average_game_fps, 0, 'f', 0)); + tr("Game: %1 FPS (Unlocked)").arg(std::round(results.average_game_fps), 0, 'f', 0)); } else { - game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); + game_fps_label->setText( + tr("Game: %1 FPS").arg(std::round(results.average_game_fps), 0, 'f', 0)); } emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); From 1a9b71b1c6a5b6fb2a41fc485a986e9c505b2856 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 16 Jun 2022 02:00:29 +0200 Subject: [PATCH 094/245] Common: Fix variable shadowing. --- src/common/address_space.inc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/address_space.inc b/src/common/address_space.inc index 907c55d88e..e1241d0992 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc @@ -30,9 +30,9 @@ FlatAllocator namespace Common { -MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType vaLimit, - std::function unmapCallback) - : unmapCallback(std::move(unmapCallback)), vaLimit(vaLimit) { +MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType vaLimit_, + std::function unmapCallback_) + : unmapCallback(std::move(unmapCallback_)), vaLimit(vaLimit_) { if (vaLimit > VaMaximum) UNREACHABLE_MSG("Invalid VA limit!"); } @@ -261,8 +261,8 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { unmapCallback(virt, size); } -ALLOC_MEMBER_CONST()::FlatAllocator(VaType vaStart, VaType vaLimit) - : Base(vaLimit), currentLinearAllocEnd(vaStart), vaStart(vaStart) {} +ALLOC_MEMBER_CONST()::FlatAllocator(VaType vaStart_, VaType vaLimit) + : Base(vaLimit), currentLinearAllocEnd(vaStart_), vaStart(vaStart_) {} ALLOC_MEMBER(VaType)::Allocate(VaType size) { std::scoped_lock lock(this->blockMutex); From fe24c65153349d3a759a2eef02ec703851a96847 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 16 Jun 2022 02:12:21 +0200 Subject: [PATCH 095/245] General: Fix clang format. --- src/common/address_space.inc | 4 ++-- src/video_core/control/channel_state_cache.h | 1 + src/video_core/host1x/codecs/vp9.cpp | 5 +++-- src/video_core/host1x/syncpoint_manager.h | 4 ++-- src/video_core/host1x/vic.cpp | 4 ++-- src/video_core/memory_manager.cpp | 1 - src/video_core/renderer_vulkan/renderer_vulkan.cpp | 13 ++++--------- 7 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/common/address_space.inc b/src/common/address_space.inc index e1241d0992..7cfbb150b0 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc @@ -261,8 +261,8 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { unmapCallback(virt, size); } -ALLOC_MEMBER_CONST()::FlatAllocator(VaType vaStart_, VaType vaLimit) - : Base(vaLimit), currentLinearAllocEnd(vaStart_), vaStart(vaStart_) {} +ALLOC_MEMBER_CONST()::FlatAllocator(VaType vaStart_, VaType vaLimit_) + : Base(vaLimit_), currentLinearAllocEnd(vaStart_), vaStart(vaStart_) {} ALLOC_MEMBER(VaType)::Allocate(VaType size) { std::scoped_lock lock(this->blockMutex); diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index dbf833de74..102947adb2 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/common_types.h" diff --git a/src/video_core/host1x/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp index 667aadc6a9..cf40c90121 100644 --- a/src/video_core/host1x/codecs/vp9.cpp +++ b/src/video_core/host1x/codecs/vp9.cpp @@ -382,8 +382,9 @@ Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters // gpu.SyncGuestHost(); epic, why? current_frame.info = GetVp9PictureInfo(state); current_frame.bit_stream.resize(current_frame.info.bitstream_size); - host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), - current_frame.info.bitstream_size); + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, + current_frame.bit_stream.data(), + current_frame.info.bitstream_size); } if (!next_frame.bit_stream.empty()) { Vp9FrameContainer temp{ diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h index 0ecc040ab8..440b1508a3 100644 --- a/src/video_core/host1x/syncpoint_manager.h +++ b/src/video_core/host1x/syncpoint_manager.h @@ -49,9 +49,9 @@ public: expected_value, func); } - void DeregisterGuestAction(u32 syncpoint_id,ActionHandle& handle); + void DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle); - void DeregisterHostAction(u32 syncpoint_id,ActionHandle& handle); + void DeregisterHostAction(u32 syncpoint_id, ActionHandle& handle); void IncrementGuest(u32 syncpoint_id); diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index b9ac415295..ac0b7d20e0 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -157,8 +157,8 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); luma_buffer.resize(size); std::span frame_buff(converted_frame_buf_addr, 4 * width * height); - Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, - 0, 0, width, height, block_height, 0, width * 4); + Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height, + block_height, 0, width * 4); host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); } else { diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 3cb2d9224b..cca401c74b 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -619,7 +619,6 @@ bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const } bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const { - std::optional old_page_addr{}; bool result{true}; auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, [[maybe_unused]] std::size_t copy_amount) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 51d9e8f68c..d8131232ac 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -95,19 +95,14 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) try - : RendererBase(emu_window, std::move(context_)), - telemetry_session(telemetry_session_), - cpu_memory(cpu_memory_), - gpu(gpu_), - library(OpenLibrary()), + : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), + cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug.GetValue())), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), - device(CreateDevice(instance, dld, *surface)), - memory_allocator(device, false), - state_tracker(), - scheduler(device, state_tracker), + device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), + state_tracker(), scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, From 9982cff98b4db38565715cc515ea496b6195725b Mon Sep 17 00:00:00 2001 From: VonChenPlus Date: Mon, 27 Jun 2022 12:39:57 +0800 Subject: [PATCH 096/245] Core: Fix get nvmap object random crash --- src/core/hle/service/nvdrv/core/nvmap.cpp | 15 ++++++++++++++- src/core/hle/service/nvdrv/core/nvmap.h | 5 +++++ src/core/hle/service/nvdrv/devices/nvmap.cpp | 12 ------------ src/core/hle/service/nvdrv/devices/nvmap.h | 5 ----- src/core/hle/service/nvdrv/nvdrv.h | 3 +-- .../service/nvflinger/buffer_queue_consumer.cpp | 9 +++++++-- .../service/nvflinger/buffer_queue_consumer.h | 8 +++++++- .../service/nvflinger/buffer_queue_producer.cpp | 10 +++++++--- .../service/nvflinger/buffer_queue_producer.h | 9 ++++++++- src/core/hle/service/nvflinger/nvflinger.cpp | 2 +- src/core/hle/service/vi/display/vi_display.cpp | 16 ++++++++++------ src/core/hle/service/vi/display/vi_display.h | 7 ++++++- 12 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index b02dbb9c98..dd30e156eb 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -207,6 +207,19 @@ void NvMap::UnpinHandle(Handle::Id handle) { } } +void NvMap::DuplicateHandle(Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); + return; + } + + auto result = handle_description->Duplicate(false); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + } +} + std::optional NvMap::FreeHandle(Handle::Id handle, bool internal_session) { std::weak_ptr hWeak{GetHandle(handle)}; FreeInfo freeInfo; @@ -254,7 +267,7 @@ std::optional NvMap::FreeHandle(Handle::Id handle, bool interna // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed if (!hWeak.expired()) { - LOG_ERROR(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); + LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); freeInfo.address = 0; } diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index 1082bb58df..b6613a521e 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -162,6 +162,11 @@ public: */ void UnpinHandle(Handle::Id handle); + /** + * @brief Tries to duplicate a handle + */ + void DuplicateHandle(Handle::Id handle); + /** * @brief Tries to free a handle and remove a single dupe * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index f84fc8c379..ddf273b5ec 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -69,18 +69,6 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& input, void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {} -VAddr nvmap::GetObjectAddress(u32 handle) const { - auto obj = file.GetHandle(handle); - if (obj) { - return obj->address; - } - return 0; -} - -std::shared_ptr nvmap::GetObject(u32 handle) const { - return file.GetHandle(handle); -} - NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index c22eb57a40..52e1d7cff9 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -36,11 +36,6 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - /// Returns the allocated address of an nvmap object given its handle. - VAddr GetObjectAddress(u32 handle) const; - - std::shared_ptr GetObject(u32 handle) const; - enum class HandleParameterType : u32_le { Size = 1, Alignment = 2, diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index b262547539..22836529d0 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -96,6 +96,7 @@ public: private: friend class EventInterface; + friend class Service::NVFlinger::NVFlinger; /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; @@ -111,8 +112,6 @@ private: /// Manages syncpoints on the host NvCore::Container container; - void CreateEvent(u32 event_id); - void FreeEvent(u32 event_id); std::unordered_map> builders; }; diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index 4b3d5efd6a..a0330ab4aa 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -5,15 +5,18 @@ // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp #include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvflinger/buffer_item.h" #include "core/hle/service/nvflinger/buffer_queue_consumer.h" #include "core/hle/service/nvflinger/buffer_queue_core.h" #include "core/hle/service/nvflinger/producer_listener.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" namespace Service::android { -BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr core_) - : core{std::move(core_)}, slots{core->slots} {} +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} BufferQueueConsumer::~BufferQueueConsumer() = default; @@ -133,6 +136,8 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc slots[slot].buffer_state = BufferState::Free; + nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), false); + listener = core->connected_producer_listener; LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h index b598c314ff..4ec06ca131 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h @@ -13,6 +13,10 @@ #include "core/hle/service/nvflinger/buffer_queue_defs.h" #include "core/hle/service/nvflinger/status.h" +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::android { class BufferItem; @@ -21,7 +25,8 @@ class IConsumerListener; class BufferQueueConsumer final { public: - explicit BufferQueueConsumer(std::shared_ptr core_); + explicit BufferQueueConsumer(std::shared_ptr core_, + Service::Nvidia::NvCore::NvMap& nvmap_); ~BufferQueueConsumer(); Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); @@ -32,6 +37,7 @@ public: private: std::shared_ptr core; BufferQueueDefs::SlotsType& slots; + Service::Nvidia::NvCore::NvMap& nvmap; }; } // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 337431488e..a4e46964ce 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -14,7 +14,7 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvflinger/buffer_queue_core.h" #include "core/hle/service/nvflinger/buffer_queue_producer.h" #include "core/hle/service/nvflinger/consumer_listener.h" @@ -26,8 +26,10 @@ namespace Service::android { BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr buffer_queue_core_) - : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) { + std::shared_ptr buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), + nvmap(nvmap_) { buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); } @@ -530,6 +532,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; + nvmap.DuplicateHandle(item.graphic_buffer->BufferId()); + sticky_transform = sticky_transform_; if (core->queue.empty()) { diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 42d4722dc3..0ba03a568e 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -31,6 +31,10 @@ namespace Service::KernelHelpers { class ServiceContext; } // namespace Service::KernelHelpers +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::android { class BufferQueueCore; @@ -39,7 +43,8 @@ class IProducerListener; class BufferQueueProducer final : public IBinder { public: explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr buffer_queue_core_); + std::shared_ptr buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_); ~BufferQueueProducer(); void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; @@ -78,6 +83,8 @@ private: s32 next_callback_ticket{}; s32 current_callback_ticket{}; std::condition_variable_any callback_condition; + + Service::Nvidia::NvCore::NvMap& nvmap; }; } // namespace Service::android diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 4658f1e8b8..aa14d2cbcf 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -149,7 +149,7 @@ std::optional NVFlinger::CreateLayer(u64 display_id) { void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { const auto buffer_id = next_buffer_queue_id++; - display.CreateLayer(layer_id, buffer_id); + display.CreateLayer(layer_id, buffer_id, nvdrv->container); } void NVFlinger::CloseLayer(u64 layer_id) { diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index aa49aa7752..288aafaaf6 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -12,6 +12,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvflinger/buffer_item_consumer.h" #include "core/hle/service/nvflinger/buffer_queue_consumer.h" #include "core/hle/service/nvflinger/buffer_queue_core.h" @@ -29,11 +30,13 @@ struct BufferQueue { std::unique_ptr consumer; }; -static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { +static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context, + Service::Nvidia::NvCore::NvMap& nvmap) { auto buffer_queue_core = std::make_shared(); - return {buffer_queue_core, - std::make_unique(service_context, buffer_queue_core), - std::make_unique(buffer_queue_core)}; + return { + buffer_queue_core, + std::make_unique(service_context, buffer_queue_core, nvmap), + std::make_unique(buffer_queue_core, nvmap)}; } Display::Display(u64 id, std::string name_, @@ -74,10 +77,11 @@ void Display::SignalVSyncEvent() { vsync_event->GetWritableEvent().Signal(); } -void Display::CreateLayer(u64 layer_id, u32 binder_id) { +void Display::CreateLayer(u64 layer_id, u32 binder_id, + Service::Nvidia::NvCore::Container& nv_core) { ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); - auto [core, producer, consumer] = CreateBufferQueue(service_context); + auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); auto buffer_item_consumer = std::make_shared(std::move(consumer)); buffer_item_consumer->Connect(false); diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 8dbb0ef80d..33d5f398cd 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -27,6 +27,11 @@ namespace Service::NVFlinger { class HosBinderDriverServer; } +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::VI { class Layer; @@ -93,7 +98,7 @@ public: /// @param layer_id The ID to assign to the created layer. /// @param binder_id The ID assigned to the buffer queue. /// - void CreateLayer(u64 layer_id, u32 binder_id); + void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); /// Closes and removes a layer from this display with the given ID. /// From 8a372035db35465b8241d1e13eeb979e8682bb3f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 27 Jun 2022 21:45:33 +0200 Subject: [PATCH 097/245] Nvflinger: correct duplication. --- src/core/hle/service/nvdrv/core/nvmap.cpp | 4 ++-- src/core/hle/service/nvdrv/core/nvmap.h | 2 +- src/core/hle/service/nvflinger/buffer_queue_consumer.cpp | 2 +- src/core/hle/service/nvflinger/buffer_queue_producer.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index dd30e156eb..f811b66a0d 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -207,14 +207,14 @@ void NvMap::UnpinHandle(Handle::Id handle) { } } -void NvMap::DuplicateHandle(Handle::Id handle) { +void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) { auto handle_description{GetHandle(handle)}; if (!handle_description) { LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); return; } - auto result = handle_description->Duplicate(false); + auto result = handle_description->Duplicate(internal_session); if (result != NvResult::Success) { LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); } diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index b6613a521e..ef2df3ad75 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -165,7 +165,7 @@ public: /** * @brief Tries to duplicate a handle */ - void DuplicateHandle(Handle::Id handle); + void DuplicateHandle(Handle::Id handle, bool internal_session = false); /** * @brief Tries to free a handle and remove a single dupe diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index a0330ab4aa..1ce67c771c 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -136,7 +136,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc slots[slot].buffer_state = BufferState::Free; - nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), false); + nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); listener = core->connected_producer_listener; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index a4e46964ce..d4ab23a108 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -532,7 +532,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; - nvmap.DuplicateHandle(item.graphic_buffer->BufferId()); + nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); sticky_transform = sticky_transform_; From c2b7de66b325b52cebb7e26948db0d5b0eefee25 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 27 Jun 2022 21:23:06 +0200 Subject: [PATCH 098/245] Address Feedback from bylaws. --- src/core/hle/service/nvdrv/core/nvmap.cpp | 6 +----- src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | 2 +- src/video_core/renderer_vulkan/vk_query_cache.cpp | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index f811b66a0d..9b21da6b1e 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -41,22 +41,18 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) size = Common::AlignUp(size, YUZU_PAGESIZE); aligned_size = Common::AlignUp(size, align); address = pAddress; - - // TODO: pin init - allocated = true; return NvResult::Success; } NvResult NvMap::Handle::Duplicate(bool internal_session) { + std::scoped_lock lock(mutex); // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) if (!allocated) [[unlikely]] { return NvResult::BadValue; } - std::scoped_lock lock(mutex); - // If we internally use FromId the duplication tracking of handles won't work accurately due to // us not implementing per-process handle refs. if (internal_session) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 908e60191d..32e45540d6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -270,12 +270,12 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector } } - gpu.PushGPUEntries(bind_id, std::move(entries)); params.fence.id = channel_syncpoint; u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); + gpu.PushGPUEntries(bind_id, std::move(entries)); if (flags.fence_increment.Value()) { if (flags.suppress_wfi.Value()) { diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 393bbdf378..7cb02631c4 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -66,7 +66,7 @@ void QueryPool::Reserve(std::pair query) { } QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, - Scheduler& scheduler_) + Scheduler& scheduler_) : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_}, query_pools{ QueryPool{device_, scheduler_, QueryType::SamplesPassed}, From d97d409647686aefe701aec1363e328be11d1443 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 1 Jul 2022 02:18:33 +0200 Subject: [PATCH 099/245] NvHostChannels: improve hack for supporting multiple channels. --- .../hle/service/nvdrv/devices/nvhost_nvdec_common.cpp | 10 ++++++++-- .../hle/service/nvdrv/devices/nvhost_nvdec_common.h | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index fe83423d53..2ec1ad3e9e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -47,15 +47,21 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } // Anonymous namespace std::unordered_map nvhost_nvdec_common::fd_to_id{}; +std::deque nvhost_nvdec_common::syncpts_accumulated{}; nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, NvCore::ChannelType channel_type_) : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { - channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + if (syncpts_accumulated.empty()) { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + } else { + channel_syncpoint = syncpts_accumulated.front(); + syncpts_accumulated.pop_front(); + } } nvhost_nvdec_common::~nvhost_nvdec_common() { - syncpoint_manager.FreeSyncpoint(channel_syncpoint); + syncpts_accumulated.push_back(channel_syncpoint); } NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 4046b0e13f..93990bb9b8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "common/common_types.h" #include "common/swap.h" @@ -127,6 +128,8 @@ protected: NvCore::NvMap& nvmap; NvCore::ChannelType channel_type; std::array device_syncpoints{}; + + static std::deque syncpts_accumulated; }; }; // namespace Devices } // namespace Service::Nvidia From fedd983f96bcbcc0c39f651db1cca0503d582fd9 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 29 Jun 2022 19:27:49 -0400 Subject: [PATCH 100/245] general: Format licenses as per SPDX guidelines --- src/common/address_space.cpp | 5 ++--- src/common/address_space.h | 5 ++--- src/common/address_space.inc | 4 ++-- src/common/multi_level_page_table.cpp | 3 +++ src/common/multi_level_page_table.h | 5 ++--- src/common/multi_level_page_table.inc | 5 ++--- src/core/hle/service/nvdrv/core/container.cpp | 7 +++---- src/core/hle/service/nvdrv/core/container.h | 7 +++---- src/core/hle/service/nvdrv/core/nvmap.cpp | 7 +++---- src/core/hle/service/nvdrv/core/nvmap.h | 7 +++---- src/core/hle/service/nvdrv/core/syncpoint_manager.cpp | 7 +++---- src/core/hle/service/nvdrv/core/syncpoint_manager.h | 7 +++---- src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | 7 +++---- src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | 7 +++---- src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 7 +++---- src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | 7 +++---- src/core/hle/service/nvdrv/nvdata.h | 7 +++---- src/core/hle/service/nvdrv/nvdrv.cpp | 7 +++---- src/core/hle/service/nvdrv/nvdrv.h | 7 +++---- src/core/hle/service/nvdrv/nvdrv_interface.cpp | 7 +++---- src/video_core/control/channel_state.cpp | 5 ++--- src/video_core/control/channel_state.h | 5 ++--- src/video_core/control/channel_state_cache.cpp | 3 +++ src/video_core/control/channel_state_cache.h | 5 ++--- src/video_core/control/channel_state_cache.inc | 2 ++ src/video_core/control/scheduler.cpp | 5 ++--- src/video_core/control/scheduler.h | 5 ++--- src/video_core/engines/puller.cpp | 5 ++--- src/video_core/engines/puller.h | 5 ++--- src/video_core/host1x/control.cpp | 5 ++--- src/video_core/host1x/control.h | 7 +++---- src/video_core/host1x/host1x.cpp | 5 ++--- src/video_core/host1x/host1x.h | 5 ++--- src/video_core/host1x/syncpoint_manager.cpp | 5 ++--- src/video_core/host1x/syncpoint_manager.h | 5 ++--- src/video_core/texture_cache/texture_cache.cpp | 5 ++--- src/video_core/texture_cache/texture_cache.h | 6 ++---- src/video_core/texture_cache/texture_cache_base.h | 6 ++---- 38 files changed, 93 insertions(+), 121 deletions(-) diff --git a/src/common/address_space.cpp b/src/common/address_space.cpp index 6db85be87b..866e78dbe0 100644 --- a/src/common/address_space.cpp +++ b/src/common/address_space.cpp @@ -1,6 +1,5 @@ -// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/address_space.inc" diff --git a/src/common/address_space.h b/src/common/address_space.h index 8e13935aff..5b3832f079 100644 --- a/src/common/address_space.h +++ b/src/common/address_space.h @@ -1,6 +1,5 @@ -// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/common/address_space.inc b/src/common/address_space.inc index 7cfbb150b0..a063782b3f 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GPLv3 or later -// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/address_space.h" #include "common/assert.h" diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp index 3a7a75aa71..46e362f3ba 100644 --- a/src/common/multi_level_page_table.cpp +++ b/src/common/multi_level_page_table.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include "common/multi_level_page_table.inc" namespace Common { diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h index dde1cc962e..08092c89a6 100644 --- a/src/common/multi_level_page_table.h +++ b/src/common/multi_level_page_table.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc index 4def6dba85..8ac506fa0a 100644 --- a/src/common/multi_level_page_table.inc +++ b/src/common/multi_level_page_table.inc @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #ifdef _WIN32 #include diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index 4175d3d9c3..d2a6326466 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index e069ade4e5..5c8b958034 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 9b21da6b1e..e63ec77174 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/alignment.h" #include "common/assert.h" diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index ef2df3ad75..6d6dac023a 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index fc4ff3c2fa..0bb2aec974 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/assert.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h index da456f2062..6b71cd33df 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2022 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index d1beefba6a..b48f7fcafd 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include #include diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 12e881f0db..86fe71c750 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 7fffb8e48c..5bee4a3d3f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include #include diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index f511c02961..4aa738b41a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team, Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 2ee91f9c43..0e2f47075f 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 20bf24ec8c..7929443d27 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 22836529d0..a2aeb80b49 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 5e50a04e8a..edbdfee43c 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include #include "common/logging/log.h" diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp index 3613c49927..b04922ac0e 100644 --- a/src/video_core/control/channel_state.cpp +++ b/src/video_core/control/channel_state.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/assert.h" #include "video_core/control/channel_state.h" diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h index 08a7591e17..305b21cbaa 100644 --- a/src/video_core/control/channel_state.h +++ b/src/video_core/control/channel_state.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/control/channel_state_cache.cpp b/src/video_core/control/channel_state_cache.cpp index ec7ba907ca..4ebeb6356b 100644 --- a/src/video_core/control/channel_state_cache.cpp +++ b/src/video_core/control/channel_state_cache.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + #include "video_core/control/channel_state_cache.inc" namespace VideoCommon { diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 102947adb2..5246192a80 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc index d3ae758b2c..460313893f 100644 --- a/src/video_core/control/channel_state_cache.inc +++ b/src/video_core/control/channel_state_cache.inc @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp index a9bb00aa77..7330426909 100644 --- a/src/video_core/control/scheduler.cpp +++ b/src/video_core/control/scheduler.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h index c1a7739467..305a01e0a0 100644 --- a/src/video_core/control/scheduler.h +++ b/src/video_core/control/scheduler.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index c3ed11c13e..cca890792a 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h index b4619e9a88..d4175ee945 100644 --- a/src/video_core/engines/puller.h +++ b/src/video_core/engines/puller.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/host1x/control.cpp b/src/video_core/host1x/control.cpp index a81c635aec..dceefdb7f4 100644 --- a/src/video_core/host1x/control.cpp +++ b/src/video_core/host1x/control.cpp @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/assert.h" #include "video_core/host1x/control.h" diff --git a/src/video_core/host1x/control.h b/src/video_core/host1x/control.h index 18a9b56c0e..e117888a3b 100644 --- a/src/video_core/host1x/control.h +++ b/src/video_core/host1x/control.h @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp index eb00f48557..7c317a85d4 100644 --- a/src/video_core/host1x/host1x.cpp +++ b/src/video_core/host1x/host1x.cpp @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include "core/core.h" #include "video_core/host1x/host1x.h" diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h index e4b69d75aa..7ecf853d95 100644 --- a/src/video_core/host1x/host1x.h +++ b/src/video_core/host1x/host1x.h @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp index 825bd551e7..4471bacae2 100644 --- a/src/video_core/host1x/syncpoint_manager.cpp +++ b/src/video_core/host1x/syncpoint_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include "common/microprofile.h" #include "video_core/host1x/syncpoint_manager.h" diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h index 440b1508a3..72220a09a5 100644 --- a/src/video_core/host1x/syncpoint_manager.h +++ b/src/video_core/host1x/syncpoint_manager.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index bc905a1a4f..8a9a32f44a 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv3 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include "video_core/control/channel_state_cache.inc" #include "video_core/texture_cache/texture_cache_base.h" diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 9a835cefce..eaf4a1c959 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1,7 +1,5 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 2f4db50478..2fa8445eb4 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -1,7 +1,5 @@ -// SPDX-FileCopyrightText: 2021 yuzu emulator team -// (https://github.com/skyline-emu/) -// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 -// or any later version Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once From fa342cae227666c861806b9bf63e4286aff1e4d7 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 29 Jun 2022 20:33:04 -0400 Subject: [PATCH 101/245] address_space: Address feedback --- src/common/address_space.h | 105 +++--- src/common/address_space.inc | 337 ++++++++++-------- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 8 +- 3 files changed, 246 insertions(+), 204 deletions(-) diff --git a/src/common/address_space.h b/src/common/address_space.h index 5b3832f079..bf649018c5 100644 --- a/src/common/address_space.h +++ b/src/common/address_space.h @@ -23,9 +23,29 @@ template requires AddressSpaceValid class FlatAddressSpaceMap { -private: - std::function - unmapCallback{}; //!< Callback called when the mappings in an region have changed +public: + /// The maximum VA that this AS can technically reach + static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) + + ((1ULL << (AddressSpaceBits - 1)) - 1)}; + + explicit FlatAddressSpaceMap(VaType va_limit, + std::function unmap_callback = {}); + + FlatAddressSpaceMap() = default; + + void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info = {}) { + std::scoped_lock lock(block_mutex); + MapLocked(virt, phys, size, extra_info); + } + + void Unmap(VaType virt, VaType size) { + std::scoped_lock lock(block_mutex); + UnmapLocked(virt, size); + } + + VaType GetVALimit() const { + return va_limit; + } protected: /** @@ -33,68 +53,55 @@ protected: * another block with a different phys address is hit */ struct Block { - VaType virt{UnmappedVa}; //!< VA of the block - PaType phys{UnmappedPa}; //!< PA of the block, will increase 1-1 with VA until a new block - //!< is encountered - [[no_unique_address]] ExtraBlockInfo extraInfo; + /// VA of the block + VaType virt{UnmappedVa}; + /// PA of the block, will increase 1-1 with VA until a new block is encountered + PaType phys{UnmappedPa}; + [[no_unique_address]] ExtraBlockInfo extra_info; Block() = default; - Block(VaType virt_, PaType phys_, ExtraBlockInfo extraInfo_) - : virt(virt_), phys(phys_), extraInfo(extraInfo_) {} + Block(VaType virt_, PaType phys_, ExtraBlockInfo extra_info_) + : virt(virt_), phys(phys_), extra_info(extra_info_) {} - constexpr bool Valid() { + bool Valid() const { return virt != UnmappedVa; } - constexpr bool Mapped() { + bool Mapped() const { return phys != UnmappedPa; } - constexpr bool Unmapped() { + bool Unmapped() const { return phys == UnmappedPa; } - bool operator<(const VaType& pVirt) const { - return virt < pVirt; + bool operator<(const VaType& p_virt) const { + return virt < p_virt; } }; - std::mutex blockMutex; - std::vector blocks{Block{}}; - /** * @brief Maps a PA range into the given AS region - * @note blockMutex MUST be locked when calling this + * @note block_mutex MUST be locked when calling this */ - void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo); + void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info); /** * @brief Unmaps the given range and merges it with other unmapped regions - * @note blockMutex MUST be locked when calling this + * @note block_mutex MUST be locked when calling this */ void UnmapLocked(VaType virt, VaType size); -public: - static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) + - ((1ULL << (AddressSpaceBits - 1)) - - 1)}; //!< The maximum VA that this AS can technically reach + std::mutex block_mutex; + std::vector blocks{Block{}}; - VaType vaLimit{VaMaximum}; //!< A soft limit on the maximum VA of the AS + /// a soft limit on the maximum VA of the AS + VaType va_limit{VaMaximum}; - FlatAddressSpaceMap(VaType vaLimit, std::function unmapCallback = {}); - - FlatAddressSpaceMap() = default; - - void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo = {}) { - std::scoped_lock lock(blockMutex); - MapLocked(virt, phys, size, extraInfo); - } - - void Unmap(VaType virt, VaType size) { - std::scoped_lock lock(blockMutex); - UnmapLocked(virt, size); - } +private: + /// Callback called when the mappings in an region have changed + std::function unmap_callback{}; }; /** @@ -108,14 +115,8 @@ class FlatAllocator private: using Base = FlatAddressSpaceMap; - VaType currentLinearAllocEnd; //!< The end address for the initial linear allocation pass, once - //!< this reaches the AS limit the slower allocation path will be - //!< used - public: - VaType vaStart; //!< The base VA of the allocator, no allocations will be below this - - FlatAllocator(VaType vaStart, VaType vaLimit = Base::VaMaximum); + explicit FlatAllocator(VaType va_start, VaType va_limit = Base::VaMaximum); /** * @brief Allocates a region in the AS of the given size and returns its address @@ -131,5 +132,19 @@ public: * @brief Frees an AS region so it can be used again */ void Free(VaType virt, VaType size); + + VaType GetVAStart() const { + return va_start; + } + +private: + /// The base VA of the allocator, no allocations will be below this + VaType va_start; + + /** + * The end address for the initial linear allocation pass + * Once this reaches the AS limit the slower allocation path will be used + */ + VaType current_linear_alloc_end; }; } // namespace Common diff --git a/src/common/address_space.inc b/src/common/address_space.inc index a063782b3f..3661b298ef 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc @@ -30,137 +30,151 @@ FlatAllocator namespace Common { -MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType vaLimit_, - std::function unmapCallback_) - : unmapCallback(std::move(unmapCallback_)), vaLimit(vaLimit_) { - if (vaLimit > VaMaximum) +MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType va_limit_, + std::function unmap_callback_) + : va_limit{va_limit_}, unmap_callback{std::move(unmap_callback_)} { + if (va_limit > VaMaximum) { UNREACHABLE_MSG("Invalid VA limit!"); + } } -MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo) { - VaType virtEnd{virt + size}; +MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info) { + VaType virt_end{virt + size}; - if (virtEnd > vaLimit) - UNREACHABLE_MSG("Trying to map a block past the VA limit: virtEnd: 0x{:X}, vaLimit: 0x{:X}", - virtEnd, vaLimit); + if (virt_end > va_limit) { + UNREACHABLE_MSG( + "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", virt_end, + va_limit); + } - auto blockEndSuccessor{std::lower_bound(blocks.begin(), blocks.end(), virtEnd)}; - if (blockEndSuccessor == blocks.begin()) - UNREACHABLE_MSG("Trying to map a block before the VA start: virtEnd: 0x{:X}", virtEnd); + auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)}; + if (block_end_successor == blocks.begin()) { + UNREACHABLE_MSG("Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end); + } - auto blockEndPredecessor{std::prev(blockEndSuccessor)}; + auto block_end_predecessor{std::prev(block_end_successor)}; - if (blockEndSuccessor != blocks.end()) { + if (block_end_successor != blocks.end()) { // We have blocks in front of us, if one is directly in front then we don't have to add a // tail - if (blockEndSuccessor->virt != virtEnd) { + if (block_end_successor->virt != virt_end) { PaType tailPhys{[&]() -> PaType { if constexpr (!PaContigSplit) { - return blockEndPredecessor - ->phys; // Always propagate unmapped regions rather than calculating offset + // Always propagate unmapped regions rather than calculating offset + return block_end_predecessor->phys; } else { - if (blockEndPredecessor->Unmapped()) - return blockEndPredecessor->phys; // Always propagate unmapped regions - // rather than calculating offset - else - return blockEndPredecessor->phys + virtEnd - blockEndPredecessor->virt; + if (block_end_predecessor->Unmapped()) { + // Always propagate unmapped regions rather than calculating offset + return block_end_predecessor->phys; + } else { + return block_end_predecessor->phys + virt_end - block_end_predecessor->virt; + } } }()}; - if (blockEndPredecessor->virt >= virt) { + if (block_end_predecessor->virt >= virt) { // If this block's start would be overlapped by the map then reuse it as a tail // block - blockEndPredecessor->virt = virtEnd; - blockEndPredecessor->phys = tailPhys; - blockEndPredecessor->extraInfo = blockEndPredecessor->extraInfo; + block_end_predecessor->virt = virt_end; + block_end_predecessor->phys = tailPhys; + block_end_predecessor->extra_info = block_end_predecessor->extra_info; // No longer predecessor anymore - blockEndSuccessor = blockEndPredecessor--; + block_end_successor = block_end_predecessor--; } else { // Else insert a new one and we're done - blocks.insert(blockEndSuccessor, - {Block(virt, phys, extraInfo), - Block(virtEnd, tailPhys, blockEndPredecessor->extraInfo)}); - if (unmapCallback) - unmapCallback(virt, size); + blocks.insert(block_end_successor, + {Block(virt, phys, extra_info), + Block(virt_end, tailPhys, block_end_predecessor->extra_info)}); + if (unmap_callback) { + unmap_callback(virt, size); + } return; } } } else { - // blockEndPredecessor will always be unmapped as blocks has to be terminated by an unmapped - // chunk - if (blockEndPredecessor != blocks.begin() && blockEndPredecessor->virt >= virt) { + // block_end_predecessor will always be unmapped as blocks has to be terminated by an + // unmapped chunk + if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) { // Move the unmapped block start backwards - blockEndPredecessor->virt = virtEnd; + block_end_predecessor->virt = virt_end; // No longer predecessor anymore - blockEndSuccessor = blockEndPredecessor--; + block_end_successor = block_end_predecessor--; } else { // Else insert a new one and we're done - blocks.insert(blockEndSuccessor, - {Block(virt, phys, extraInfo), Block(virtEnd, UnmappedPa, {})}); - if (unmapCallback) - unmapCallback(virt, size); + blocks.insert(block_end_successor, + {Block(virt, phys, extra_info), Block(virt_end, UnmappedPa, {})}); + if (unmap_callback) { + unmap_callback(virt, size); + } return; } } - auto blockStartSuccessor{blockEndSuccessor}; + auto block_start_successor{block_end_successor}; // Walk the block vector to find the start successor as this is more efficient than another // binary search in most scenarios - while (std::prev(blockStartSuccessor)->virt >= virt) - blockStartSuccessor--; - - // Check that the start successor is either the end block or something in between - if (blockStartSuccessor->virt > virtEnd) { - UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", blockStartSuccessor->virt); - } else if (blockStartSuccessor->virt == virtEnd) { - // We need to create a new block as there are none spare that we would overwrite - blocks.insert(blockStartSuccessor, Block(virt, phys, extraInfo)); - } else { - // Erase overwritten blocks - if (auto eraseStart{std::next(blockStartSuccessor)}; eraseStart != blockEndSuccessor) - blocks.erase(eraseStart, blockEndSuccessor); - - // Reuse a block that would otherwise be overwritten as a start block - blockStartSuccessor->virt = virt; - blockStartSuccessor->phys = phys; - blockStartSuccessor->extraInfo = extraInfo; + while (std::prev(block_start_successor)->virt >= virt) { + block_start_successor--; } - if (unmapCallback) - unmapCallback(virt, size); + // Check that the start successor is either the end block or something in between + if (block_start_successor->virt > virt_end) { + UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); + } else if (block_start_successor->virt == virt_end) { + // We need to create a new block as there are none spare that we would overwrite + blocks.insert(block_start_successor, Block(virt, phys, extra_info)); + } else { + // Erase overwritten blocks + if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) { + blocks.erase(eraseStart, block_end_successor); + } + + // Reuse a block that would otherwise be overwritten as a start block + block_start_successor->virt = virt; + block_start_successor->phys = phys; + block_start_successor->extra_info = extra_info; + } + + if (unmap_callback) { + unmap_callback(virt, size); + } } MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { - VaType virtEnd{virt + size}; + VaType virt_end{virt + size}; - if (virtEnd > vaLimit) - UNREACHABLE_MSG("Trying to map a block past the VA limit: virtEnd: 0x{:X}, vaLimit: 0x{:X}", - virtEnd, vaLimit); + if (virt_end > va_limit) { + UNREACHABLE_MSG( + "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", virt_end, + va_limit); + } - auto blockEndSuccessor{std::lower_bound(blocks.begin(), blocks.end(), virtEnd)}; - if (blockEndSuccessor == blocks.begin()) - UNREACHABLE_MSG("Trying to unmap a block before the VA start: virtEnd: 0x{:X}", virtEnd); + auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)}; + if (block_end_successor == blocks.begin()) { + UNREACHABLE_MSG("Trying to unmap a block before the VA start: virt_end: 0x{:X}", virt_end); + } - auto blockEndPredecessor{std::prev(blockEndSuccessor)}; + auto block_end_predecessor{std::prev(block_end_successor)}; - auto walkBackToPredecessor{[&](auto iter) { - while (iter->virt >= virt) + auto walk_back_to_predecessor{[&](auto iter) { + while (iter->virt >= virt) { iter--; + } return iter; }}; - auto eraseBlocksWithEndUnmapped{[&](auto unmappedEnd) { - auto blockStartPredecessor{walkBackToPredecessor(unmappedEnd)}; - auto blockStartSuccessor{std::next(blockStartPredecessor)}; + auto erase_blocks_with_end_unmapped{[&](auto unmappedEnd) { + auto block_start_predecessor{walk_back_to_predecessor(unmappedEnd)}; + auto block_start_successor{std::next(block_start_predecessor)}; auto eraseEnd{[&]() { - if (blockStartPredecessor->Unmapped()) { + if (block_start_predecessor->Unmapped()) { // If the start predecessor is unmapped then we can erase everything in our region // and be done return std::next(unmappedEnd); @@ -174,158 +188,171 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { // We can't have two unmapped regions after each other if (eraseEnd != blocks.end() && - (eraseEnd == blockStartSuccessor || - (blockStartPredecessor->Unmapped() && eraseEnd->Unmapped()))) + (eraseEnd == block_start_successor || + (block_start_predecessor->Unmapped() && eraseEnd->Unmapped()))) { UNREACHABLE_MSG("Multiple contiguous unmapped regions are unsupported!"); + } - blocks.erase(blockStartSuccessor, eraseEnd); + blocks.erase(block_start_successor, eraseEnd); }}; // We can avoid any splitting logic if these are the case - if (blockEndPredecessor->Unmapped()) { - if (blockEndPredecessor->virt > virt) - eraseBlocksWithEndUnmapped(blockEndPredecessor); + if (block_end_predecessor->Unmapped()) { + if (block_end_predecessor->virt > virt) { + erase_blocks_with_end_unmapped(block_end_predecessor); + } - if (unmapCallback) - unmapCallback(virt, size); + if (unmap_callback) { + unmap_callback(virt, size); + } return; // The region is unmapped, bail out early - } else if (blockEndSuccessor->virt == virtEnd && blockEndSuccessor->Unmapped()) { - eraseBlocksWithEndUnmapped(blockEndSuccessor); + } else if (block_end_successor->virt == virt_end && block_end_successor->Unmapped()) { + erase_blocks_with_end_unmapped(block_end_successor); - if (unmapCallback) - unmapCallback(virt, size); + if (unmap_callback) { + unmap_callback(virt, size); + } return; // The region is unmapped here and doesn't need splitting, bail out early - } else if (blockEndSuccessor == blocks.end()) { + } else if (block_end_successor == blocks.end()) { // This should never happen as the end should always follow an unmapped block UNREACHABLE_MSG("Unexpected Memory Manager state!"); - } else if (blockEndSuccessor->virt != virtEnd) { + } else if (block_end_successor->virt != virt_end) { // If one block is directly in front then we don't have to add a tail // The previous block is mapped so we will need to add a tail with an offset PaType tailPhys{[&]() { - if constexpr (PaContigSplit) - return blockEndPredecessor->phys + virtEnd - blockEndPredecessor->virt; - else - return blockEndPredecessor->phys; + if constexpr (PaContigSplit) { + return block_end_predecessor->phys + virt_end - block_end_predecessor->virt; + } else { + return block_end_predecessor->phys; + } }()}; - if (blockEndPredecessor->virt >= virt) { + if (block_end_predecessor->virt >= virt) { // If this block's start would be overlapped by the unmap then reuse it as a tail block - blockEndPredecessor->virt = virtEnd; - blockEndPredecessor->phys = tailPhys; + block_end_predecessor->virt = virt_end; + block_end_predecessor->phys = tailPhys; // No longer predecessor anymore - blockEndSuccessor = blockEndPredecessor--; + block_end_successor = block_end_predecessor--; } else { - blocks.insert(blockEndSuccessor, + blocks.insert(block_end_successor, {Block(virt, UnmappedPa, {}), - Block(virtEnd, tailPhys, blockEndPredecessor->extraInfo)}); - if (unmapCallback) - unmapCallback(virt, size); + Block(virt_end, tailPhys, block_end_predecessor->extra_info)}); + if (unmap_callback) { + unmap_callback(virt, size); + } - return; // The previous block is mapped and ends before + // The previous block is mapped and ends before + return; } } // Walk the block vector to find the start predecessor as this is more efficient than another // binary search in most scenarios - auto blockStartPredecessor{walkBackToPredecessor(blockEndSuccessor)}; - auto blockStartSuccessor{std::next(blockStartPredecessor)}; + auto block_start_predecessor{walk_back_to_predecessor(block_end_successor)}; + auto block_start_successor{std::next(block_start_predecessor)}; - if (blockStartSuccessor->virt > virtEnd) { - UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", blockStartSuccessor->virt); - } else if (blockStartSuccessor->virt == virtEnd) { + if (block_start_successor->virt > virt_end) { + UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); + } else if (block_start_successor->virt == virt_end) { // There are no blocks between the start and the end that would let us skip inserting a new // one for head // The previous block is may be unmapped, if so we don't need to insert any unmaps after it - if (blockStartPredecessor->Mapped()) - blocks.insert(blockStartSuccessor, Block(virt, UnmappedPa, {})); - } else if (blockStartPredecessor->Unmapped()) { + if (block_start_predecessor->Mapped()) { + blocks.insert(block_start_successor, Block(virt, UnmappedPa, {})); + } + } else if (block_start_predecessor->Unmapped()) { // If the previous block is unmapped - blocks.erase(blockStartSuccessor, blockEndPredecessor); + blocks.erase(block_start_successor, block_end_predecessor); } else { // Erase overwritten blocks, skipping the first one as we have written the unmapped start // block there - if (auto eraseStart{std::next(blockStartSuccessor)}; eraseStart != blockEndSuccessor) - blocks.erase(eraseStart, blockEndSuccessor); + if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) { + blocks.erase(eraseStart, block_end_successor); + } // Add in the unmapped block header - blockStartSuccessor->virt = virt; - blockStartSuccessor->phys = UnmappedPa; + block_start_successor->virt = virt; + block_start_successor->phys = UnmappedPa; } - if (unmapCallback) - unmapCallback(virt, size); + if (unmap_callback) + unmap_callback(virt, size); } -ALLOC_MEMBER_CONST()::FlatAllocator(VaType vaStart_, VaType vaLimit_) - : Base(vaLimit_), currentLinearAllocEnd(vaStart_), vaStart(vaStart_) {} +ALLOC_MEMBER_CONST()::FlatAllocator(VaType va_start_, VaType va_limit_) + : Base{va_limit_}, va_start{va_start_}, current_linear_alloc_end{va_start_} {} ALLOC_MEMBER(VaType)::Allocate(VaType size) { - std::scoped_lock lock(this->blockMutex); + std::scoped_lock lock(this->block_mutex); - VaType allocStart{UnmappedVa}; - VaType allocEnd{currentLinearAllocEnd + size}; + VaType alloc_start{UnmappedVa}; + VaType alloc_end{current_linear_alloc_end + size}; // Avoid searching backwards in the address space if possible - if (allocEnd >= currentLinearAllocEnd && allocEnd <= this->vaLimit) { - auto allocEndSuccessor{ - std::lower_bound(this->blocks.begin(), this->blocks.end(), allocEnd)}; - if (allocEndSuccessor == this->blocks.begin()) + if (alloc_end >= current_linear_alloc_end && alloc_end <= this->va_limit) { + auto alloc_end_successor{ + std::lower_bound(this->blocks.begin(), this->blocks.end(), alloc_end)}; + if (alloc_end_successor == this->blocks.begin()) { UNREACHABLE_MSG("First block in AS map is invalid!"); + } - auto allocEndPredecessor{std::prev(allocEndSuccessor)}; - if (allocEndPredecessor->virt <= currentLinearAllocEnd) { - allocStart = currentLinearAllocEnd; + auto alloc_end_predecessor{std::prev(alloc_end_successor)}; + if (alloc_end_predecessor->virt <= current_linear_alloc_end) { + alloc_start = current_linear_alloc_end; } else { // Skip over fixed any mappings in front of us - while (allocEndSuccessor != this->blocks.end()) { - if (allocEndSuccessor->virt - allocEndPredecessor->virt < size || - allocEndPredecessor->Mapped()) { - allocStart = allocEndPredecessor->virt; + while (alloc_end_successor != this->blocks.end()) { + if (alloc_end_successor->virt - alloc_end_predecessor->virt < size || + alloc_end_predecessor->Mapped()) { + alloc_start = alloc_end_predecessor->virt; break; } - allocEndPredecessor = allocEndSuccessor++; + alloc_end_predecessor = alloc_end_successor++; // Use the VA limit to calculate if we can fit in the final block since it has no // successor - if (allocEndSuccessor == this->blocks.end()) { - allocEnd = allocEndPredecessor->virt + size; + if (alloc_end_successor == this->blocks.end()) { + alloc_end = alloc_end_predecessor->virt + size; - if (allocEnd >= allocEndPredecessor->virt && allocEnd <= this->vaLimit) - allocStart = allocEndPredecessor->virt; + if (alloc_end >= alloc_end_predecessor->virt && alloc_end <= this->va_limit) { + alloc_start = alloc_end_predecessor->virt; + } } } } } - if (allocStart != UnmappedVa) { - currentLinearAllocEnd = allocStart + size; + if (alloc_start != UnmappedVa) { + current_linear_alloc_end = alloc_start + size; } else { // If linear allocation overflows the AS then find a gap - if (this->blocks.size() <= 2) + if (this->blocks.size() <= 2) { UNREACHABLE_MSG("Unexpected allocator state!"); - - auto searchPredecessor{this->blocks.begin()}; - auto searchSuccessor{std::next(searchPredecessor)}; - - while (searchSuccessor != this->blocks.end() && - (searchSuccessor->virt - searchPredecessor->virt < size || - searchPredecessor->Mapped())) { - searchPredecessor = searchSuccessor++; } - if (searchSuccessor != this->blocks.end()) - allocStart = searchPredecessor->virt; - else + auto search_predecessor{this->blocks.begin()}; + auto search_successor{std::next(search_predecessor)}; + + while (search_successor != this->blocks.end() && + (search_successor->virt - search_predecessor->virt < size || + search_predecessor->Mapped())) { + search_predecessor = search_successor++; + } + + if (search_successor != this->blocks.end()) { + alloc_start = search_predecessor->virt; + } else { return {}; // AS is full + } } - this->MapLocked(allocStart, true, size, {}); - return allocStart; + this->MapLocked(alloc_start, true, size, {}); + return alloc_start; } ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index b48f7fcafd..7a95f53055 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -472,16 +472,16 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { params.regions = std::array{ VaRegion{ - .offset = vm.small_page_allocator->vaStart << VM::PAGE_SIZE_BITS, + .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS, .page_size = VM::YUZU_PAGESIZE, ._pad0_{}, - .pages = vm.small_page_allocator->vaLimit - vm.small_page_allocator->vaStart, + .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(), }, VaRegion{ - .offset = vm.big_page_allocator->vaStart << vm.big_page_size_bits, + .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits, .page_size = vm.big_page_size, ._pad0_{}, - .pages = vm.big_page_allocator->vaLimit - vm.big_page_allocator->vaStart, + .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(), }, }; } From 11e1cbbdbde8269e7cdb0e150f25639223bdd3e6 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 29 Jun 2022 20:36:39 -0400 Subject: [PATCH 102/245] address_space: Rename va_start to virt_start Avoids conflicting with the va_start macro --- src/common/address_space.h | 6 +++--- src/common/address_space.inc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/address_space.h b/src/common/address_space.h index bf649018c5..9222b2fdc6 100644 --- a/src/common/address_space.h +++ b/src/common/address_space.h @@ -116,7 +116,7 @@ private: using Base = FlatAddressSpaceMap; public: - explicit FlatAllocator(VaType va_start, VaType va_limit = Base::VaMaximum); + explicit FlatAllocator(VaType virt_start, VaType va_limit = Base::VaMaximum); /** * @brief Allocates a region in the AS of the given size and returns its address @@ -134,12 +134,12 @@ public: void Free(VaType virt, VaType size); VaType GetVAStart() const { - return va_start; + return virt_start; } private: /// The base VA of the allocator, no allocations will be below this - VaType va_start; + VaType virt_start; /** * The end address for the initial linear allocation pass diff --git a/src/common/address_space.inc b/src/common/address_space.inc index 3661b298ef..9f957c81d9 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc @@ -284,8 +284,8 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { unmap_callback(virt, size); } -ALLOC_MEMBER_CONST()::FlatAllocator(VaType va_start_, VaType va_limit_) - : Base{va_limit_}, va_start{va_start_}, current_linear_alloc_end{va_start_} {} +ALLOC_MEMBER_CONST()::FlatAllocator(VaType virt_start_, VaType va_limit_) + : Base{va_limit_}, virt_start{virt_start_}, current_linear_alloc_end{virt_start_} {} ALLOC_MEMBER(VaType)::Allocate(VaType size) { std::scoped_lock lock(this->block_mutex); From 903705043dd768fe2bbeeddc621994ac61aae561 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 2 Aug 2022 03:38:19 -0400 Subject: [PATCH 103/245] nvdisp: End system frame after requesting to swap buffers Fixes frametime reporting --- src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 18c5324a9a..4122fc98de 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -50,8 +50,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, stride, format, transform, crop_rect}; - system.GetPerfStats().EndSystemFrame(); system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); + system.GetPerfStats().EndSystemFrame(); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } From c80ed6d81fef5858508ac4b841defe8ee3a8663d Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 19 Aug 2022 21:58:25 -0400 Subject: [PATCH 104/245] general: rework usages of UNREACHABLE macro --- src/common/address_space.inc | 31 ++++++++++--------- .../service/nvdrv/core/syncpoint_manager.cpp | 14 ++++----- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 10 +++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/common/address_space.inc b/src/common/address_space.inc index 9f957c81d9..2195dabd54 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc @@ -34,7 +34,7 @@ MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType va_limit_, std::function unmap_callback_) : va_limit{va_limit_}, unmap_callback{std::move(unmap_callback_)} { if (va_limit > VaMaximum) { - UNREACHABLE_MSG("Invalid VA limit!"); + ASSERT_MSG(false, "Invalid VA limit!"); } } @@ -42,14 +42,14 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf VaType virt_end{virt + size}; if (virt_end > va_limit) { - UNREACHABLE_MSG( - "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", virt_end, - va_limit); + ASSERT_MSG(false, + "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", + virt_end, va_limit); } auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)}; if (block_end_successor == blocks.begin()) { - UNREACHABLE_MSG("Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end); + ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end); } auto block_end_predecessor{std::prev(block_end_successor)}; @@ -124,7 +124,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf // Check that the start successor is either the end block or something in between if (block_start_successor->virt > virt_end) { - UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); + ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); } else if (block_start_successor->virt == virt_end) { // We need to create a new block as there are none spare that we would overwrite blocks.insert(block_start_successor, Block(virt, phys, extra_info)); @@ -149,14 +149,15 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { VaType virt_end{virt + size}; if (virt_end > va_limit) { - UNREACHABLE_MSG( - "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", virt_end, - va_limit); + ASSERT_MSG(false, + "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", + virt_end, va_limit); } auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)}; if (block_end_successor == blocks.begin()) { - UNREACHABLE_MSG("Trying to unmap a block before the VA start: virt_end: 0x{:X}", virt_end); + ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}", + virt_end); } auto block_end_predecessor{std::prev(block_end_successor)}; @@ -190,7 +191,7 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { if (eraseEnd != blocks.end() && (eraseEnd == block_start_successor || (block_start_predecessor->Unmapped() && eraseEnd->Unmapped()))) { - UNREACHABLE_MSG("Multiple contiguous unmapped regions are unsupported!"); + ASSERT_MSG(false, "Multiple contiguous unmapped regions are unsupported!"); } blocks.erase(block_start_successor, eraseEnd); @@ -217,7 +218,7 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { return; // The region is unmapped here and doesn't need splitting, bail out early } else if (block_end_successor == blocks.end()) { // This should never happen as the end should always follow an unmapped block - UNREACHABLE_MSG("Unexpected Memory Manager state!"); + ASSERT_MSG(false, "Unexpected Memory Manager state!"); } else if (block_end_successor->virt != virt_end) { // If one block is directly in front then we don't have to add a tail @@ -256,7 +257,7 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { auto block_start_successor{std::next(block_start_predecessor)}; if (block_start_successor->virt > virt_end) { - UNREACHABLE_MSG("Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); + ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); } else if (block_start_successor->virt == virt_end) { // There are no blocks between the start and the end that would let us skip inserting a new // one for head @@ -298,7 +299,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) { auto alloc_end_successor{ std::lower_bound(this->blocks.begin(), this->blocks.end(), alloc_end)}; if (alloc_end_successor == this->blocks.begin()) { - UNREACHABLE_MSG("First block in AS map is invalid!"); + ASSERT_MSG(false, "First block in AS map is invalid!"); } auto alloc_end_predecessor{std::prev(alloc_end_successor)}; @@ -332,7 +333,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) { current_linear_alloc_end = alloc_start + size; } else { // If linear allocation overflows the AS then find a gap if (this->blocks.size() <= 2) { - UNREACHABLE_MSG("Unexpected allocator state!"); + ASSERT_MSG(false, "Unexpected allocator state!"); } auto search_predecessor{this->blocks.begin()}; diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index 0bb2aec974..072b3a22f1 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -29,7 +29,7 @@ SyncpointManager::~SyncpointManager() = default; u32 SyncpointManager::ReserveSyncpoint(u32 id, bool clientManaged) { if (syncpoints.at(id).reserved) { - UNREACHABLE_MSG("Requested syncpoint is in use"); + ASSERT_MSG(false, "Requested syncpoint is in use"); return 0; } @@ -45,7 +45,7 @@ u32 SyncpointManager::FindFreeSyncpoint() { return i; } } - UNREACHABLE_MSG("Failed to find a free syncpoint!"); + ASSERT_MSG(false, "Failed to find a free syncpoint!"); return 0; } @@ -68,7 +68,7 @@ bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) { const SyncpointInfo& syncpoint{syncpoints.at(id)}; if (!syncpoint.reserved) { - UNREACHABLE(); + ASSERT(false); return 0; } @@ -83,7 +83,7 @@ bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) { u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { if (!syncpoints.at(id).reserved) { - UNREACHABLE(); + ASSERT(false); return 0; } @@ -92,7 +92,7 @@ u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { if (!syncpoints.at(id).reserved) { - UNREACHABLE(); + ASSERT(false); return 0; } @@ -101,7 +101,7 @@ u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { u32 SyncpointManager::UpdateMin(u32 id) { if (!syncpoints.at(id).reserved) { - UNREACHABLE(); + ASSERT(false); return 0; } @@ -111,7 +111,7 @@ u32 SyncpointManager::UpdateMin(u32 id) { NvFence SyncpointManager::GetSyncpointFence(u32 id) { if (!syncpoints.at(id).reserved) { - UNREACHABLE(); + ASSERT(false); return NvFence{}; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 7a95f53055..192503ffc3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -96,7 +96,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& std::scoped_lock lock(mutex); if (vm.initialised) { - UNREACHABLE_MSG("Cannot initialise an address space twice!"); + ASSERT_MSG(false, "Cannot initialise an address space twice!"); return NvResult::InvalidState; } @@ -174,7 +174,7 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector& input, std::vector< } else { params.offset = static_cast(allocator.Allocate(params.pages)) << page_size_bits; if (!params.offset) { - UNREACHABLE_MSG("Failed to allocate free space in the GPU AS!"); + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); return NvResult::InsufficientMemory; } } @@ -372,7 +372,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectoralign, VM::YUZU_PAGESIZE)) return false; else { - UNREACHABLE(); + ASSERT(false); return false; } }()}; @@ -382,7 +382,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorfirst) + size > alloc->second.size) { - UNREACHABLE_MSG("Cannot perform a fixed mapping into an unallocated region!"); + ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!"); return NvResult::BadValue; } @@ -403,7 +403,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector(Common::AlignUp(size, page_size) >> page_size_bits))) << page_size_bits; if (!params.offset) { - UNREACHABLE_MSG("Failed to allocate free space in the GPU AS!"); + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); return NvResult::InsufficientMemory; } From 0d99b7962d8e06958668168cdae155fd1e3d1757 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 19 Aug 2022 22:15:23 -0400 Subject: [PATCH 105/245] state_tracker: workaround channel setup for homebrew --- src/video_core/query_cache.h | 6 ++++-- src/video_core/renderer_opengl/gl_state_tracker.cpp | 2 +- src/video_core/renderer_opengl/gl_state_tracker.h | 1 + src/video_core/renderer_vulkan/vk_state_tracker.cpp | 3 ++- src/video_core/renderer_vulkan/vk_state_tracker.h | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index eb68ea6388..b0ebe71b74 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -135,8 +135,10 @@ public: /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. void UpdateCounters() { std::unique_lock lock{mutex}; - const auto& regs = maxwell3d->regs; - Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); + if (maxwell3d) { + const auto& regs = maxwell3d->regs; + Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); + } } /// Resets a counter to zero. It doesn't disable the query after resetting. diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index 3657f867d3..a8f3a0f57b 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -237,6 +237,6 @@ void StateTracker::InvalidateState() { flags->set(); } -StateTracker::StateTracker() : flags{} {} +StateTracker::StateTracker() : flags{&default_flags} {} } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h index 97d32768b5..19bcf3f355 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.h +++ b/src/video_core/renderer_opengl/gl_state_tracker.h @@ -223,6 +223,7 @@ public: private: Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags{}; GLuint framebuffer = 0; GLuint index_buffer = 0; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 5a11d3267b..f234e1a310 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -206,6 +206,7 @@ void StateTracker::InvalidateState() { flags->set(); } -StateTracker::StateTracker() : flags{}, invalidation_flags{MakeInvalidationFlags()} {} +StateTracker::StateTracker() + : flags{&default_flags}, default_flags{}, invalidation_flags{MakeInvalidationFlags()} {} } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index c107d9c246..2296dea602 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -161,6 +161,7 @@ private: } Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags; Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; }; From ca3db0d7c94a20668781830ff852dbf512598efb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 1 Sep 2022 05:45:22 +0200 Subject: [PATCH 106/245] General: address feedback --- src/common/multi_level_page_table.h | 8 +-- src/core/hle/service/nvdrv/core/container.cpp | 12 +++- src/core/hle/service/nvdrv/core/container.h | 23 ++++++-- src/core/hle/service/nvdrv/core/nvmap.cpp | 4 +- src/core/hle/service/nvdrv/core/nvmap.h | 59 +++++++++---------- .../service/nvdrv/core/syncpoint_manager.cpp | 32 +++++----- .../service/nvdrv/core/syncpoint_manager.h | 34 +++++------ .../service/nvdrv/devices/nvhost_as_gpu.cpp | 28 +++++---- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 2 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 2 +- .../service/nvdrv/devices/nvhost_nvdec.cpp | 13 ++-- .../hle/service/nvdrv/devices/nvhost_nvdec.h | 3 - .../nvdrv/devices/nvhost_nvdec_common.cpp | 13 ++-- .../nvdrv/devices/nvhost_nvdec_common.h | 5 -- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 16 ++--- .../hle/service/nvdrv/devices/nvhost_vic.h | 3 - src/core/hle/service/nvdrv/devices/nvmap.h | 4 +- src/core/hle/service/nvdrv/nvdrv.cpp | 4 +- src/core/hle/service/nvdrv/nvdrv.h | 2 +- src/video_core/control/channel_state.cpp | 7 +-- src/video_core/control/channel_state.h | 4 +- src/video_core/control/channel_state_cache.h | 2 +- src/video_core/control/scheduler.cpp | 6 +- src/video_core/control/scheduler.h | 2 +- src/video_core/engines/maxwell_dma.h | 2 +- src/video_core/gpu.cpp | 2 +- src/video_core/host1x/host1x.h | 2 +- src/video_core/host1x/syncpoint_manager.cpp | 12 ++-- src/video_core/host1x/syncpoint_manager.h | 24 ++++---- src/video_core/memory_manager.h | 2 +- 30 files changed, 167 insertions(+), 165 deletions(-) diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h index 08092c89a6..31f6676a06 100644 --- a/src/common/multi_level_page_table.h +++ b/src/common/multi_level_page_table.h @@ -46,19 +46,19 @@ public: void ReserveRange(u64 start, std::size_t size); - [[nodiscard]] constexpr const BaseAddr& operator[](std::size_t index) const { + [[nodiscard]] const BaseAddr& operator[](std::size_t index) const { return base_ptr[index]; } - [[nodiscard]] constexpr BaseAddr& operator[](std::size_t index) { + [[nodiscard]] BaseAddr& operator[](std::size_t index) { return base_ptr[index]; } - [[nodiscard]] constexpr BaseAddr* data() { + [[nodiscard]] BaseAddr* data() { return base_ptr; } - [[nodiscard]] constexpr const BaseAddr* data() const { + [[nodiscard]] const BaseAddr* data() const { return base_ptr; } diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index d2a6326466..37ca24f5db 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -10,9 +10,11 @@ namespace Service::Nvidia::NvCore { struct ContainerImpl { - ContainerImpl(Tegra::Host1x::Host1x& host1x_) : file{host1x_}, manager{host1x_} {} + explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) + : file{host1x_}, manager{host1x_}, device_file_data{} {} NvMap file; SyncpointManager manager; + Container::Host1xDeviceFileData device_file_data; }; Container::Container(Tegra::Host1x::Host1x& host1x_) { @@ -29,6 +31,14 @@ const NvMap& Container::GetNvMapFile() const { return impl->file; } +Container::Host1xDeviceFileData& Container::Host1xDeviceFile() { + return impl->device_file_data; +} + +const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const { + return impl->device_file_data; +} + SyncpointManager& Container::GetSyncpointManager() { return impl->manager; } diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index 5c8b958034..b4b63ac90c 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -4,15 +4,15 @@ #pragma once +#include #include +#include -namespace Tegra { +#include "core/hle/service/nvdrv/nvdata.h" -namespace Host1x { +namespace Tegra::Host1x { class Host1x; -} // namespace Host1x - -} // namespace Tegra +} // namespace Tegra::Host1x namespace Service::Nvidia::NvCore { @@ -23,7 +23,7 @@ struct ContainerImpl; class Container { public: - Container(Tegra::Host1x::Host1x& host1x); + explicit Container(Tegra::Host1x::Host1x& host1x); ~Container(); NvMap& GetNvMapFile(); @@ -34,6 +34,17 @@ public: const SyncpointManager& GetSyncpointManager() const; + struct Host1xDeviceFileData { + std::unordered_map fd_to_id{}; + std::deque syncpts_accumulated{}; + u32 nvdec_next_id{}; + u32 vic_next_id{}; + }; + + Host1xDeviceFileData& Host1xDeviceFile(); + + const Host1xDeviceFileData& Host1xDeviceFile() const; + private: std::unique_ptr impl; }; diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index e63ec77174..fbd8a74a55 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -119,7 +119,7 @@ std::shared_ptr NvMap::GetHandle(Handle::Id handle) { std::scoped_lock lock(handles_lock); try { return handles.at(handle); - } catch ([[maybe_unused]] std::out_of_range& e) { + } catch (std::out_of_range&) { return nullptr; } } @@ -128,7 +128,7 @@ VAddr NvMap::GetHandleAddress(Handle::Id handle) { std::scoped_lock lock(handles_lock); try { return handles.at(handle)->address; - } catch ([[maybe_unused]] std::out_of_range& e) { + } catch (std::out_of_range&) { return 0; } } diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index 6d6dac023a..b9dd3801f4 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -98,35 +98,6 @@ public: } }; -private: - std::list> unmap_queue{}; - std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` - - std::unordered_map> - handles{}; //!< Main owning map of handles - std::mutex handles_lock; //!< Protects access to `handles` - - static constexpr u32 HandleIdIncrement{ - 4}; //!< Each new handle ID is an increment of 4 from the previous - std::atomic next_handle_id{HandleIdIncrement}; - Tegra::Host1x::Host1x& host1x; - - void AddHandle(std::shared_ptr handle); - - /** - * @brief Unmaps and frees the SMMU memory region a handle is mapped to - * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this - */ - void UnmapHandle(Handle& handle_description); - - /** - * @brief Removes a handle from the map taking its dupes into account - * @note handle_description.mutex MUST be locked when calling this - * @return If the handle was removed from the map - */ - bool TryRemoveHandle(const Handle& handle_description); - -public: /** * @brief Encapsulates the result of a FreeHandle operation */ @@ -136,7 +107,7 @@ public: bool was_uncached; //!< If the handle was allocated as uncached }; - NvMap(Tegra::Host1x::Host1x& host1x); + explicit NvMap(Tegra::Host1x::Host1x& host1x); /** * @brief Creates an unallocated handle of the given size @@ -172,5 +143,33 @@ public: * describing the prior state of the handle */ std::optional FreeHandle(Handle::Id handle, bool internal_session); + +private: + std::list> unmap_queue{}; + std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` + + std::unordered_map> + handles{}; //!< Main owning map of handles + std::mutex handles_lock; //!< Protects access to `handles` + + static constexpr u32 HandleIdIncrement{ + 4}; //!< Each new handle ID is an increment of 4 from the previous + std::atomic next_handle_id{HandleIdIncrement}; + Tegra::Host1x::Host1x& host1x; + + void AddHandle(std::shared_ptr handle); + + /** + * @brief Unmaps and frees the SMMU memory region a handle is mapped to + * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this + */ + void UnmapHandle(Handle& handle_description); + + /** + * @brief Removes a handle from the map taking its dupes into account + * @note handle_description.mutex MUST be locked when calling this + * @return If the handle was removed from the map + */ + bool TryRemoveHandle(const Handle& handle_description); }; } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index 072b3a22f1..eda2041a0d 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -18,23 +18,23 @@ SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host ReserveSyncpoint(VBlank0SyncpointId, true); ReserveSyncpoint(VBlank1SyncpointId, true); - for (u32 syncpointId : channel_syncpoints) { - if (syncpointId) { - ReserveSyncpoint(syncpointId, false); + for (u32 syncpoint_id : channel_syncpoints) { + if (syncpoint_id) { + ReserveSyncpoint(syncpoint_id, false); } } } SyncpointManager::~SyncpointManager() = default; -u32 SyncpointManager::ReserveSyncpoint(u32 id, bool clientManaged) { +u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) { if (syncpoints.at(id).reserved) { ASSERT_MSG(false, "Requested syncpoint is in use"); return 0; } syncpoints.at(id).reserved = true; - syncpoints.at(id).interfaceManaged = clientManaged; + syncpoints.at(id).interface_managed = client_managed; return id; } @@ -49,9 +49,9 @@ u32 SyncpointManager::FindFreeSyncpoint() { return 0; } -u32 SyncpointManager::AllocateSyncpoint(bool clientManaged) { +u32 SyncpointManager::AllocateSyncpoint(bool client_managed) { std::lock_guard lock(reservation_lock); - return ReserveSyncpoint(FindFreeSyncpoint(), clientManaged); + return ReserveSyncpoint(FindFreeSyncpoint(), client_managed); } void SyncpointManager::FreeSyncpoint(u32 id) { @@ -64,7 +64,7 @@ bool SyncpointManager::IsSyncpointAllocated(u32 id) { return (id <= SyncpointCount) && syncpoints[id].reserved; } -bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) { +bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const { const SyncpointInfo& syncpoint{syncpoints.at(id)}; if (!syncpoint.reserved) { @@ -74,10 +74,10 @@ bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) { // If the interface manages counters then we don't keep track of the maximum value as it handles // sanity checking the values then - if (syncpoint.interfaceManaged) { - return static_cast(syncpoint.counterMin - threshold) >= 0; + if (syncpoint.interface_managed) { + return static_cast(syncpoint.counter_min - threshold) >= 0; } else { - return (syncpoint.counterMax - threshold) >= (syncpoint.counterMin - threshold); + return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold); } } @@ -87,7 +87,7 @@ u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { return 0; } - return syncpoints.at(id).counterMax += amount; + return syncpoints.at(id).counter_max += amount; } u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { @@ -96,7 +96,7 @@ u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { return 0; } - return syncpoints.at(id).counterMin; + return syncpoints.at(id).counter_min; } u32 SyncpointManager::UpdateMin(u32 id) { @@ -105,8 +105,8 @@ u32 SyncpointManager::UpdateMin(u32 id) { return 0; } - syncpoints.at(id).counterMin = host1x.GetSyncpointManager().GetHostSyncpointValue(id); - return syncpoints.at(id).counterMin; + syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id); + return syncpoints.at(id).counter_min; } NvFence SyncpointManager::GetSyncpointFence(u32 id) { @@ -115,7 +115,7 @@ NvFence SyncpointManager::GetSyncpointFence(u32 id) { return NvFence{}; } - return {.id = static_cast(id), .value = syncpoints.at(id).counterMax}; + return {.id = static_cast(id), .value = syncpoints.at(id).counter_max}; } } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h index 6b71cd33df..b76ef90326 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -11,13 +11,9 @@ #include "common/common_types.h" #include "core/hle/service/nvdrv/nvdata.h" -namespace Tegra { - -namespace Host1x { +namespace Tegra::Host1x { class Host1x; -} // namespace Host1x - -} // namespace Tegra +} // namespace Tegra::Host1x namespace Service::Nvidia::NvCore { @@ -54,15 +50,15 @@ public: * @brief Finds a free syncpoint and reserves it * @return The ID of the reserved syncpoint */ - u32 AllocateSyncpoint(bool clientManaged); + u32 AllocateSyncpoint(bool client_managed); /** * @url * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259 */ - bool HasSyncpointExpired(u32 id, u32 threshold); + bool HasSyncpointExpired(u32 id, u32 threshold) const; - bool IsFenceSignalled(NvFence fence) { + bool IsFenceSignalled(NvFence fence) const { return HasSyncpointExpired(fence.id, fence.value); } @@ -107,7 +103,7 @@ private: /** * @note reservation_lock should be locked when calling this */ - u32 ReserveSyncpoint(u32 id, bool clientManaged); + u32 ReserveSyncpoint(u32 id, bool client_managed); /** * @return The ID of the first free syncpoint @@ -115,15 +111,15 @@ private: u32 FindFreeSyncpoint(); struct SyncpointInfo { - std::atomic counterMin; //!< The least value the syncpoint can be (The value it was - //!< when it was last synchronized with host1x) - std::atomic counterMax; //!< The maximum value the syncpoint can reach according to the - //!< current usage - bool interfaceManaged; //!< If the syncpoint is managed by a host1x client interface, a - //!< client interface is a HW block that can handle host1x - //!< transactions on behalf of a host1x client (Which would otherwise - //!< need to be manually synced using PIO which is synchronous and - //!< requires direct cooperation of the CPU) + std::atomic counter_min; //!< The least value the syncpoint can be (The value it was + //!< when it was last synchronized with host1x) + std::atomic counter_max; //!< The maximum value the syncpoint can reach according to + //!< the current usage + bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a + //!< client interface is a HW block that can handle host1x + //!< transactions on behalf of a host1x client (Which would + //!< otherwise need to be manually synced using PIO which is + //!< synchronous and requires direct cooperation of the CPU) bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved //!< value }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 192503ffc3..6411dbf433 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -106,7 +106,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& return NvResult::BadValue; } - if (!(params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES)) { + if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size); return NvResult::BadValue; } @@ -124,12 +124,13 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& vm.va_range_end = params.va_range_end; } - const u64 start_pages{vm.va_range_start >> VM::PAGE_SIZE_BITS}; - const u64 end_pages{vm.va_range_split >> VM::PAGE_SIZE_BITS}; + const auto start_pages{static_cast(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; + const auto end_pages{static_cast(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; vm.small_page_allocator = std::make_shared(start_pages, end_pages); - const u64 start_big_pages{vm.va_range_split >> vm.big_page_size_bits}; - const u64 end_big_pages{(vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits}; + const auto start_big_pages{static_cast(vm.va_range_split >> vm.big_page_size_bits)}; + const auto end_big_pages{ + static_cast((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; vm.big_page_allocator = std::make_unique(start_big_pages, end_big_pages); gmmu = std::make_shared(system, 40, vm.big_page_size_bits, @@ -210,10 +211,11 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) { // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state // Only FreeSpace can unmap them fully - if (mapping->sparse_alloc) + if (mapping->sparse_alloc) { gmmu->MapSparse(offset, mapping->size, mapping->big_page); - else + } else { gmmu->Unmap(offset, mapping->size); + } mapping_map.erase(offset); } @@ -256,7 +258,7 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector& input, std::vector& allocator.Free(static_cast(params.offset >> page_size_bits), static_cast(allocation.size >> page_size_bits)); allocation_map.erase(params.offset); - } catch ([[maybe_unused]] const std::out_of_range& e) { + } catch (const std::out_of_range&) { return NvResult::BadValue; } @@ -351,7 +353,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorMap(gpu_address, cpu_address, params.mapping_size, mapping->big_page); return NvResult::Success; - } catch ([[maybe_unused]] const std::out_of_range& e) { + } catch (const std::out_of_range&) { LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}", params.offset); return NvResult::BadValue; @@ -367,11 +369,11 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectororig_size}; bool big_page{[&]() { - if (Common::IsAligned(handle->align, vm.big_page_size)) + if (Common::IsAligned(handle->align, vm.big_page_size)) { return true; - else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) + } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) { return false; - else { + } else { ASSERT(false); return false; } @@ -450,7 +452,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vector& input, std::vectorinitiated) { + if (channel_state->initialized) { LOG_CRITICAL(Service_NVDRV, "Already allocated!"); return NvResult::AlreadyAllocated; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index fed5370394..1703f9cc37 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -5,13 +5,12 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -u32 nvhost_nvdec::next_id{}; - nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} nvhost_nvdec::~nvhost_nvdec() = default; @@ -22,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& case 0x0: switch (command.cmd) { case 0x1: { - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; } return Submit(fd, input, output); } @@ -74,8 +74,9 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) { void nvhost_nvdec::OnClose(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } system.AudioCore().SetNVDECActive(false); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 3261ce1d40..c1b4e53e83 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -22,9 +22,6 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - static u32 next_id; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 2ec1ad3e9e..99eede702f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -46,13 +46,11 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } } // Anonymous namespace -std::unordered_map nvhost_nvdec_common::fd_to_id{}; -std::deque nvhost_nvdec_common::syncpts_accumulated{}; - nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, NvCore::ChannelType channel_type_) : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { + auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; if (syncpts_accumulated.empty()) { channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); } else { @@ -60,8 +58,9 @@ nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Containe syncpts_accumulated.pop_front(); } } + nvhost_nvdec_common::~nvhost_nvdec_common() { - syncpts_accumulated.push_back(channel_syncpoint); + core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); } NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { @@ -108,7 +107,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector& input, Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), cmdlist.size() * sizeof(u32)); - gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); + gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); } std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); // Some games expect command_buffers to be written back @@ -186,8 +185,4 @@ Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { return nullptr; } -void nvhost_nvdec_common::Reset() { - fd_to_id.clear(); -} - } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 93990bb9b8..fe76100c86 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -25,8 +25,6 @@ public: NvCore::ChannelType channel_type); ~nvhost_nvdec_common() override; - static void Reset(); - protected: struct IoctlSetNvmapFD { s32_le nvmap_fd{}; @@ -119,7 +117,6 @@ protected: Kernel::KEvent* QueryEvent(u32 event_id) override; - static std::unordered_map fd_to_id; u32 channel_syncpoint; s32_le nvmap_fd{}; u32_le submit_timeout{}; @@ -128,8 +125,6 @@ protected: NvCore::NvMap& nvmap; NvCore::ChannelType channel_type; std::array device_syncpoints{}; - - static std::deque syncpts_accumulated; }; }; // namespace Devices } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 2e4ff988cd..73f97136e3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -4,13 +4,12 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -u32 nvhost_vic::next_id{}; - nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {} @@ -21,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& i switch (command.group) { case 0x0: switch (command.cmd) { - case 0x1: - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + case 0x1: { + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; } return Submit(fd, input, output); + } case 0x2: return GetSyncpoint(input, output); case 0x3: @@ -69,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& i void nvhost_vic::OnOpen(DeviceFD fd) {} void nvhost_vic::OnClose(DeviceFD fd) { - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 59e23b41ef..f164caafb3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -21,8 +21,5 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - static u32 next_id; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 52e1d7cff9..e9bfd03580 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -23,8 +23,8 @@ public: explicit nvmap(Core::System& system_, NvCore::Container& container); ~nvmap() override; - nvmap(nvmap const&) = delete; - nvmap& operator=(nvmap const&) = delete; + nvmap(const nvmap&) = delete; + nvmap& operator=(const nvmap&) = delete; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) override; diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 7929443d27..5e7b7468f4 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -101,9 +101,7 @@ Module::Module(Core::System& system) }; } -Module::~Module() { - Devices::nvhost_nvdec_common::Reset(); -} +Module::~Module() {} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a2aeb80b49..146d046a93 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -46,7 +46,7 @@ class Module; class EventInterface { public: - EventInterface(Module& module_); + explicit EventInterface(Module& module_); ~EventInterface(); Kernel::KEvent* CreateEvent(std::string name); diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp index b04922ac0e..cdecc3a91f 100644 --- a/src/video_core/control/channel_state.cpp +++ b/src/video_core/control/channel_state.cpp @@ -14,10 +14,7 @@ namespace Tegra::Control { -ChannelState::ChannelState(s32 bind_id_) { - bind_id = bind_id_; - initiated = false; -} +ChannelState::ChannelState(s32 bind_id_) : bind_id{bind_id_}, initialized{} {} void ChannelState::Init(Core::System& system, GPU& gpu) { ASSERT(memory_manager); @@ -27,7 +24,7 @@ void ChannelState::Init(Core::System& system, GPU& gpu) { kepler_compute = std::make_unique(system, *memory_manager); maxwell_dma = std::make_unique(system, *memory_manager); kepler_memory = std::make_unique(system, *memory_manager); - initiated = true; + initialized = true; } void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h index 305b21cbaa..3a7b9872c1 100644 --- a/src/video_core/control/channel_state.h +++ b/src/video_core/control/channel_state.h @@ -34,7 +34,7 @@ class DmaPusher; namespace Control { struct ChannelState { - ChannelState(s32 bind_id); + explicit ChannelState(s32 bind_id); ChannelState(const ChannelState& state) = delete; ChannelState& operator=(const ChannelState&) = delete; ChannelState(ChannelState&& other) noexcept = default; @@ -60,7 +60,7 @@ struct ChannelState { std::unique_ptr dma_pusher; - bool initiated{}; + bool initialized{}; }; } // namespace Control diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 5246192a80..584a0c26cd 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -32,7 +32,7 @@ namespace VideoCommon { class ChannelInfo { public: ChannelInfo() = delete; - ChannelInfo(Tegra::Control::ChannelState& state); + explicit ChannelInfo(Tegra::Control::ChannelState& state); ChannelInfo(const ChannelInfo& state) = delete; ChannelInfo& operator=(const ChannelInfo&) = delete; ChannelInfo(ChannelInfo&& other) = default; diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp index 7330426909..f7cbe204ee 100644 --- a/src/video_core/control/scheduler.cpp +++ b/src/video_core/control/scheduler.cpp @@ -3,6 +3,7 @@ #include +#include "common/assert.h" #include "video_core/control/channel_state.h" #include "video_core/control/scheduler.h" #include "video_core/gpu.h" @@ -13,8 +14,9 @@ Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {} Scheduler::~Scheduler() = default; void Scheduler::Push(s32 channel, CommandList&& entries) { - std::unique_lock lk(scheduling_guard); + std::unique_lock lk(scheduling_guard); auto it = channels.find(channel); + ASSERT(it != channels.end()); auto channel_state = it->second; gpu.BindChannel(channel_state->bind_id); channel_state->dma_pusher->Push(std::move(entries)); @@ -23,7 +25,7 @@ void Scheduler::Push(s32 channel, CommandList&& entries) { void Scheduler::DeclareChannel(std::shared_ptr new_channel) { s32 channel = new_channel->bind_id; - std::unique_lock lk(scheduling_guard); + std::unique_lock lk(scheduling_guard); channels.emplace(channel, new_channel); } diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h index 305a01e0a0..44addf61c4 100644 --- a/src/video_core/control/scheduler.h +++ b/src/video_core/control/scheduler.h @@ -19,7 +19,7 @@ struct ChannelState; class Scheduler { public: - Scheduler(GPU& gpu_); + explicit Scheduler(GPU& gpu_); ~Scheduler(); void Push(s32 channel, CommandList&& entries); diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 9c5d567a67..bc48320ce2 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -195,7 +195,7 @@ public: BitField<24, 2, u32> num_dst_components_minus_one; }; - Swizzle GetComponent(size_t i) { + Swizzle GetComponent(size_t i) const { const u32 raw = dst_components_raw; return static_cast((raw >> (i * 3)) & 0x7); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index d7a3dd96ba..28b38273e8 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -355,7 +355,7 @@ struct GPU::Impl { std::condition_variable sync_cv; - std::list> sync_requests; + std::list> sync_requests; std::atomic current_sync_fence{}; u64 last_sync_fence{}; std::mutex sync_request_mutex; diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h index 7ecf853d95..57082ae54f 100644 --- a/src/video_core/host1x/host1x.h +++ b/src/video_core/host1x/host1x.h @@ -19,7 +19,7 @@ namespace Host1x { class Host1x { public: - Host1x(Core::System& system); + explicit Host1x(Core::System& system); SyncpointManager& GetSyncpointManager() { return syncpoint_manager; diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp index 4471bacae2..326e8355ad 100644 --- a/src/video_core/host1x/syncpoint_manager.cpp +++ b/src/video_core/host1x/syncpoint_manager.cpp @@ -12,13 +12,13 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); SyncpointManager::ActionHandle SyncpointManager::RegisterAction( std::atomic& syncpoint, std::list& action_storage, u32 expected_value, - std::function& action) { + std::function&& action) { if (syncpoint.load(std::memory_order_acquire) >= expected_value) { action(); return {}; } - std::unique_lock lk(guard); + std::unique_lock lk(guard); if (syncpoint.load(std::memory_order_relaxed) >= expected_value) { action(); return {}; @@ -30,12 +30,12 @@ SyncpointManager::ActionHandle SyncpointManager::RegisterAction( } ++it; } - return action_storage.emplace(it, expected_value, action); + return action_storage.emplace(it, expected_value, std::move(action)); } void SyncpointManager::DeregisterAction(std::list& action_storage, ActionHandle& handle) { - std::unique_lock lk(guard); + std::unique_lock lk(guard); action_storage.erase(handle); } @@ -68,7 +68,7 @@ void SyncpointManager::Increment(std::atomic& syncpoint, std::condition_var std::list& action_storage) { auto new_value{syncpoint.fetch_add(1, std::memory_order_acq_rel) + 1}; - std::unique_lock lk(guard); + std::unique_lock lk(guard); auto it = action_storage.begin(); while (it != action_storage.end()) { if (it->expected_value > new_value) { @@ -87,7 +87,7 @@ void SyncpointManager::Wait(std::atomic& syncpoint, std::condition_variable return; } - std::unique_lock lk(guard); + std::unique_lock lk(guard); wait_cv.wait(lk, pred); } diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h index 72220a09a5..50a264e23f 100644 --- a/src/video_core/host1x/syncpoint_manager.h +++ b/src/video_core/host1x/syncpoint_manager.h @@ -18,34 +18,34 @@ namespace Host1x { class SyncpointManager { public: - u32 GetGuestSyncpointValue(u32 id) { + u32 GetGuestSyncpointValue(u32 id) const { return syncpoints_guest[id].load(std::memory_order_acquire); } - u32 GetHostSyncpointValue(u32 id) { + u32 GetHostSyncpointValue(u32 id) const { return syncpoints_host[id].load(std::memory_order_acquire); } struct RegisteredAction { - RegisteredAction(u32 expected_value_, std::function& action_) - : expected_value{expected_value_}, action{action_} {} + explicit RegisteredAction(u32 expected_value_, std::function&& action_) + : expected_value{expected_value_}, action{std::move(action_)} {} u32 expected_value; - std::function action; + std::function action; }; using ActionHandle = std::list::iterator; template ActionHandle RegisterGuestAction(u32 syncpoint_id, u32 expected_value, Func&& action) { - std::function func(action); + std::function func(action); return RegisterAction(syncpoints_guest[syncpoint_id], guest_action_storage[syncpoint_id], - expected_value, func); + expected_value, std::move(func)); } template ActionHandle RegisterHostAction(u32 syncpoint_id, u32 expected_value, Func&& action) { - std::function func(action); + std::function func(action); return RegisterAction(syncpoints_host[syncpoint_id], host_action_storage[syncpoint_id], - expected_value, func); + expected_value, std::move(func)); } void DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle); @@ -60,11 +60,11 @@ public: void WaitHost(u32 syncpoint_id, u32 expected_value); - bool IsReadyGuest(u32 syncpoint_id, u32 expected_value) { + bool IsReadyGuest(u32 syncpoint_id, u32 expected_value) const { return syncpoints_guest[syncpoint_id].load(std::memory_order_acquire) >= expected_value; } - bool IsReadyHost(u32 syncpoint_id, u32 expected_value) { + bool IsReadyHost(u32 syncpoint_id, u32 expected_value) const { return syncpoints_host[syncpoint_id].load(std::memory_order_acquire) >= expected_value; } @@ -74,7 +74,7 @@ private: ActionHandle RegisterAction(std::atomic& syncpoint, std::list& action_storage, u32 expected_value, - std::function& action); + std::function&& action); void DeregisterAction(std::list& action_storage, ActionHandle& handle); diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index ae4fd98dfa..f992e29f3d 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -126,7 +126,7 @@ private: void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); template - [[nodiscard]] inline std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { + [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { if constexpr (is_big_page) { return (gpu_addr >> big_page_bits) & big_page_table_mask; } else { From aedd739631efcebf42a1dae601096c10b99d9cd5 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 3 Sep 2022 21:02:02 -0400 Subject: [PATCH 107/245] maxwell_dma: remove warnings from implemented functionality --- src/video_core/engines/maxwell_dma.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index bcffd1862d..3909d36c19 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -133,7 +133,6 @@ void MaxwellDMA::CopyBlockLinearToPitch() { } // Deswizzle the input and copy it over. - UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); const Parameters& src_params = regs.src_params; const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; @@ -182,7 +181,6 @@ void MaxwellDMA::CopyBlockLinearToPitch() { void MaxwellDMA::CopyPitchToBlockLinear() { UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); UNIMPLEMENTED_IF(regs.dst_params.layer != 0); - UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); const bool is_remapping = regs.launch_dma.remap_enable != 0; const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; From df6dffa30baefd9f1e73399c632ab4e5f6475bab Mon Sep 17 00:00:00 2001 From: Byte Date: Thu, 6 Oct 2022 20:59:40 +0200 Subject: [PATCH 108/245] vulkan_blitter: Fix pool allocation double free. --- .../renderer_vulkan/vk_blit_screen.cpp | 13 ++++++++---- .../renderer_vulkan/vk_blit_screen.h | 2 +- src/video_core/vulkan_common/vulkan_wrapper.h | 20 ------------------- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 444c29f686..cb7fa20784 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -145,6 +145,11 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, // Finish any pending renderpass scheduler.RequestOutsideRenderPassOperationContext(); + if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) { + image_count = swapchain_images; + Recreate(); + } + const std::size_t image_index = swapchain.GetImageIndex(); scheduler.Wait(resource_ticks[image_index]); @@ -448,15 +453,15 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE void BlitScreen::CreateStaticResources() { CreateShaders(); + CreateSampler(); +} + +void BlitScreen::CreateDynamicResources() { CreateSemaphores(); CreateDescriptorPool(); CreateDescriptorSetLayout(); CreateDescriptorSets(); CreatePipelineLayout(); - CreateSampler(); -} - -void BlitScreen::CreateDynamicResources() { CreateRenderPass(); CreateFramebuffers(); CreateGraphicsPipeline(); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index b8c67bef08..29e2ea925e 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -109,7 +109,7 @@ private: MemoryAllocator& memory_allocator; Swapchain& swapchain; Scheduler& scheduler; - const std::size_t image_count; + std::size_t image_count; const ScreenInfo& screen_info; vk::ShaderModule vertex_shader; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 795f16bfb7..1b3f493bde 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -519,9 +519,7 @@ public: dld{rhs.dld} {} /// Assign an allocation transfering ownership from another allocation. - /// Releases any previously held allocation. PoolAllocations& operator=(PoolAllocations&& rhs) noexcept { - Release(); allocations = std::move(rhs.allocations); num = rhs.num; device = rhs.device; @@ -530,11 +528,6 @@ public: return *this; } - /// Destroys any held allocation. - ~PoolAllocations() { - Release(); - } - /// Returns the number of allocations. std::size_t size() const noexcept { return num; @@ -557,19 +550,6 @@ public: } private: - /// Destroys the held allocations if they exist. - void Release() noexcept { - if (!allocations) { - return; - } - const Span span(allocations.get(), num); - const VkResult result = Free(device, pool, span, *dld); - // There's no way to report errors from a destructor. - if (result != VK_SUCCESS) { - std::terminate(); - } - } - std::unique_ptr allocations; std::size_t num = 0; VkDevice device = nullptr; From 20cf09471aed3fd19d40452b050cc7dc841bc243 Mon Sep 17 00:00:00 2001 From: liamwhite Date: Fri, 7 Oct 2022 04:49:08 -0400 Subject: [PATCH 109/245] Revert "vulkan: automatically use larger staging buffer sizes when possible" --- .../vk_staging_buffer_pool.cpp | 84 ++++++------------- .../renderer_vulkan/vk_staging_buffer_pool.h | 3 - 2 files changed, 27 insertions(+), 60 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 7fb2569537..06f68d09af 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -26,39 +26,20 @@ using namespace Common::Literals; constexpr VkDeviceSize MAX_ALIGNMENT = 256; // Maximum size to put elements in the stream buffer constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB; +// Stream buffer size in bytes +constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB; +constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; constexpr VkMemoryPropertyFlags HOST_FLAGS = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS; -static bool IsStreamHeap(VkMemoryHeap heap, size_t staging_buffer_size) noexcept { - return staging_buffer_size < (heap.size * 2) / 3; -} - -static bool HasLargeDeviceLocalHostVisibleMemory(const VkPhysicalDeviceMemoryProperties& props) { - const auto flags{VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT}; - - for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { - const auto& memory_type{props.memoryTypes[type_index]}; - - if ((memory_type.propertyFlags & flags) != flags) { - // Memory must be device local and host visible - continue; - } - - const auto& heap{props.memoryHeaps[memory_type.heapIndex]}; - if (heap.size >= 7168_MiB) { - // This is the right type of memory - return true; - } - } - - return false; +bool IsStreamHeap(VkMemoryHeap heap) noexcept { + return STREAM_BUFFER_SIZE < (heap.size * 2) / 3; } std::optional FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, - VkMemoryPropertyFlags flags, - size_t staging_buffer_size) noexcept { + VkMemoryPropertyFlags flags) noexcept { for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { if (((type_mask >> type_index) & 1) == 0) { // Memory type is incompatible @@ -69,7 +50,7 @@ std::optional FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p // Memory type doesn't have the flags we want continue; } - if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex], staging_buffer_size)) { + if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex])) { // Memory heap is not suitable for streaming continue; } @@ -80,17 +61,17 @@ std::optional FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p } u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, - bool try_device_local, size_t staging_buffer_size) { + bool try_device_local) { std::optional type; if (try_device_local) { // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this - type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS, staging_buffer_size); + type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS); if (type) { return *type; } } // Otherwise try without the DEVICE_LOCAL_BIT - type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS, staging_buffer_size); + type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS); if (type) { return *type; } @@ -98,32 +79,20 @@ u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_ throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); } -size_t Region(size_t iterator, size_t region_size) noexcept { - return iterator / region_size; +size_t Region(size_t iterator) noexcept { + return iterator / REGION_SIZE; } } // Anonymous namespace StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_) : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { - - const auto memory_properties{device.GetPhysical().GetMemoryProperties().memoryProperties}; - if (HasLargeDeviceLocalHostVisibleMemory(memory_properties)) { - // Possible on many integrated and newer discrete cards - staging_buffer_size = 1_GiB; - } else { - // Well-supported default size used by most Vulkan PC games - staging_buffer_size = 256_MiB; - } - - region_size = staging_buffer_size / StagingBufferPool::NUM_SYNCS; - const vk::Device& dev = device.GetLogical(); stream_buffer = dev.CreateBuffer(VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .size = staging_buffer_size, + .size = STREAM_BUFFER_SIZE, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, @@ -148,18 +117,19 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem .image = nullptr, .buffer = *stream_buffer, }; + const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties; VkMemoryAllocateInfo stream_memory_info{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = make_dedicated ? &dedicated_info : nullptr, .allocationSize = requirements.size, - .memoryTypeIndex = FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true, - staging_buffer_size), + .memoryTypeIndex = + FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true), }; stream_memory = dev.TryAllocateMemory(stream_memory_info); if (!stream_memory) { LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory"); - stream_memory_info.memoryTypeIndex = FindMemoryTypeIndex( - memory_properties, requirements.memoryTypeBits, false, staging_buffer_size); + stream_memory_info.memoryTypeIndex = + FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false); stream_memory = dev.AllocateMemory(stream_memory_info); } @@ -167,7 +137,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem stream_memory.SetObjectNameEXT("Stream Buffer Memory"); } stream_buffer.BindMemory(*stream_memory, 0); - stream_pointer = stream_memory.Map(0, staging_buffer_size); + stream_pointer = stream_memory.Map(0, STREAM_BUFFER_SIZE); } StagingBufferPool::~StagingBufferPool() = default; @@ -188,25 +158,25 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - if (AreRegionsActive(Region(free_iterator, region_size) + 1, - std::min(Region(iterator + size, region_size) + 1, NUM_SYNCS))) { + if (AreRegionsActive(Region(free_iterator) + 1, + std::min(Region(iterator + size) + 1, NUM_SYNCS))) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } const u64 current_tick = scheduler.CurrentTick(); - std::fill(sync_ticks.begin() + Region(used_iterator, region_size), - sync_ticks.begin() + Region(iterator, region_size), current_tick); + std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), + current_tick); used_iterator = iterator; free_iterator = std::max(free_iterator, iterator + size); - if (iterator + size >= staging_buffer_size) { - std::fill(sync_ticks.begin() + Region(used_iterator, region_size), - sync_ticks.begin() + NUM_SYNCS, current_tick); + if (iterator + size >= STREAM_BUFFER_SIZE) { + std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, + current_tick); used_iterator = 0; iterator = 0; free_iterator = size; - if (AreRegionsActive(0, Region(size, region_size) + 1)) { + if (AreRegionsActive(0, Region(size) + 1)) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 90c67177f6..91dc84da80 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -93,9 +93,6 @@ private: size_t free_iterator = 0; std::array sync_ticks{}; - size_t staging_buffer_size = 0; - size_t region_size = 0; - StagingBuffersCache device_local_cache; StagingBuffersCache upload_cache; StagingBuffersCache download_cache; From 9574429c5f726794ac96a61326f36fdcd61a2232 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 7 Oct 2022 06:52:28 -0400 Subject: [PATCH 110/245] nfp_types: silence -Wtype-limits --- src/core/hle/service/nfp/nfp_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 867ea2f36e..c09f9ddb6a 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -167,7 +167,7 @@ struct AmiiboDate { bool IsValidDate() const { const bool is_day_valid = GetDay() > 0 && GetDay() < 32; - const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13; + const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13; const bool is_year_valid = GetYear() >= 2000; return is_year_valid && is_month_valid && is_day_valid; } From 752659aef3f16111981a097989dd7a5ddecff316 Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Fri, 12 Aug 2022 10:58:09 +0100 Subject: [PATCH 111/245] Update 3D regs --- src/video_core/buffer_cache/buffer_cache.h | 34 +- src/video_core/dirty_flags.cpp | 24 +- src/video_core/engines/maxwell_3d.cpp | 334 +- src/video_core/engines/maxwell_3d.h | 3965 ++++++++++++----- src/video_core/gpu.h | 50 +- src/video_core/macro/macro_hle.cpp | 48 +- src/video_core/query_cache.h | 2 +- .../renderer_opengl/gl_graphics_pipeline.cpp | 29 +- .../renderer_opengl/gl_graphics_pipeline.h | 4 +- .../renderer_opengl/gl_rasterizer.cpp | 140 +- .../renderer_opengl/gl_shader_cache.cpp | 43 +- .../renderer_opengl/gl_state_tracker.cpp | 57 +- .../renderer_opengl/maxwell_to_gl.h | 287 +- .../renderer_vulkan/fixed_pipeline_state.cpp | 299 +- .../renderer_vulkan/fixed_pipeline_state.h | 15 +- .../renderer_vulkan/maxwell_to_vk.cpp | 337 +- .../renderer_vulkan/maxwell_to_vk.h | 2 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 14 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 67 +- .../renderer_vulkan/vk_rasterizer.cpp | 104 +- .../renderer_vulkan/vk_state_tracker.cpp | 58 +- src/video_core/shader_cache.cpp | 12 +- src/video_core/shader_environment.cpp | 23 +- src/video_core/shader_environment.h | 2 +- src/video_core/surface.cpp | 14 +- src/video_core/texture_cache/image_info.cpp | 37 +- src/video_core/texture_cache/texture_cache.h | 19 +- src/video_core/transform_feedback.cpp | 118 +- src/video_core/transform_feedback.h | 3 +- 29 files changed, 4036 insertions(+), 2105 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 8e26b3f953..2ba33543cb 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -994,13 +994,13 @@ void BufferCache

::BindHostIndexBuffer() { const u32 size = index_buffer.size; SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const u32 new_offset = offset + maxwell3d->regs.index_array.first * - maxwell3d->regs.index_array.FormatSizeInBytes(); + const u32 new_offset = offset + maxwell3d->regs.index_buffer.first * + maxwell3d->regs.index_buffer.FormatSizeInBytes(); runtime.BindIndexBuffer(buffer, new_offset, size); } else { - runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_array.format, - maxwell3d->regs.index_array.first, - maxwell3d->regs.index_array.count, buffer, offset, size); + runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format, + maxwell3d->regs.index_buffer.first, + maxwell3d->regs.index_buffer.count, buffer, offset, size); } } @@ -1017,7 +1017,7 @@ void BufferCache

::BindHostVertexBuffers() { } flags[Dirty::VertexBuffer0 + index] = false; - const u32 stride = maxwell3d->regs.vertex_array[index].stride; + const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 offset = buffer.Offset(binding.cpu_addr); runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride); } @@ -1157,7 +1157,7 @@ void BufferCache

::BindHostGraphicsTextureBuffers(size_t stage) { template void BufferCache

::BindHostTransformFeedbackBuffers() { - if (maxwell3d->regs.tfb_enabled == 0) { + if (maxwell3d->regs.transform_feedback_enabled == 0) { return; } for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { @@ -1268,7 +1268,7 @@ template void BufferCache

::UpdateIndexBuffer() { // We have to check for the dirty flags and index count // The index count is currently changed without updating the dirty flags - const auto& index_array = maxwell3d->regs.index_array; + const auto& index_array = maxwell3d->regs.index_buffer; auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { return; @@ -1311,10 +1311,10 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) { return; } - const auto& array = maxwell3d->regs.vertex_array[index]; - const auto& limit = maxwell3d->regs.vertex_array_limit[index]; - const GPUVAddr gpu_addr_begin = array.StartAddress(); - const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; + const auto& array = maxwell3d->regs.vertex_streams[index]; + const auto& limit = maxwell3d->regs.vertex_stream_limits[index]; + const GPUVAddr gpu_addr_begin = array.Address(); + const GPUVAddr gpu_addr_end = limit.Address() + 1; const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); u32 address_size = static_cast( std::min(gpu_addr_end - gpu_addr_begin, static_cast(std::numeric_limits::max()))); @@ -1380,7 +1380,7 @@ void BufferCache

::UpdateTextureBuffers(size_t stage) { template void BufferCache

::UpdateTransformFeedbackBuffers() { - if (maxwell3d->regs.tfb_enabled == 0) { + if (maxwell3d->regs.transform_feedback_enabled == 0) { return; } for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { @@ -1390,11 +1390,11 @@ void BufferCache

::UpdateTransformFeedbackBuffers() { template void BufferCache

::UpdateTransformFeedbackBuffer(u32 index) { - const auto& binding = maxwell3d->regs.tfb_bindings[index]; - const GPUVAddr gpu_addr = binding.Address() + binding.buffer_offset; - const u32 size = binding.buffer_size; + const auto& binding = maxwell3d->regs.transform_feedback.buffers[index]; + const GPUVAddr gpu_addr = binding.Address() + binding.start_offset; + const u32 size = binding.size; const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); - if (binding.buffer_enable == 0 || size == 0 || !cpu_addr) { + if (binding.enable == 0 || size == 0 || !cpu_addr) { transform_feedback_buffers[index] = NULL_BINDING; return; } diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp index 9dc4341f0e..1039e036fe 100644 --- a/src/video_core/dirty_flags.cpp +++ b/src/video_core/dirty_flags.cpp @@ -17,21 +17,23 @@ using Tegra::Engines::Maxwell3D; void SetupDirtyVertexBuffers(Maxwell3D::DirtyState::Tables& tables) { static constexpr std::size_t num_array = 3; for (std::size_t i = 0; i < Maxwell3D::Regs::NumVertexArrays; ++i) { - const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]); - const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]); + const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]); + const std::size_t limit_offset = + OFF(vertex_stream_limits) + i * NUM(vertex_stream_limits[0]); FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers); - FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers); + FillBlock(tables, limit_offset, NUM(vertex_stream_limits), VertexBuffer0 + i, + VertexBuffers); } } void SetupIndexBuffer(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(index_array), NUM(index_array), IndexBuffer); + FillBlock(tables[0], OFF(index_buffer), NUM(index_buffer), IndexBuffer); } void SetupDirtyDescriptors(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(tic), NUM(tic), Descriptors); - FillBlock(tables[0], OFF(tsc), NUM(tsc), Descriptors); + FillBlock(tables[0], OFF(tex_header), NUM(tex_header), Descriptors); + FillBlock(tables[0], OFF(tex_sampler), NUM(tex_sampler), Descriptors); } void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { @@ -42,7 +44,7 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt); } FillBlock(tables[1], begin, num, RenderTargets); - FillBlock(tables[0], OFF(render_area), NUM(render_area), RenderTargets); + FillBlock(tables[0], OFF(surface_clip), NUM(surface_clip), RenderTargets); tables[0][OFF(rt_control)] = RenderTargets; tables[1][OFF(rt_control)] = RenderTargetControl; @@ -52,15 +54,15 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { const u8 flag = zeta_flags[i]; auto& table = tables[i]; table[OFF(zeta_enable)] = flag; - table[OFF(zeta_width)] = flag; - table[OFF(zeta_height)] = flag; + table[OFF(zeta_size.width)] = flag; + table[OFF(zeta_size.height)] = flag; FillBlock(table, OFF(zeta), NUM(zeta), flag); } } void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(shader_config[0]), - NUM(shader_config[0]) * Maxwell3D::Regs::MaxShaderProgram, Shaders); + FillBlock(tables[0], OFF(pipelines), NUM(pipelines) * Maxwell3D::Regs::MaxShaderProgram, + Shaders); } } // Anonymous namespace diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 3c6e44a252..84c1abf3d0 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -56,37 +56,37 @@ void Maxwell3D::InitializeRegisterDefaults() { // Doom and Bomberman seems to use the uninitialized registers and just enable blend // so initialize blend registers with sane values - regs.blend.equation_rgb = Regs::Blend::Equation::Add; - regs.blend.factor_source_rgb = Regs::Blend::Factor::One; - regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero; - regs.blend.equation_a = Regs::Blend::Equation::Add; - regs.blend.factor_source_a = Regs::Blend::Factor::One; - regs.blend.factor_dest_a = Regs::Blend::Factor::Zero; - for (auto& blend : regs.independent_blend) { - blend.equation_rgb = Regs::Blend::Equation::Add; - blend.factor_source_rgb = Regs::Blend::Factor::One; - blend.factor_dest_rgb = Regs::Blend::Factor::Zero; - blend.equation_a = Regs::Blend::Equation::Add; - blend.factor_source_a = Regs::Blend::Factor::One; - blend.factor_dest_a = Regs::Blend::Factor::Zero; + regs.blend.color_op = Regs::Blend::Equation::Add_D3D; + regs.blend.color_source = Regs::Blend::Factor::One_D3D; + regs.blend.color_dest = Regs::Blend::Factor::Zero_D3D; + regs.blend.alpha_op = Regs::Blend::Equation::Add_D3D; + regs.blend.alpha_source = Regs::Blend::Factor::One_D3D; + regs.blend.alpha_dest = Regs::Blend::Factor::Zero_D3D; + for (auto& blend : regs.blend_per_target) { + blend.color_op = Regs::Blend::Equation::Add_D3D; + blend.color_source = Regs::Blend::Factor::One_D3D; + blend.color_dest = Regs::Blend::Factor::Zero_D3D; + blend.alpha_op = Regs::Blend::Equation::Add_D3D; + blend.alpha_source = Regs::Blend::Factor::One_D3D; + blend.alpha_dest = Regs::Blend::Factor::Zero_D3D; } - regs.stencil_front_op_fail = Regs::StencilOp::Keep; - regs.stencil_front_op_zfail = Regs::StencilOp::Keep; - regs.stencil_front_op_zpass = Regs::StencilOp::Keep; - regs.stencil_front_func_func = Regs::ComparisonOp::Always; - regs.stencil_front_func_mask = 0xFFFFFFFF; - regs.stencil_front_mask = 0xFFFFFFFF; + regs.stencil_front_op.fail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_front_op.zfail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_front_op.zpass = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_front_op.func = Regs::ComparisonOp::Always_GL; + regs.stencil_front_func.func_mask = 0xFFFFFFFF; + regs.stencil_front_func.mask = 0xFFFFFFFF; regs.stencil_two_side_enable = 1; - regs.stencil_back_op_fail = Regs::StencilOp::Keep; - regs.stencil_back_op_zfail = Regs::StencilOp::Keep; - regs.stencil_back_op_zpass = Regs::StencilOp::Keep; - regs.stencil_back_func_func = Regs::ComparisonOp::Always; - regs.stencil_back_func_mask = 0xFFFFFFFF; - regs.stencil_back_mask = 0xFFFFFFFF; + regs.stencil_back_op.fail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_back_op.zfail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_back_op.zpass = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_back_op.func = Regs::ComparisonOp::Always_GL; + regs.stencil_back_func.func_mask = 0xFFFFFFFF; + regs.stencil_back_func.mask = 0xFFFFFFFF; - regs.depth_test_func = Regs::ComparisonOp::Always; - regs.front_face = Regs::FrontFace::CounterClockWise; - regs.cull_face = Regs::CullFace::Back; + regs.depth_test_func = Regs::ComparisonOp::Always_GL; + regs.gl_front_face = Regs::FrontFace::CounterClockWise; + regs.gl_cull_face = Regs::CullFace::Back; // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a // register carrying a default value. Assume it's OpenGL's default (1). @@ -107,20 +107,20 @@ void Maxwell3D::InitializeRegisterDefaults() { // NVN games expect these values to be enabled at boot regs.rasterize_enable = 1; - regs.rt_separate_frag_data = 1; + regs.color_target_mrt_enable = 1; regs.framebuffer_srgb = 1; regs.line_width_aliased = 1.0f; regs.line_width_smooth = 1.0f; - regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise; + regs.gl_front_face = Maxwell3D::Regs::FrontFace::ClockWise; regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill; regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill; shadow_state = regs; - mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true; - mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true; + 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_array.count)] = true; + mme_inline[MAXWELL3D_REG_INDEX(index_buffer.count)] = true; } void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { @@ -173,51 +173,56 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume case MAXWELL3D_REG_INDEX(shadow_ram_control): shadow_state.shadow_ram_control = static_cast(nonshadow_argument); return; - case MAXWELL3D_REG_INDEX(macros.upload_address): - return macro_engine->ClearCode(regs.macros.upload_address); - case MAXWELL3D_REG_INDEX(macros.data): - return macro_engine->AddCode(regs.macros.upload_address, argument); - case MAXWELL3D_REG_INDEX(macros.bind): + case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr): + return macro_engine->ClearCode(regs.load_mme.instruction_ptr); + case MAXWELL3D_REG_INDEX(load_mme.instruction): + return macro_engine->AddCode(regs.load_mme.instruction_ptr, argument); + case MAXWELL3D_REG_INDEX(load_mme.start_address): return ProcessMacroBind(argument); - case MAXWELL3D_REG_INDEX(firmware[4]): + case MAXWELL3D_REG_INDEX(falcon[4]): return ProcessFirmwareCall4(); - case MAXWELL3D_REG_INDEX(const_buffer.cb_data): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: + case MAXWELL3D_REG_INDEX(const_buffer.buffer): + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: return ProcessCBData(argument); - case MAXWELL3D_REG_INDEX(cb_bind[0]): + case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config): return ProcessCBBind(0); - case MAXWELL3D_REG_INDEX(cb_bind[1]): + case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config): return ProcessCBBind(1); - case MAXWELL3D_REG_INDEX(cb_bind[2]): + case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config): return ProcessCBBind(2); - case MAXWELL3D_REG_INDEX(cb_bind[3]): + case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config): return ProcessCBBind(3); - case MAXWELL3D_REG_INDEX(cb_bind[4]): + case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): return ProcessCBBind(4); - case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): + case MAXWELL3D_REG_INDEX(draw.end): return DrawArrays(); - case MAXWELL3D_REG_INDEX(small_index): - regs.index_array.count = regs.small_index.count; - regs.index_array.first = regs.small_index.first; + 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(small_index_2): - regs.index_array.count = regs.small_index_2.count; - regs.index_array.first = regs.small_index_2.first; + 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. @@ -225,28 +230,24 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume case MAXWELL3D_REG_INDEX(topology_override): use_topology_override = true; return; - case MAXWELL3D_REG_INDEX(clear_buffers): + case MAXWELL3D_REG_INDEX(clear_surface): return ProcessClearBuffers(); - case MAXWELL3D_REG_INDEX(query.query_get): + case MAXWELL3D_REG_INDEX(report_semaphore.query): return ProcessQueryGet(); - case MAXWELL3D_REG_INDEX(condition.mode): + case MAXWELL3D_REG_INDEX(render_enable.mode): return ProcessQueryCondition(); - case MAXWELL3D_REG_INDEX(counter_reset): + case MAXWELL3D_REG_INDEX(clear_report_value): return ProcessCounterReset(); case MAXWELL3D_REG_INDEX(sync_info): return ProcessSyncPoint(); - case MAXWELL3D_REG_INDEX(exec_upload): - return upload_state.ProcessExec(regs.exec_upload.linear != 0); - case MAXWELL3D_REG_INDEX(data_upload): + case MAXWELL3D_REG_INDEX(launch_dma): + return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() == + Regs::LaunchDMA::Layout::Pitch); + case MAXWELL3D_REG_INDEX(inline_data): upload_state.ProcessData(argument, is_last_call); return; case MAXWELL3D_REG_INDEX(fragment_barrier): return rasterizer->FragmentBarrier(); - case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache): - rasterizer->InvalidateGPUCache(); - return rasterizer->WaitForIdle(); - case MAXWELL3D_REG_INDEX(tiled_cache_barrier): - return rasterizer->TiledCacheBarrier(); } } @@ -296,25 +297,25 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, return; } switch (method) { - case MAXWELL3D_REG_INDEX(const_buffer.cb_data): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: + case MAXWELL3D_REG_INDEX(const_buffer.buffer): + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: ProcessCBMultiData(base_start, amount); break; - case MAXWELL3D_REG_INDEX(data_upload): + case MAXWELL3D_REG_INDEX(inline_data): upload_state.ProcessData(base_start, static_cast(amount)); return; default: @@ -353,14 +354,15 @@ 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_array.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.vertex_begin_gl)) { + } else if (method == MAXWELL3D_REG_INDEX(draw.begin)) { mme_draw.instance_mode = - (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0); + (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++; @@ -405,11 +407,12 @@ 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_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); + 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_next || !regs.draw.instance_cont, + 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(); @@ -424,7 +427,7 @@ void Maxwell3D::FlushMMEInlineDraw() { // 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_array.count = 0; + regs.index_buffer.count = 0; } else { regs.vertex_buffer.count = 0; } @@ -437,11 +440,11 @@ void Maxwell3D::FlushMMEInlineDraw() { } void Maxwell3D::ProcessMacroUpload(u32 data) { - macro_engine->AddCode(regs.macros.upload_address++, data); + macro_engine->AddCode(regs.load_mme.instruction_ptr++, data); } void Maxwell3D::ProcessMacroBind(u32 data) { - macro_positions[regs.macros.entry++] = data; + macro_positions[regs.load_mme.start_address_ptr++] = data; } void Maxwell3D::ProcessFirmwareCall4() { @@ -449,11 +452,11 @@ void Maxwell3D::ProcessFirmwareCall4() { // Firmware call 4 is a blob that changes some registers depending on its parameters. // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. - regs.reg_array[0xd00] = 1; + regs.shadow_scratch[0] = 1; } void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { - const GPUVAddr sequence_address{regs.query.QueryAddress()}; + const GPUVAddr sequence_address{regs.report_semaphore.Address()}; if (long_query) { memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); memory_manager.Write(sequence_address, payload); @@ -464,15 +467,17 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { void Maxwell3D::ProcessQueryGet() { // TODO(Subv): Support the other query units. - if (regs.query.query_get.unit != Regs::QueryUnit::Crop) { - LOG_DEBUG(HW_GPU, "Units other than CROP are unimplemented"); + if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) { + LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented"); } - switch (regs.query.query_get.operation) { - case Regs::QueryOperation::Release: - if (regs.query.query_get.fence == 1 || regs.query.query_get.short_query != 0) { - const GPUVAddr sequence_address{regs.query.QueryAddress()}; - const u32 payload = regs.query.query_sequence; + switch (regs.report_semaphore.query.operation) { + case Regs::ReportSemaphore::Operation::Release: + if (regs.report_semaphore.query.release == + Regs::ReportSemaphore::Release::AfterAllPreceedingWrites || + regs.report_semaphore.query.short_query != 0) { + const GPUVAddr sequence_address{regs.report_semaphore.Address()}; + const u32 payload = regs.report_semaphore.payload; std::function operation([this, sequence_address, payload] { memory_manager.Write(sequence_address, payload); }); @@ -482,8 +487,8 @@ void Maxwell3D::ProcessQueryGet() { u64_le value; u64_le timestamp; }; - const GPUVAddr sequence_address{regs.query.QueryAddress()}; - const u32 payload = regs.query.query_sequence; + const GPUVAddr sequence_address{regs.report_semaphore.Address()}; + const u32 payload = regs.report_semaphore.payload; std::function operation([this, sequence_address, payload] { memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); memory_manager.Write(sequence_address, payload); @@ -491,19 +496,19 @@ void Maxwell3D::ProcessQueryGet() { rasterizer->SyncOperation(std::move(operation)); } break; - case Regs::QueryOperation::Acquire: + case Regs::ReportSemaphore::Operation::Acquire: // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that // matches the current payload. UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE"); break; - case Regs::QueryOperation::Counter: + case Regs::ReportSemaphore::Operation::ReportOnly: if (const std::optional result = GetQueryResult()) { // If the query returns an empty optional it means it's cached and deferred. // In this case we have a non-empty result, so we stamp it immediately. - StampQueryResult(*result, regs.query.query_get.short_query == 0); + StampQueryResult(*result, regs.report_semaphore.query.short_query == 0); } break; - case Regs::QueryOperation::Trap: + case Regs::ReportSemaphore::Operation::Trap: UNIMPLEMENTED_MSG("Unimplemented query operation TRAP"); break; default: @@ -513,31 +518,31 @@ void Maxwell3D::ProcessQueryGet() { } void Maxwell3D::ProcessQueryCondition() { - const GPUVAddr condition_address{regs.condition.Address()}; - switch (regs.condition.mode) { - case Regs::ConditionMode::Always: { + const GPUVAddr condition_address{regs.render_enable.Address()}; + switch (regs.render_enable.mode) { + case Regs::RenderEnable::Mode::True: { execute_on = true; break; } - case Regs::ConditionMode::Never: { + case Regs::RenderEnable::Mode::False: { execute_on = false; break; } - case Regs::ConditionMode::ResNonZero: { - Regs::QueryCompare cmp; + case Regs::RenderEnable::Mode::Conditional: { + Regs::ReportSemaphore::Compare cmp; memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; break; } - case Regs::ConditionMode::Equal: { - Regs::QueryCompare cmp; + case Regs::RenderEnable::Mode::IfEqual: { + Regs::ReportSemaphore::Compare cmp; memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); execute_on = cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; break; } - case Regs::ConditionMode::NotEqual: { - Regs::QueryCompare cmp; + case Regs::RenderEnable::Mode::IfNotEqual: { + Regs::ReportSemaphore::Compare cmp; memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); execute_on = cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; @@ -552,21 +557,21 @@ void Maxwell3D::ProcessQueryCondition() { } void Maxwell3D::ProcessCounterReset() { - switch (regs.counter_reset) { - case Regs::CounterReset::SampleCnt: + switch (regs.clear_report_value) { + case Regs::ClearReport::ZPassPixelCount: rasterizer->ResetCounter(QueryType::SamplesPassed); break; default: - LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.counter_reset); + LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value); break; } } void Maxwell3D::ProcessSyncPoint() { const u32 sync_point = regs.sync_info.sync_point.Value(); - const u32 increment = regs.sync_info.increment.Value(); - [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); - if (increment) { + const auto condition = regs.sync_info.condition.Value(); + [[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value(); + if (condition == Regs::SyncInfo::Condition::RopWritesDone) { rasterizer->SignalSyncPoint(sync_point); } } @@ -574,23 +579,24 @@ void Maxwell3D::ProcessSyncPoint() { void Maxwell3D::DrawArrays() { LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), regs.vertex_buffer.count); - ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); + 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_next || !regs.draw.instance_cont, + 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_next) { + if (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) { // Increment the current instance *before* drawing. - state.current_instance += 1; - } else if (!regs.draw.instance_cont) { + 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_array.count && !regs.vertex_buffer.count}; + const bool is_indexed{regs.index_buffer.count && !regs.vertex_buffer.count}; if (ShouldExecute()) { rasterizer->Draw(is_indexed, false); } @@ -600,60 +606,60 @@ void Maxwell3D::DrawArrays() { // 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_array.count = 0; + regs.index_buffer.count = 0; } else { regs.vertex_buffer.count = 0; } } std::optional Maxwell3D::GetQueryResult() { - switch (regs.query.query_get.select) { - case Regs::QuerySelect::Payload: - return regs.query.query_sequence; - case Regs::QuerySelect::SamplesPassed: + switch (regs.report_semaphore.query.report) { + case Regs::ReportSemaphore::Report::Payload: + return regs.report_semaphore.payload; + case Regs::ReportSemaphore::Report::ZPassPixelCount64: // Deferred. - rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, + rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed, system.GPU().GetTicks()); return std::nullopt; default: - LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", - regs.query.query_get.select.Value()); + LOG_DEBUG(HW_GPU, "Unimplemented query report type {}", + regs.report_semaphore.query.report.Value()); return 1; } } void Maxwell3D::ProcessCBBind(size_t stage_index) { // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. - const auto& bind_data = regs.cb_bind[stage_index]; - auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.index]; + const auto& bind_data = regs.bind_groups[stage_index]; + auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot]; buffer.enabled = bind_data.valid.Value() != 0; - buffer.address = regs.const_buffer.BufferAddress(); - buffer.size = regs.const_buffer.cb_size; + buffer.address = regs.const_buffer.Address(); + buffer.size = regs.const_buffer.size; const bool is_enabled = bind_data.valid.Value() != 0; if (!is_enabled) { - rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.index); + rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.shader_slot); return; } - const GPUVAddr gpu_addr = regs.const_buffer.BufferAddress(); - const u32 size = regs.const_buffer.cb_size; - rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); + const GPUVAddr gpu_addr = regs.const_buffer.Address(); + const u32 size = regs.const_buffer.size; + rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.shader_slot, gpu_addr, size); } void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) { // Write the input value to the current const buffer at the current position. - const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); + const GPUVAddr buffer_address = regs.const_buffer.Address(); ASSERT(buffer_address != 0); // Don't allow writing past the end of the buffer. - ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); + ASSERT(regs.const_buffer.offset <= regs.const_buffer.size); - const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos}; + const GPUVAddr address{buffer_address + regs.const_buffer.offset}; const size_t copy_size = amount * sizeof(u32); memory_manager.WriteBlock(address, start_base, copy_size); // Increment the current buffer position. - regs.const_buffer.cb_pos += static_cast(copy_size); + regs.const_buffer.offset += static_cast(copy_size); } void Maxwell3D::ProcessCBData(u32 value) { @@ -661,7 +667,8 @@ void Maxwell3D::ProcessCBData(u32 value) { } Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { - const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)}; + const GPUVAddr tic_address_gpu{regs.tex_header.Address() + + tic_index * sizeof(Texture::TICEntry)}; Texture::TICEntry tic_entry; memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); @@ -670,7 +677,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { } Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { - const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)}; + const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() + + tsc_index * sizeof(Texture::TSCEntry)}; Texture::TSCEntry tsc_entry; memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 5f9eb208c3..efe1073b06 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -39,12 +39,15 @@ namespace Tegra::Engines { /** * This Engine is known as GF100_3D. Documentation can be found in: + * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/3d/clb197.h * https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h + * + * Note: nVidia have confirmed that their open docs have had parts redacted, so this list is + * currently incomplete, and the gaps are still worth exploring. */ -#define MAXWELL3D_REG_INDEX(field_name) \ - (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) +#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Maxwell3D::Regs, field_name) / sizeof(u32)) class Maxwell3D final : public EngineInterface { public: @@ -55,7 +58,6 @@ public: void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); /// Register structure of the Maxwell3D engine. - /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. struct Regs { static constexpr std::size_t NUM_REGS = 0xE00; @@ -74,90 +76,515 @@ public: static constexpr std::size_t MaxConstBuffers = 18; static constexpr std::size_t MaxConstBufferSize = 0x10000; - enum class QueryOperation : u32 { - Release = 0, - Acquire = 1, - Counter = 2, - Trap = 3, + struct ID { + union { + BitField<0, 16, u32> cls; + BitField<16, 5, u32> engine; + }; }; - enum class QueryUnit : u32 { - VFetch = 1, - VP = 2, - Rast = 4, - StrmOut = 5, - GP = 6, - ZCull = 7, - Prop = 10, - Crop = 15, + struct LoadMME { + u32 instruction_ptr; + u32 instruction; + u32 start_address_ptr; + u32 start_address; }; - enum class QuerySelect : u32 { - Payload = 0, - TimeElapsed = 2, - TransformFeedbackPrimitivesGenerated = 11, - PrimitivesGenerated = 18, - SamplesPassed = 21, - TransformFeedbackUnknown = 26, + struct Notify { + u32 address_high; + u32 address_low; + u32 type; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } }; - struct QueryCompare { - u32 initial_sequence; - u32 initial_mode; - u32 unknown1; - u32 unknown2; - u32 current_sequence; - u32 current_mode; + struct PeerSemaphore { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } }; - enum class QuerySyncCondition : u32 { - NotEqual = 0, - GreaterThan = 1, + struct GlobalRender { + enum class Mode : u32 { + False = 0, + True = 1, + Conditional = 2, + IfEqual = 3, + IfNotEqual = 4, + }; + u32 offset_high; + u32 offset_low; + Mode mode; + + GPUVAddr Address() const { + return static_cast((static_cast(offset_high) << 32) | + offset_low); + } }; - enum class ConditionMode : u32 { - Never = 0, - Always = 1, - ResNonZero = 2, - Equal = 3, - NotEqual = 4, + enum class ReductionOp : u32 { + RedAdd = 0, + RedMin = 1, + RedMax = 2, + RedInc = 3, + RedDec = 4, + RedAnd = 5, + RedOr = 6, + RedXor = 7, }; - enum class ShaderProgram : u32 { - VertexA = 0, - VertexB = 1, - TesselationControl = 2, - TesselationEval = 3, - Geometry = 4, - Fragment = 5, + struct LaunchDMA { + enum class Layout : u32 { + Blocklinear = 0, + Pitch = 1, + }; + + enum class CompletionType : u32 { + FlushDisable = 0, + FlushOnly = 1, + Release = 2, + }; + + union { + BitField<0, 1, Layout> memory_layout; + BitField<4, 2, CompletionType> completion_type; + BitField<8, 2, u32> interrupt_type; + BitField<12, 1, u32> sem_size; + BitField<1, 1, u32> reduction_enable; + BitField<13, 3, ReductionOp> reduction_op; + BitField<2, 2, u32> reduction_format; + BitField<6, 1, u32> sysmembar_disable; + }; + }; + + struct I2M { + u32 address_high; + u32 address_low; + u32 payload; + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 nop0; + u32 nop1; + u32 nop2; + u32 nop3; + }; + + struct OpportunisticEarlyZ { + BitField<0, 5, u32> threshold; + + u32 Threshold() const { + switch (threshold) { + case 0x0: + return 0; + case 0x1F: + return 0x1F; + default: + // Thresholds begin at 0x10 (1 << 4) + // Threshold is in the range 0x1 to 0x13 + return 1 << (4 + threshold.Value() - 1); + } + } + }; + + struct GeometryShaderDmFifo { + union { + BitField<0, 13, u32> raster_on; + BitField<16, 13, u32> raster_off; + BitField<31, 1, u32> spill_enabled; + }; + }; + + struct L2CacheControl { + enum class EvictPolicy : u32 { + First = 0, + Normal = 1, + Last = 2, + }; + + union { + BitField<4, 2, EvictPolicy> policy; + }; + }; + + struct InvalidateShaderCache { + union { + BitField<0, 1, u32> instruction; + BitField<4, 1, u32> data; + BitField<12, 1, u32> constant; + BitField<1, 1, u32> locks; + BitField<2, 1, u32> flush_data; + }; + }; + + struct SyncInfo { + enum class Condition : u32 { + StreamOutWritesDone = 0, + RopWritesDone = 1, + }; + + union { + BitField<0, 16, u32> sync_point; + BitField<16, 1, u32> clean_l2; + BitField<20, 1, Condition> condition; + }; + }; + + struct SurfaceClipBlockId { + union { + BitField<0, 4, u32> block_width; + BitField<4, 4, u32> block_height; + BitField<8, 4, u32> block_depth; + }; + }; + + struct DecompressSurface { + union { + BitField<0, 3, u32> mrt_select; + BitField<4, 16, u32> rt_array_index; + }; + }; + + struct ZCullRopBypass { + union { + BitField<0, 1, u32> enable; + BitField<4, 1, u32> no_stall; + BitField<8, 1, u32> cull_everything; + BitField<12, 4, u32> threshold; + }; + }; + + struct ZCullSubregion { + union { + BitField<0, 1, u32> enable; + BitField<4, 24, u32> normalized_aliquots; + }; + }; + + struct RasterBoundingBox { + enum class Mode : u32 { + BoundingBox = 0, + FullViewport = 1, + }; + + union { + BitField<0, 1, Mode> mode; + BitField<4, 8, u32> pad; + }; + }; + + struct IteratedBlendOptimization { + enum class Noop : u32 { + Never = 0, + SourceRGBA0000 = 1, + SourceAlpha = 2, + SourceRGBA0001 = 3, + }; + + union { + BitField<0, 1, Noop> noop; + }; + }; + + struct ZCullSubregionAllocation { + enum class Format : u32 { + Z_16x16x2_4x4 = 0, + ZS_16x16_4x4 = 1, + Z_16x16_4x2 = 2, + Z_16x16_2x4 = 3, + Z_16x8_4x4 = 4, + Z_8x8_4x2 = 5, + Z_8x8_2x4 = 6, + Z_16x16_4x8 = 7, + Z_4x8_2x2 = 8, + ZS_16x8_4x2 = 9, + ZS_16x8_2x4 = 10, + ZS_8x8_2x2 = 11, + Z_4x8_1x1 = 12, + None = 15, + }; + + union { + BitField<0, 8, u32> id; + BitField<8, 16, u32> aliquots; + BitField<24, 4, Format> format; + }; + }; + + enum class ZCullSubregionAlgorithm : u32 { + Static = 0, + Adaptive = 1, + }; + + struct PixelShaderOutputSampleMaskUsage { + union { + BitField<0, 1, u32> enable; + BitField<1, 1, u32> qualify_by_aa; + }; + }; + + struct L1Configuration { + enum class AddressableMemory : u32 { + Size16Kb = 0, + Size48Kb = 3, + }; + union { + BitField<0, 3, AddressableMemory> direct_addressable_memory; + }; + }; + + struct SPAVersion { + union { + BitField<0, 8, u32> minor; + BitField<8, 8, u32> major; + }; + }; + + struct SnapGrid { + enum class Location : u32 { + Pixel2x2 = 1, + Pixel4x4 = 2, + Pixel8x8 = 3, + Pixel16x16 = 4, + Pixel32x32 = 5, + Pixel64x64 = 6, + Pixel128x128 = 7, + Pixel256x256 = 8, + }; + + enum class Mode : u32 { + RTNE = 0, + Tesla = 1, + }; + + struct { + union { + BitField<0, 4, Location> location; + BitField<8, 1, Mode> rounding_mode; + }; + } line; + + struct { + union { + BitField<0, 4, Location> location; + BitField<8, 1, Mode> rounding_mode; + }; + } non_line; + }; + + struct Tessellation { + enum class DomainType : u32 { + Isolines = 0, + Triangles = 1, + Quads = 2, + }; + + enum class Spacing : u32 { + Integer = 0, + FractionalOdd = 1, + FractionalEven = 2, + }; + + enum class OutputPrimitves : u32 { + Points = 0, + Lines = 1, + Triangles_CW = 2, + Triangles_CCW = 3, + }; + + struct Parameters { + union { + BitField<0, 2, DomainType> domain_type; + BitField<4, 2, Spacing> spacing; + BitField<8, 2, OutputPrimitves> output_primitives; + }; + } params; + + struct LOD { + std::array outer; + std::array inner; + } lod; + + std::array reserved; + }; + + struct SubTilingPerf { + struct { + union { + BitField<0, 8, u32> spm_triangle_register_file_per; + BitField<8, 8, u32> spm_pixel_output_buffer_per; + BitField<16, 8, u32> spm_triangle_ram_per; + BitField<24, 8, u32> max_quads_per; + }; + } knob_a; + + struct { + union { + BitField<0, 8, u32> max_primitives_per; + }; + } knob_b; + + u32 knob_c; + }; + + struct ZCullSubregionReport { + enum class ReportType : u32 { + DepthTest = 0, + DepthTestNoAccept = 1, + DepthTestLateZ = 2, + StencilTest = 3, + }; + + union { + BitField<0, 1, u32> enabled; + BitField<4, 8, u32> subregion_id; + } to_report; + + union { + BitField<0, 1, u32> enabled; + BitField<4, 3, ReportType> type; + } report_type; + }; + + struct BalancedPrimitiveWorkload { + union { + BitField<0, 1, u32> unpartitioned_mode; + BitField<4, 1, u32> timesliced_mode; + }; + }; + + struct TransformFeedback { + struct Buffer { + u32 enable; + u32 address_high; + u32 address_low; + s32 size; + s32 start_offset; + INSERT_PADDING_BYTES_NOINIT(0xC); + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; + static_assert(sizeof(Buffer) == 0x20); + + struct Control { + u32 stream; + u32 varying_count; + u32 stride; + INSERT_PADDING_BYTES_NOINIT(0x4); + }; + static_assert(sizeof(Control) == 0x10); + + std::array buffers; + + INSERT_PADDING_BYTES_NOINIT(0x300); + + std::array controls; + }; + + struct HybridAntiAliasControl { + enum class Centroid : u32 { + PerFragment = 0, + PerPass = 1, + }; + union { + BitField<0, 4, u32> passes; + BitField<4, 1, Centroid> centroid; + BitField<5, 1, u32> passes_extended; + }; + }; + + struct ShaderLocalMemory { + u32 base_address; + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 address_high; + u32 address_low; + u32 size_high; + u32 size_low; + u32 default_size_per_warp; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + + u64 Size() const { + return (static_cast(size_high) << 32) | size_low; + } + }; + + struct ZCullRegion { + u32 width; + u32 height; + u32 depth; + u32 offset; + INSERT_PADDING_BYTES_NOINIT(0xC); + u32 fetch_streams_once; + union { + BitField<0, 16, u32> start_aliquot; + BitField<16, 16, u32> aliquot_count; + } location; + u32 aliquots_per_layer; + u32 storage_address_high; + u32 storage_address_low; + u32 storage_limit_address_high; + u32 storage_limit_address_low; + + GPUVAddr StorageAddress() const { + return static_cast((static_cast(storage_address_high) << 32) | + storage_address_low); + } + GPUVAddr StorageLimitAddress() const { + return static_cast( + (static_cast(storage_limit_address_high) << 32) | + storage_limit_address_low); + } + }; + + struct ZetaReadOnly { + union { + BitField<0, 1, u32> enable_z; + BitField<4, 1, u32> enable_stencil; + }; }; struct VertexAttribute { enum class Size : u32 { Invalid = 0x0, - Size_32_32_32_32 = 0x01, - Size_32_32_32 = 0x02, - Size_16_16_16_16 = 0x03, - Size_32_32 = 0x04, - Size_16_16_16 = 0x05, - Size_8_8_8_8 = 0x0a, - Size_16_16 = 0x0f, - Size_32 = 0x12, - Size_8_8_8 = 0x13, - Size_8_8 = 0x18, - Size_16 = 0x1b, - Size_8 = 0x1d, - Size_10_10_10_2 = 0x30, - Size_11_11_10 = 0x31, + Size_R32_G32_B32_A32 = 0x01, + Size_R32_G32_B32 = 0x02, + Size_R16_G16_B16_A16 = 0x03, + Size_R32_G32 = 0x04, + Size_R16_G16_B16 = 0x05, + Size_R8_G8_B8_A8 = 0x0A, + Size_R16_G16 = 0x0F, + Size_R32 = 0x12, + Size_R8_G8_B8 = 0x13, + Size_R8_G8 = 0x18, + Size_R16 = 0x1B, + Size_R8 = 0x1D, + Size_A2_B10_G10_R10 = 0x30, + Size_B10_G11_R11 = 0x31, + Size_G8_R8 = 0x32, + Size_X8_B8_G8_R8 = 0x33, + Size_A8 = 0x34, }; enum class Type : u32 { - SignedNorm = 1, - UnsignedNorm = 2, - SignedInt = 3, - UnsignedInt = 4, - UnsignedScaled = 5, - SignedScaled = 6, + UnusedEnumDoNotUseBecauseItWillGoAway = 0, + SNorm = 1, + UNorm = 2, + SInt = 3, + UInt = 4, + UScaled = 5, + SScaled = 6, Float = 7, }; @@ -173,33 +600,36 @@ public: u32 ComponentCount() const { switch (size) { - case Size::Size_32_32_32_32: + case Size::Size_R32_G32_B32_A32: return 4; - case Size::Size_32_32_32: + case Size::Size_R32_G32_B32: return 3; - case Size::Size_16_16_16_16: + case Size::Size_R16_G16_B16_A16: return 4; - case Size::Size_32_32: + case Size::Size_R32_G32: return 2; - case Size::Size_16_16_16: + case Size::Size_R16_G16_B16: return 3; - case Size::Size_8_8_8_8: + case Size::Size_R8_G8_B8_A8: + case Size::Size_X8_B8_G8_R8: return 4; - case Size::Size_16_16: + case Size::Size_R16_G16: return 2; - case Size::Size_32: + case Size::Size_R32: return 1; - case Size::Size_8_8_8: + case Size::Size_R8_G8_B8: return 3; - case Size::Size_8_8: + case Size::Size_R8_G8: + case Size::Size_G8_R8: return 2; - case Size::Size_16: + case Size::Size_R16: return 1; - case Size::Size_8: + case Size::Size_R8: + case Size::Size_A8: return 1; - case Size::Size_10_10_10_2: + case Size::Size_A2_B10_G10_R10: return 4; - case Size::Size_11_11_10: + case Size::Size_B10_G11_R11: return 3; default: ASSERT(false); @@ -209,33 +639,36 @@ public: u32 SizeInBytes() const { switch (size) { - case Size::Size_32_32_32_32: + case Size::Size_R32_G32_B32_A32: return 16; - case Size::Size_32_32_32: + case Size::Size_R32_G32_B32: return 12; - case Size::Size_16_16_16_16: + case Size::Size_R16_G16_B16_A16: return 8; - case Size::Size_32_32: + case Size::Size_R32_G32: return 8; - case Size::Size_16_16_16: + case Size::Size_R16_G16_B16: return 6; - case Size::Size_8_8_8_8: + case Size::Size_R8_G8_B8_A8: + case Size::Size_X8_B8_G8_R8: return 4; - case Size::Size_16_16: + case Size::Size_R16_G16: return 4; - case Size::Size_32: + case Size::Size_R32: return 4; - case Size::Size_8_8_8: + case Size::Size_R8_G8_B8: return 3; - case Size::Size_8_8: + case Size::Size_R8_G8: + case Size::Size_G8_R8: return 2; - case Size::Size_16: + case Size::Size_R16: return 2; - case Size::Size_8: + case Size::Size_R8: + case Size::Size_A8: return 1; - case Size::Size_10_10_10_2: + case Size::Size_A2_B10_G10_R10: return 4; - case Size::Size_11_11_10: + case Size::Size_B10_G11_R11: return 4; default: ASSERT(false); @@ -245,34 +678,36 @@ public: std::string SizeString() const { switch (size) { - case Size::Size_32_32_32_32: + case Size::Size_R32_G32_B32_A32: return "32_32_32_32"; - case Size::Size_32_32_32: + case Size::Size_R32_G32_B32: return "32_32_32"; - case Size::Size_16_16_16_16: + case Size::Size_R16_G16_B16_A16: return "16_16_16_16"; - case Size::Size_32_32: + case Size::Size_R32_G32: return "32_32"; - case Size::Size_16_16_16: + case Size::Size_R16_G16_B16: return "16_16_16"; - case Size::Size_8_8_8_8: + case Size::Size_R8_G8_B8_A8: return "8_8_8_8"; - case Size::Size_16_16: + case Size::Size_R16_G16: return "16_16"; - case Size::Size_32: + case Size::Size_R32: return "32"; - case Size::Size_8_8_8: + case Size::Size_R8_G8_B8: return "8_8_8"; - case Size::Size_8_8: + case Size::Size_R8_G8: + case Size::Size_G8_R8: return "8_8"; - case Size::Size_16: + case Size::Size_R16: return "16"; - case Size::Size_8: + case Size::Size_R8: + case Size::Size_A8: return "8"; - case Size::Size_10_10_10_2: - return "10_10_10_2"; - case Size::Size_11_11_10: - return "11_11_10"; + case Size::Size_A2_B10_G10_R10: + return "2_10_10_10"; + case Size::Size_B10_G11_R11: + return "10_11_12"; default: ASSERT(false); return {}; @@ -281,17 +716,19 @@ public: std::string TypeString() const { switch (type) { - case Type::SignedNorm: + case Type::UnusedEnumDoNotUseBecauseItWillGoAway: + return "Unused"; + case Type::SNorm: return "SNORM"; - case Type::UnsignedNorm: + case Type::UNorm: return "UNORM"; - case Type::SignedInt: + case Type::SInt: return "SINT"; - case Type::UnsignedInt: + case Type::UInt: return "UINT"; - case Type::UnsignedScaled: + case Type::UScaled: return "USCALED"; - case Type::SignedScaled: + case Type::SScaled: return "SSCALED"; case Type::Float: return "FLOAT"; @@ -301,7 +738,7 @@ public: } bool IsNormalized() const { - return (type == Type::SignedNorm) || (type == Type::UnsignedNorm); + return (type == Type::SNorm) || (type == Type::UNorm); } bool IsValid() const { @@ -312,6 +749,7 @@ public: return hex < other.hex; } }; + static_assert(sizeof(VertexAttribute) == 0x4); struct MsaaSampleLocation { union { @@ -342,9 +780,96 @@ public: } }; - enum class DepthMode : u32 { - MinusOneToOne = 0, - ZeroToOne = 1, + struct MultisampleCoverageToColor { + union { + BitField<0, 1, u32> enable; + BitField<4, 3, u32> target; + }; + }; + + struct DecompressZetaSurface { + union { + BitField<0, 1, u32> z_enable; + BitField<4, 1, u32> stencil_enable; + }; + }; + + struct ZetaSparse { + enum class UnmappedCompare : u32 { + Unmapped = 0, + FailAlways = 1, + }; + union { + BitField<0, 1, u32> enable; + BitField<1, 1, UnmappedCompare> unmapped_compare; + }; + }; + + struct RtControl { + union { + BitField<0, 4, u32> count; + BitField<4, 3, u32> target0; + BitField<7, 3, u32> target1; + BitField<10, 3, u32> target2; + BitField<13, 3, u32> target3; + BitField<16, 3, u32> target4; + BitField<19, 3, u32> target5; + BitField<22, 3, u32> target6; + BitField<25, 3, u32> target7; + }; + + u32 Map(std::size_t index) const { + const std::array maps{target0, target1, target2, target3, + target4, target5, target6, target7}; + ASSERT(index < maps.size()); + return maps[index]; + } + }; + + struct CompressionThresholdSamples { + u32 samples; + + u32 Samples() { + if (samples == 0) { + return 0; + } + return 1 << (samples - 1); + } + }; + + struct PixelShaderInterlockControl { + enum class TileMode : u32 { + NoConflictDetect = 0, + DetectSampleConflict = 1, + DetectPixelConflict = 2, + }; + enum class TileSize : u32 { + Size_16x16 = 0, + Size_8x8 = 1, + }; + enum class FragmentOrder : u32 { + FragmentOrdered = 0, + FragmentUnordered = 1, + }; + union { + BitField<0, 2, TileMode> tile_mode; + BitField<2, 1, TileSize> tile_size; + BitField<3, 1, FragmentOrder> fragment_order; + }; + }; + + struct ZetaSize { + enum class DimensionControl : u32 { + DepthDefinesArray = 0, + ArraySizeOne = 1, + }; + + u32 width; + u32 height; + union { + BitField<0, 16, u32> depth; + BitField<16, 1, DimensionControl> dim_control; + }; }; enum class PrimitiveTopology : u32 { @@ -358,15 +883,21 @@ public: Quads = 0x7, QuadStrip = 0x8, Polygon = 0x9, - LinesAdjacency = 0xa, - LineStripAdjacency = 0xb, - TrianglesAdjacency = 0xc, - TriangleStripAdjacency = 0xd, - Patches = 0xe, + LinesAdjacency = 0xA, + LineStripAdjacency = 0xB, + TrianglesAdjacency = 0xC, + TriangleStripAdjacency = 0xD, + Patches = 0xE, + }; + + struct VertexArray { + union { + BitField<0, 16, u32> start; + BitField<16, 12, u32> count; + BitField<28, 3, PrimitiveTopology> topology; + }; }; - // Constants as from NVC0_3D_UNK1970_D3D - // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h#L1598 enum class PrimitiveTopologyOverride : u32 { None = 0x0, Points = 0x1, @@ -374,11 +905,32 @@ public: LineStrip = 0x3, Triangles = 0x4, TriangleStrip = 0x5, - LinesAdjacency = 0xa, - LineStripAdjacency = 0xb, - TrianglesAdjacency = 0xc, - TriangleStripAdjacency = 0xd, - Patches = 0xe, + LinesAdjacency = 0xA, + LineStripAdjacency = 0xB, + TrianglesAdjacency = 0xC, + TriangleStripAdjacency = 0xD, + Patches = 0xE, + + LegacyPoints = 0x1001, + LegacyIndexedLines = 0x1002, + LegacyIndexedTriangles = 0x1003, + LegacyLines = 0x100F, + LegacyLineStrip = 0x1010, + LegacyIndexedLineStrip = 0x1011, + LegacyTriangles = 0x1012, + LegacyTriangleStrip = 0x1013, + LegacyIndexedTriangleStrip = 0x1014, + LegacyTriangleFan = 0x1015, + LegacyIndexedTriangleFan = 0x1016, + LegacyTriangleFanImm = 0x1017, + LegacyLinesImm = 0x1018, + LegacyIndexedTriangles2 = 0x101A, + LegacyIndexedLines2 = 0x101B, + }; + + enum class DepthMode : u32 { + MinusOneToOne = 0, + ZeroToOne = 1, }; enum class IndexFormat : u32 { @@ -388,183 +940,143 @@ public: }; enum class ComparisonOp : u32 { - // These values are used by Nouveau and most games, they correspond to the OpenGL token - // values for these operations. - Never = 0x200, - Less = 0x201, - Equal = 0x202, - LessEqual = 0x203, - Greater = 0x204, - NotEqual = 0x205, - GreaterEqual = 0x206, - Always = 0x207, + Never_D3D = 1, + Less_D3D = 2, + Equal_D3D = 3, + LessEqual_D3D = 4, + Greater_D3D = 5, + NotEqual_D3D = 6, + GreaterEqual_D3D = 7, + Always_D3D = 8, - // These values are used by some games, they seem to be NV04 values. - NeverOld = 1, - LessOld = 2, - EqualOld = 3, - LessEqualOld = 4, - GreaterOld = 5, - NotEqualOld = 6, - GreaterEqualOld = 7, - AlwaysOld = 8, + Never_GL = 0x200, + Less_GL = 0x201, + Equal_GL = 0x202, + LessEqual_GL = 0x203, + Greater_GL = 0x204, + NotEqual_GL = 0x205, + GreaterEqual_GL = 0x206, + Always_GL = 0x207, }; - enum class LogicOperation : u32 { - Clear = 0x1500, - And = 0x1501, - AndReverse = 0x1502, - Copy = 0x1503, - AndInverted = 0x1504, - NoOp = 0x1505, - Xor = 0x1506, - Or = 0x1507, - Nor = 0x1508, - Equiv = 0x1509, - Invert = 0x150A, - OrReverse = 0x150B, - CopyInverted = 0x150C, - OrInverted = 0x150D, - Nand = 0x150E, - Set = 0x150F, - }; - - enum class StencilOp : u32 { - Keep = 1, - Zero = 2, - Replace = 3, - Incr = 4, - Decr = 5, - Invert = 6, - IncrWrap = 7, - DecrWrap = 8, - KeepOGL = 0x1E00, - ZeroOGL = 0, - ReplaceOGL = 0x1E01, - IncrOGL = 0x1E02, - DecrOGL = 0x1E03, - InvertOGL = 0x150A, - IncrWrapOGL = 0x8507, - DecrWrapOGL = 0x8508, - }; - - enum class CounterReset : u32 { - SampleCnt = 0x01, - Unk02 = 0x02, - Unk03 = 0x03, - Unk04 = 0x04, - EmittedPrimitives = 0x10, // Not tested - Unk11 = 0x11, - Unk12 = 0x12, - Unk13 = 0x13, - Unk15 = 0x15, - Unk16 = 0x16, - Unk17 = 0x17, - Unk18 = 0x18, - Unk1A = 0x1A, - Unk1B = 0x1B, - Unk1C = 0x1C, - Unk1D = 0x1D, - Unk1E = 0x1E, - GeneratedPrimitives = 0x1F, + enum class ClearReport : u32 { + ZPassPixelCount = 0x01, + ZCullStats = 0x02, + StreamingPrimitvesNeededMinusSucceeded = 0x03, + AlphaBetaClocks = 0x04, + StreamingPrimitivesSucceeded = 0x10, + StreamingPrimitivesNeeded = 0x11, + VerticesGenerated = 0x12, + PrimitivesGenerated = 0x13, + VertexShaderInvocations = 0x15, + TessellationInitInvocations = 0x16, + TessellationShaderInvocations = 0x17, + TessellationShaderPrimitivesGenerated = 0x18, + GeometryShaderInvocations = 0x1A, + GeometryShaderPrimitivesGenerated = 0x1B, + ClipperInvocations = 0x1C, + ClipperPrimitivesGenerated = 0x1D, + PixelShaderInvocations = 0x1E, + VtgPrimitivesOut = 0x1F, }; enum class FrontFace : u32 { - ClockWise = 0x0900, - CounterClockWise = 0x0901, + ClockWise = 0x900, + CounterClockWise = 0x901, }; enum class CullFace : u32 { - Front = 0x0404, - Back = 0x0405, - FrontAndBack = 0x0408, + Front = 0x404, + Back = 0x405, + FrontAndBack = 0x408, }; struct Blend { enum class Equation : u32 { - Add = 1, - Subtract = 2, - ReverseSubtract = 3, - Min = 4, - Max = 5, + Add_D3D = 1, + Subtract_D3D = 2, + ReverseSubtract_D3D = 3, + Min_D3D = 4, + Max_D3D = 5, - // These values are used by Nouveau and some games. - AddGL = 0x8006, - MinGL = 0x8007, - MaxGL = 0x8008, - SubtractGL = 0x800a, - ReverseSubtractGL = 0x800b + Add_GL = 0x8006, + Min_GL = 0x8007, + Max_GL = 0x8008, + Subtract_GL = 0x800A, + ReverseSubtract_GL = 0x800B }; enum class Factor : u32 { - Zero = 0x1, - One = 0x2, - SourceColor = 0x3, - OneMinusSourceColor = 0x4, - SourceAlpha = 0x5, - OneMinusSourceAlpha = 0x6, - DestAlpha = 0x7, - OneMinusDestAlpha = 0x8, - DestColor = 0x9, - OneMinusDestColor = 0xa, - SourceAlphaSaturate = 0xb, - Source1Color = 0x10, - OneMinusSource1Color = 0x11, - Source1Alpha = 0x12, - OneMinusSource1Alpha = 0x13, - ConstantColor = 0x61, - OneMinusConstantColor = 0x62, - ConstantAlpha = 0x63, - OneMinusConstantAlpha = 0x64, + Zero_D3D = 0x1, + One_D3D = 0x2, + SourceColor_D3D = 0x3, + OneMinusSourceColor_D3D = 0x4, + SourceAlpha_D3D = 0x5, + OneMinusSourceAlpha_D3D = 0x6, + DestAlpha_D3D = 0x7, + OneMinusDestAlpha_D3D = 0x8, + DestColor_D3D = 0x9, + OneMinusDestColor_D3D = 0xA, + SourceAlphaSaturate_D3D = 0xB, + BothSourceAlpha_D3D = 0xC, + OneMinusBothSourceAlpha_D3D = 0xD, + BlendFactor_D3D = 0xE, + OneMinusBlendFactor_D3D = 0xF, + Source1Color_D3D = 0x10, + OneMinusSource1Color_D3D = 0x11, + Source1Alpha_D3D = 0x12, + OneMinusSource1Alpha_D3D = 0x13, - // These values are used by Nouveau and some games. - ZeroGL = 0x4000, - OneGL = 0x4001, - SourceColorGL = 0x4300, - OneMinusSourceColorGL = 0x4301, - SourceAlphaGL = 0x4302, - OneMinusSourceAlphaGL = 0x4303, - DestAlphaGL = 0x4304, - OneMinusDestAlphaGL = 0x4305, - DestColorGL = 0x4306, - OneMinusDestColorGL = 0x4307, - SourceAlphaSaturateGL = 0x4308, - ConstantColorGL = 0xc001, - OneMinusConstantColorGL = 0xc002, - ConstantAlphaGL = 0xc003, - OneMinusConstantAlphaGL = 0xc004, - Source1ColorGL = 0xc900, - OneMinusSource1ColorGL = 0xc901, - Source1AlphaGL = 0xc902, - OneMinusSource1AlphaGL = 0xc903, + Zero_GL = 0x4000, + One_GL = 0x4001, + SourceColor_GL = 0x4300, + OneMinusSourceColor_GL = 0x4301, + SourceAlpha_GL = 0x4302, + OneMinusSourceAlpha_GL = 0x4303, + DestAlpha_GL = 0x4304, + OneMinusDestAlpha_GL = 0x4305, + DestColor_GL = 0x4306, + OneMinusDestColor_GL = 0x4307, + SourceAlphaSaturate_GL = 0x4308, + ConstantColor_GL = 0xC001, + OneMinusConstantColor_GL = 0xC002, + ConstantAlpha_GL = 0xC003, + OneMinusConstantAlpha_GL = 0xC004, + Source1Color_GL = 0xC900, + OneMinusSource1Color_GL = 0xC901, + Source1Alpha_GL = 0xC902, + OneMinusSource1Alpha_GL = 0xC903, }; u32 separate_alpha; - Equation equation_rgb; - Factor factor_source_rgb; - Factor factor_dest_rgb; - Equation equation_a; - Factor factor_source_a; - Factor factor_dest_a; - INSERT_PADDING_WORDS_NOINIT(1); + Equation color_op; + Factor color_source; + Factor color_dest; + Equation alpha_op; + Factor alpha_source; + u32 enable_global_color_key; + Factor alpha_dest; + + u32 single_rop_control_enable; + u32 enable[NumRenderTargets]; }; - enum class TessellationPrimitive : u32 { - Isolines = 0, - Triangles = 1, - Quads = 2, - }; - - enum class TessellationSpacing : u32 { - Equal = 0, - FractionalOdd = 1, - FractionalEven = 2, + struct BlendPerTarget { + u32 separate_alpha; + Blend::Equation color_op; + Blend::Factor color_source; + Blend::Factor color_dest; + Blend::Equation alpha_op; + Blend::Factor alpha_source; + Blend::Factor alpha_dest; + INSERT_PADDING_BYTES_NOINIT(0x4); }; + static_assert(sizeof(BlendPerTarget) == 0x20); enum class PolygonMode : u32 { - Point = 0x1b00, - Line = 0x1b01, - Fill = 0x1b02, + Point = 0x1B00, + Line = 0x1B01, + Fill = 0x1B02, }; enum class ShadowRamControl : u32 { @@ -589,18 +1101,22 @@ public: NegativeW = 7, }; - enum class SamplerIndex : u32 { + enum class SamplerBinding : u32 { Independently = 0, - ViaHeaderIndex = 1, + ViaHeaderBinding = 1, }; struct TileMode { + enum class DimensionControl : u32 { + DepthDefinesArray = 0, + DepthDefinesDepth = 1, + }; union { BitField<0, 4, u32> block_width; BitField<4, 4, u32> block_height; BitField<8, 4, u32> block_depth; BitField<12, 1, u32> is_pitch_linear; - BitField<16, 1, u32> is_3d; + BitField<16, 1, DimensionControl> dim_control; }; }; static_assert(sizeof(TileMode) == 4); @@ -616,23 +1132,25 @@ public: BitField<0, 16, u32> depth; BitField<16, 1, u32> volume; }; - u32 layer_stride; + u32 array_pitch; u32 base_layer; - INSERT_PADDING_WORDS_NOINIT(7); + u32 mark_ieee_clean; + INSERT_PADDING_BYTES_NOINIT(0x18); GPUVAddr Address() const { return static_cast((static_cast(address_high) << 32) | address_low); } }; + static_assert(sizeof(RenderTargetConfig) == 0x40); struct ColorMask { union { u32 raw; - BitField<0, 4, u32> R; - BitField<4, 4, u32> G; - BitField<8, 4, u32> B; - BitField<12, 4, u32> A; + BitField<0, 1, u32> R; + BitField<4, 1, u32> G; + BitField<8, 1, u32> B; + BitField<12, 1, u32> A; }; }; @@ -643,6 +1161,7 @@ public: f32 translate_x; f32 translate_y; f32 translate_z; + union { u32 raw; BitField<0, 3, ViewportSwizzle> x; @@ -650,7 +1169,11 @@ public: BitField<8, 3, ViewportSwizzle> z; BitField<12, 3, ViewportSwizzle> w; } swizzle; - INSERT_PADDING_WORDS_NOINIT(1); + + union { + BitField<0, 5, u32> x; + BitField<8, 5, u32> y; + } snap_grid_precision; Common::Rectangle GetRect() const { return { @@ -677,21 +1200,14 @@ public: return translate_y + std::fabs(scale_y) - GetY(); } }; + static_assert(sizeof(ViewportTransform) == 0x20); - struct ScissorTest { - u32 enable; - union { - BitField<0, 16, u32> min_x; - BitField<16, 16, u32> max_x; + struct Viewport { + enum class PixelCenter : u32 { + HalfIntegers = 0, + Integers = 1, }; - union { - BitField<0, 16, u32> min_y; - BitField<16, 16, u32> max_y; - }; - u32 fill; - }; - struct ViewPort { union { BitField<0, 16, u32> x; BitField<16, 16, u32> width; @@ -703,726 +1219,1822 @@ public: float depth_range_near; float depth_range_far; }; + static_assert(sizeof(Viewport) == 0x10); - struct TransformFeedbackBinding { - u32 buffer_enable; + struct Window { + union { + BitField<0, 16, u32> x_min; + BitField<16, 16, u32> x_max; + }; + union { + BitField<0, 16, u32> y_min; + BitField<16, 16, u32> y_max; + }; + }; + static_assert(sizeof(Window) == 0x8); + + struct ClipIdExtent { + union { + BitField<0, 16, u32> x; + BitField<16, 16, u32> width; + }; + union { + BitField<0, 16, u32> y; + BitField<16, 16, u32> height; + }; + }; + static_assert(sizeof(ClipIdExtent) == 0x8); + + enum class VisibleCallLimit : u32 { + Limit0 = 0, + Limit1 = 1, + Limit2 = 2, + Limit4 = 3, + Limit8 = 4, + Limit16 = 5, + Limit32 = 6, + Limit64 = 7, + Limit128 = 8, + None = 15, + }; + + struct StatisticsCounter { + union { + BitField<0, 1, u32> da_vertices; + BitField<1, 1, u32> da_primitives; + BitField<2, 1, u32> vs_invocations; + BitField<3, 1, u32> gs_invocations; + BitField<4, 1, u32> gs_primitives; + BitField<5, 1, u32> streaming_primitives_succeeded; + BitField<6, 1, u32> streaming_primitives_needed; + BitField<7, 1, u32> clipper_invocations; + BitField<8, 1, u32> clipper_primitives; + BitField<9, 1, u32> ps_invocations; + BitField<11, 1, u32> ti_invocations; + BitField<12, 1, u32> ts_invocations; + BitField<13, 1, u32> ts_primitives; + BitField<14, 1, u32> total_streaming_primitives_needed_succeeded; + BitField<10, 1, u32> vtg_primitives_out; + BitField<15, 1, u32> alpha_beta_clocks; + }; + }; + + struct ClearRect { + union { + BitField<0, 16, u32> x_min; + BitField<16, 16, u32> x_max; + }; + union { + BitField<0, 16, u32> y_min; + BitField<16, 16, u32> y_max; + }; + }; + + struct VertexBuffer { + u32 first; + u32 count; + }; + + struct InvalidateShaderCacheNoWFI { + union { + BitField<0, 1, u32> instruction; + BitField<4, 1, u32> global_data; + BitField<12, 1, u32> constant; + }; + }; + + struct ZCullSerialization { + enum class Applied : u32 { + Always = 0, + LateZ = 1, + OutOfGamutZ = 2, + LateZOrOutOfGamutZ = 3, + }; + union { + BitField<0, 1, u32> enable; + BitField<4, 2, Applied> applied; + }; + }; + + struct ZCullDirFormat { + enum class Zdir : u32 { + Less = 0, + Greater = 1, + }; + enum class Zformat : u32 { + MSB = 0, + FP = 1, + Ztrick = 2, + Zf32 = 3, + }; + + union { + BitField<0, 16, Zdir> dir; + BitField<16, 16, Zformat> format; + }; + }; + + struct IteratedBlend { + union { + BitField<0, 1, u32> enable; + BitField<1, 1, u32> enable_alpha; + }; + u32 pass_count; + }; + + struct ZCullCriterion { + enum class Sfunc : u32 { + Never = 0, + Less = 1, + Equal = 2, + LessOrEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterOrEqual = 6, + Always = 7, + }; + + union { + BitField<0, 8, Sfunc> sfunc; + BitField<8, 1, u32> no_invalidate; + BitField<9, 1, u32> force_match; + BitField<16, 8, u32> sref; + BitField<24, 8, u32> smask; + }; + }; + + struct LoadIteratedBlend { + enum class Test : u32 { + False = 0, + True = 1, + Equal = 2, + NotEqual = 3, + LessThan = 4, + LessOrEqual = 5, + Greater = 6, + GreaterOrEqual = 7, + }; + enum class Operation : u32 { + AddProducts = 0, + SubProducts = 1, + Min = 2, + Max = 3, + Reciprocal = 4, + Add = 5, + Sub = 6, + }; + enum class OperandA : u32 { + SrcRGB = 0, + DstRGB = 1, + SrcAAA = 2, + DstAAA = 3, + Temp0_RGB = 4, + Temp1_RGB = 5, + Temp2_RGB = 6, + PBR_RGB = 7, + }; + enum class OperandB : u32 { + Zero = 0, + One = 1, + SrcRGB = 2, + SrcAAA = 3, + OneMinusSrcAAA = 4, + DstRGB = 5, + DstAAA = 6, + OneMinusDstAAA = 7, + Temp0_RGB = 9, + Temp1_RGB = 10, + Temp2_RGB = 11, + PBR_RGB = 12, + ConstRGB = 13, + ZeroATimesB = 14, + }; + enum class Swizzle : u32 { + RGB = 0, + GBR = 1, + RRR = 2, + GGG = 3, + BBB = 4, + RToA = 5, + }; + enum class WriteMask : u32 { + RGB = 0, + ROnly = 1, + GOnly = 2, + BOnly = 3, + }; + enum class Pass : u32 { + Temp0 = 0, + Temp1 = 1, + Temp2 = 2, + None = 3, + }; + + u32 instruction_ptr; + union { + BitField<0, 3, Test> test; + BitField<3, 3, Operation> operation; + BitField<6, 3, u32> const_input; + BitField<9, 3, OperandA> operand_a; + BitField<12, 4, OperandB> operand_b; + BitField<16, 3, OperandA> operand_c; + BitField<19, 4, OperandB> operand_d; + BitField<23, 3, Swizzle> output_swizzle; + BitField<26, 2, WriteMask> output_mask; + BitField<28, 2, Pass> output_pass; + BitField<31, 1, u32> test_enabled; + }; + }; + + struct ScissorTest { + u32 enable; + union { + BitField<0, 16, u32> min_x; + BitField<16, 16, u32> max_x; + }; + union { + BitField<0, 16, u32> min_y; + BitField<16, 16, u32> max_y; + }; + INSERT_PADDING_BYTES_NOINIT(0x4); + }; + static_assert(sizeof(ScissorTest) == 0x10); + + struct VPCPerf { + union { + BitField<0, 8, u32> culled_small_lines; + BitField<8, 8, u32> culled_small_triangles; + BitField<16, 8, u32> nonculled_lines_and_points; + BitField<24, 8, u32> nonculled_triangles; + }; + }; + + struct ConstantColorRendering { + u32 enabled; + u32 red; + u32 green; + u32 blue; + u32 alpha; + }; + + struct VertexStreamSubstitute { u32 address_high; u32 address_low; - s32 buffer_size; - s32 buffer_offset; - INSERT_PADDING_WORDS_NOINIT(3); GPUVAddr Address() const { return static_cast((static_cast(address_high) << 32) | address_low); } }; - static_assert(sizeof(TransformFeedbackBinding) == 32); - struct TransformFeedbackLayout { - u32 stream; - u32 varying_count; - u32 stride; - INSERT_PADDING_WORDS_NOINIT(1); + struct VTGWarpWatermarks { + union { + BitField<0, 16, u32> low; + BitField<16, 16, u32> high; + }; }; - static_assert(sizeof(TransformFeedbackLayout) == 16); - bool IsShaderConfigEnabled(std::size_t index) const { - // The VertexB is always enabled. - if (index == static_cast(Regs::ShaderProgram::VertexB)) { - return true; + struct SampleMask { + struct Target { + union { + BitField<0, 1, u32> raster_out; + BitField<4, 1, u32> color_target; + }; + u32 target; + }; + struct Pos { + u32 x0_y0; + u32 x1_y0; + u32 x0_y1; + u32 x1_y1; + }; + }; + + enum class NonMultisampledZ : u32 { + PerSample = 0, + PixelCenter = 1, + }; + + enum class TIRMode : u32 { + Disabled = 0, + RasterNTargetM = 1, + }; + + enum class AntiAliasRaster : u32 { + Mode1x1 = 0, + Mode2x2 = 2, + Mode4x2_D3D = 4, + Mode2x1_D3D = 5, + Mode4x4 = 6, + }; + + struct SurfaceClipIDMemory { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); } - return shader_config[index].enable != 0; - } + }; - bool IsShaderConfigEnabled(Regs::ShaderProgram type) const { - return IsShaderConfigEnabled(static_cast(type)); - } + struct TIRModulation { + enum class Component : u32 { + None = 0, + RGB = 1, + AlphaOnly = 2, + RGBA = 3, + }; + enum class Function : u32 { + Linear = 0, + Table = 1, + }; + Component component; + Function function; + }; - union { - struct { - INSERT_PADDING_WORDS_NOINIT(0x44); + struct Zeta { + u32 address_high; + u32 address_low; + Tegra::DepthFormat format; + TileMode tile_mode; + u32 array_pitch; - u32 wait_for_idle; + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; - struct { - u32 upload_address; - u32 data; - u32 entry; - u32 bind; - } macros; + struct SurfaceClip { + union { + BitField<0, 16, u32> x; + BitField<16, 16, u32> width; + }; + union { + BitField<0, 16, u32> y; + BitField<16, 16, u32> height; + }; + }; - ShadowRamControl shadow_ram_control; + enum class L2CacheControlPolicy : u32 { + First = 0, + Normal = 1, + Last = 2, + }; - INSERT_PADDING_WORDS_NOINIT(0x16); + struct L2CacheVAFRequests { + union { + BitField<0, 1, u32> system_memory_volatile; + BitField<4, 2, L2CacheControlPolicy> policy; + }; + }; - Upload::Registers upload; - struct { - union { - BitField<0, 1, u32> linear; - }; - } exec_upload; + enum class ViewportMulticast : u32 { + ViewportOrder = 0, + PrimitiveOrder = 1, + }; - u32 data_upload; + struct TIRModulationCoeff { + union { + BitField<0, 8, u32> table_v0; + BitField<8, 8, u32> table_v1; + BitField<16, 8, u32> table_v2; + BitField<24, 8, u32> table_v3; + }; + }; + static_assert(sizeof(TIRModulationCoeff) == 0x4); - INSERT_PADDING_WORDS_NOINIT(0x16); + struct ReduceColorThreshold { + union { + BitField<0, 8, u32> all_hit_once; + BitField<16, 8, u32> all_covered; + }; + }; - u32 force_early_fragment_tests; + struct ClearControl { + union { + BitField<0, 1, u32> respect_stencil_mask; + BitField<4, 1, u32> use_clear_rect; + BitField<8, 1, u32> use_scissor; + BitField<12, 1, u32> use_viewport_clip0; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x2D); - - struct { - union { - BitField<0, 16, u32> sync_point; - BitField<16, 1, u32> unknown; - BitField<20, 1, u32> increment; - }; - } sync_info; - - INSERT_PADDING_WORDS_NOINIT(0x15); + struct L2CacheRopNonInterlockedReads { + union { + BitField<4, 2, L2CacheControlPolicy> policy; + }; + }; + struct VertexOutputAttributeSkipMasks { + struct Attributes { union { - BitField<0, 2, TessellationPrimitive> prim; - BitField<4, 2, TessellationSpacing> spacing; - BitField<8, 1, u32> cw; - BitField<9, 1, u32> connected; - } tess_mode; + BitField<0, 1, u32> attribute0_comp0; + BitField<1, 1, u32> attribute0_comp1; + BitField<2, 1, u32> attribute0_comp2; + BitField<3, 1, u32> attribute0_comp3; + BitField<4, 1, u32> attribute1_comp0; + BitField<5, 1, u32> attribute1_comp1; + BitField<6, 1, u32> attribute1_comp2; + BitField<7, 1, u32> attribute1_comp3; + BitField<8, 1, u32> attribute2_comp0; + BitField<9, 1, u32> attribute2_comp1; + BitField<10, 1, u32> attribute2_comp2; + BitField<11, 1, u32> attribute2_comp3; + BitField<12, 1, u32> attribute3_comp0; + BitField<13, 1, u32> attribute3_comp1; + BitField<14, 1, u32> attribute3_comp2; + BitField<15, 1, u32> attribute3_comp3; + BitField<16, 1, u32> attribute4_comp0; + BitField<17, 1, u32> attribute4_comp1; + BitField<18, 1, u32> attribute4_comp2; + BitField<19, 1, u32> attribute4_comp3; + BitField<20, 1, u32> attribute5_comp0; + BitField<21, 1, u32> attribute5_comp1; + BitField<22, 1, u32> attribute5_comp2; + BitField<23, 1, u32> attribute5_comp3; + BitField<24, 1, u32> attribute6_comp0; + BitField<25, 1, u32> attribute6_comp1; + BitField<26, 1, u32> attribute6_comp2; + BitField<27, 1, u32> attribute6_comp3; + BitField<28, 1, u32> attribute7_comp0; + BitField<29, 1, u32> attribute7_comp1; + BitField<30, 1, u32> attribute7_comp2; + BitField<31, 1, u32> attribute7_comp3; + }; + }; - std::array tess_level_outer; - std::array tess_level_inner; + std::array a; + std::array b; + }; - INSERT_PADDING_WORDS_NOINIT(0x10); + struct TIRControl { + union { + BitField<0, 1, u32> z_pass_pixel_count_use_raster_samples; + BitField<4, 1, u32> alpha_coverage_use_raster_samples; + BitField<1, 1, u32> reduce_coverage; + }; + }; - u32 rasterize_enable; + enum class FillViaTriangleMode : u32 { + Disabled = 0, + FillAll = 1, + FillBoundingBox = 2, + }; - std::array tfb_bindings; + struct PsTicketDispenserValue { + union { + BitField<0, 8, u32> index; + BitField<8, 16, u32> value; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0xC0); + struct RegisterWatermarks { + union { + BitField<0, 16, u32> low; + BitField<16, 16, u32> high; + }; + }; - std::array tfb_layouts; + enum class InvalidateCacheLines : u32 { + All = 0, + One = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x1); + struct InvalidateTextureDataCacheNoWfi { + union { + BitField<0, 1, InvalidateCacheLines> lines; + BitField<4, 22, u32> tag; + }; + }; - u32 tfb_enabled; + struct ZCullRegionEnable { + union { + BitField<0, 1, u32> enable_z; + BitField<4, 1, u32> enable_stencil; + BitField<1, 1, u32> rect_clear; + BitField<2, 1, u32> use_rt_array_index; + BitField<5, 16, u32> rt_array_index; + BitField<3, 1, u32> make_conservative; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x2E); + enum class FillMode : u32 { + Point = 1, + Wireframe = 2, + Solid = 3, + }; - std::array rt; + enum class ShadeMode : u32 { + Flat = 0x1, + Gouraud = 0x2, + GL_Flat = 0x1D00, + GL_Smooth = 0x1D01, + }; - std::array viewport_transform; + enum class AlphaToCoverageDither : u32 { + Footprint_1x1 = 0, + Footprint_2x2 = 1, + Footprint_1x1_Virtual = 2, + }; - std::array viewports; + struct InlineIndex4x8Align { + union { + BitField<0, 30, u32> count; + BitField<30, 2, u32> start; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x1D); + struct InlineIndex4x8Index { + union { + BitField<0, 8, u32> index0; + BitField<8, 8, u32> index1; + BitField<16, 8, u32> index2; + BitField<24, 8, u32> index3; + }; + }; - struct { - u32 first; - u32 count; - } vertex_buffer; + enum class D3DCullMode : u32 { + None = 0, + CW = 1, + CCW = 2, + }; - DepthMode depth_mode; + struct BlendColor { + f32 r; + f32 g; + f32 b; + f32 a; + }; - float clear_color[4]; - float clear_depth; + struct StencilOp { + enum class Op : u32 { + Keep_D3D = 1, + Zero_D3D = 2, + Replace_D3D = 3, + IncrSaturate_D3D = 4, + DecrSaturate_D3D = 5, + Invert_D3D = 6, + Incr_D3D = 7, + Decr_D3D = 8, - INSERT_PADDING_WORDS_NOINIT(0x3); + Keep_GL = 0x1E00, + Zero_GL = 0, + Replace_GL = 0x1E01, + IncrSaturate_GL = 0x1E02, + DecrSaturate_GL = 0x1E03, + Invert_GL = 0x150A, + Incr_GL = 0x8507, + Decr_GL = 0x8508, + }; - s32 clear_stencil; + Op fail; + Op zfail; + Op zpass; + ComparisonOp func; + }; - INSERT_PADDING_WORDS_NOINIT(0x2); + struct StencilFunc { + s32 ref; + u32 func_mask; + u32 mask; + }; - PolygonMode polygon_mode_front; - PolygonMode polygon_mode_back; + struct PsSaturate { + // Opposite of DepthMode + enum class Depth : u32 { + ZeroToOne = 0, + MinusOneToOne = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x3); + union { + BitField<0, 1, u32> output0_enable; + BitField<1, 1, Depth> output0_range; + BitField<4, 1, u32> output1_enable; + BitField<5, 1, Depth> output1_range; + BitField<8, 1, u32> output2_enable; + BitField<9, 1, Depth> output2_range; + BitField<12, 1, u32> output3_enable; + BitField<13, 1, Depth> output3_range; + BitField<16, 1, u32> output4_enable; + BitField<17, 1, Depth> output4_range; + BitField<20, 1, u32> output5_enable; + BitField<21, 1, Depth> output5_range; + BitField<24, 1, u32> output6_enable; + BitField<25, 1, Depth> output6_range; + BitField<28, 1, u32> output7_enable; + BitField<29, 1, Depth> output7_range; + }; - u32 polygon_offset_point_enable; - u32 polygon_offset_line_enable; - u32 polygon_offset_fill_enable; + bool AnyEnabled() const { + return output0_enable || output1_enable || output2_enable || output3_enable || + output4_enable || output5_enable || output6_enable || output7_enable; + } + }; - u32 patch_vertices; + struct WindowOrigin { + enum class Mode : u32 { + UpperLeft = 0, + LowerLeft = 1, + }; + union { + BitField<0, 1, Mode> mode; + BitField<4, 1, u32> flip_y; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x4); - - u32 fragment_barrier; - - INSERT_PADDING_WORDS_NOINIT(0x7); - - std::array scissor_test; - - INSERT_PADDING_WORDS_NOINIT(0x15); - - s32 stencil_back_func_ref; - u32 stencil_back_mask; - u32 stencil_back_func_mask; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 invalidate_texture_data_cache; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 tiled_cache_barrier; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - u32 color_mask_common; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - f32 depth_bounds[2]; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 rt_separate_frag_data; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 multisample_raster_enable; - u32 multisample_raster_samples; - std::array multisample_sample_mask; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - struct { - u32 address_high; - u32 address_low; - Tegra::DepthFormat format; - TileMode tile_mode; - u32 layer_stride; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } zeta; - - struct { - union { - BitField<0, 16, u32> x; - BitField<16, 16, u32> width; - }; - union { - BitField<0, 16, u32> y; - BitField<16, 16, u32> height; - }; - } render_area; - - INSERT_PADDING_WORDS_NOINIT(0x3F); + struct IteratedBlendConstants { + u32 r; + u32 g; + u32 b; + INSERT_PADDING_BYTES_NOINIT(0x4); + }; + static_assert(sizeof(IteratedBlendConstants) == 0x10); + struct UserClip { + struct Enable { union { - BitField<0, 4, u32> stencil; - BitField<4, 4, u32> unknown; - BitField<8, 4, u32> scissor; - BitField<12, 4, u32> viewport; - } clear_flags; - - INSERT_PADDING_WORDS_NOINIT(0x10); - - u32 fill_rectangle; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 conservative_raster_enable; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - std::array vertex_attrib_format; - - std::array multisample_sample_locations; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - union { - BitField<0, 1, u32> enable; - BitField<4, 3, u32> target; - } multisample_coverage_to_color; - - INSERT_PADDING_WORDS_NOINIT(0x8); - - struct { - union { - BitField<0, 4, u32> count; - BitField<4, 3, u32> map_0; - BitField<7, 3, u32> map_1; - BitField<10, 3, u32> map_2; - BitField<13, 3, u32> map_3; - BitField<16, 3, u32> map_4; - BitField<19, 3, u32> map_5; - BitField<22, 3, u32> map_6; - BitField<25, 3, u32> map_7; - }; - - u32 Map(std::size_t index) const { - const std::array maps{map_0, map_1, map_2, map_3, - map_4, map_5, map_6, map_7}; - ASSERT(index < maps.size()); - return maps[index]; - } - } rt_control; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 zeta_width; - u32 zeta_height; - union { - BitField<0, 16, u32> zeta_depth; - BitField<16, 1, u32> zeta_volume; + u32 raw; + BitField<0, 1, u32> plane0; + BitField<1, 1, u32> plane1; + BitField<2, 1, u32> plane2; + BitField<3, 1, u32> plane3; + BitField<4, 1, u32> plane4; + BitField<5, 1, u32> plane5; + BitField<6, 1, u32> plane6; + BitField<7, 1, u32> plane7; }; - SamplerIndex sampler_index; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - std::array gp_passthrough_mask; - - INSERT_PADDING_WORDS_NOINIT(0x1B); - - u32 depth_test_enable; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 independent_blend_enable; - - u32 depth_write_enabled; - - u32 alpha_test_enabled; - - INSERT_PADDING_WORDS_NOINIT(0x6); - - u32 d3d_cull_mode; - - ComparisonOp depth_test_func; - float alpha_test_ref; - ComparisonOp alpha_test_func; - u32 draw_tfb_stride; - struct { - float r; - float g; - float b; - float a; - } blend_color; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - struct { - u32 separate_alpha; - Blend::Equation equation_rgb; - Blend::Factor factor_source_rgb; - Blend::Factor factor_dest_rgb; - Blend::Equation equation_a; - Blend::Factor factor_source_a; - INSERT_PADDING_WORDS_NOINIT(1); - Blend::Factor factor_dest_a; - - u32 enable_common; - u32 enable[NumRenderTargets]; - } blend; - - u32 stencil_enable; - StencilOp stencil_front_op_fail; - StencilOp stencil_front_op_zfail; - StencilOp stencil_front_op_zpass; - ComparisonOp stencil_front_func_func; - s32 stencil_front_func_ref; - u32 stencil_front_func_mask; - u32 stencil_front_mask; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 frag_color_clamp; - - union { - BitField<0, 1, u32> y_negate; - BitField<4, 1, u32> triangle_rast_flip; - } screen_y_control; - - float line_width_smooth; - float line_width_aliased; - - INSERT_PADDING_WORDS_NOINIT(0x1B); - - u32 invalidate_sampler_cache_no_wfi; - u32 invalidate_texture_header_cache_no_wfi; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 vb_element_base; - u32 vb_base_instance; - - INSERT_PADDING_WORDS_NOINIT(0x35); - - u32 clip_distance_enabled; - - u32 samplecnt_enable; - - float point_size; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 point_sprite_enable; - - INSERT_PADDING_WORDS_NOINIT(0x3); - - CounterReset counter_reset; - - u32 multisample_enable; - - u32 zeta_enable; - - union { - BitField<0, 1, u32> alpha_to_coverage; - BitField<4, 1, u32> alpha_to_one; - } multisample_control; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - struct { - u32 address_high; - u32 address_low; - ConditionMode mode; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } condition; - - struct { - u32 address_high; - u32 address_low; - u32 limit; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } tsc; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - float polygon_offset_factor; - - u32 line_smooth_enable; - - struct { - u32 address_high; - u32 address_low; - u32 limit; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } tic; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 stencil_two_side_enable; - StencilOp stencil_back_op_fail; - StencilOp stencil_back_op_zfail; - StencilOp stencil_back_op_zpass; - ComparisonOp stencil_back_func_func; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - u32 framebuffer_srgb; - - float polygon_offset_units; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - Tegra::Texture::MsaaMode multisample_mode; - - INSERT_PADDING_WORDS_NOINIT(0xC); - - union { - BitField<2, 1, u32> coord_origin; - BitField<3, 10, u32> enable; - } point_coord_replace; - - struct { - u32 code_address_high; - u32 code_address_low; - - GPUVAddr CodeAddress() const { - return static_cast( - (static_cast(code_address_high) << 32) | code_address_low); - } - } code_address; - INSERT_PADDING_WORDS_NOINIT(1); - - struct { - u32 vertex_end_gl; - union { - u32 vertex_begin_gl; - BitField<0, 16, PrimitiveTopology> topology; - BitField<26, 1, u32> instance_next; - BitField<27, 1, u32> instance_cont; - }; - } draw; - - INSERT_PADDING_WORDS_NOINIT(0xA); - - struct { - u32 enabled; - u32 index; - } primitive_restart; - - INSERT_PADDING_WORDS_NOINIT(0xE); - - u32 provoking_vertex_last; - - INSERT_PADDING_WORDS_NOINIT(0x50); - - struct { - u32 start_addr_high; - u32 start_addr_low; - u32 end_addr_high; - u32 end_addr_low; - IndexFormat format; - u32 first; - u32 count; - - unsigned FormatSizeInBytes() const { - switch (format) { - case IndexFormat::UnsignedByte: - return 1; - case IndexFormat::UnsignedShort: - return 2; - case IndexFormat::UnsignedInt: - return 4; - } - ASSERT(false); - return 1; - } - - GPUVAddr StartAddress() const { - return static_cast( - (static_cast(start_addr_high) << 32) | start_addr_low); - } - - GPUVAddr EndAddress() const { - return static_cast((static_cast(end_addr_high) << 32) | - end_addr_low); - } - - /// Adjust the index buffer offset so it points to the first desired index. - GPUVAddr IndexStart() const { - return StartAddress() + static_cast(first) * - static_cast(FormatSizeInBytes()); - } - } index_array; - - union { - BitField<0, 16, u32> first; - BitField<16, 16, u32> count; - } small_index; - - union { - BitField<0, 16, u32> first; - BitField<16, 16, u32> count; - } small_index_2; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - INSERT_PADDING_WORDS_NOINIT(0x1F); - - float polygon_offset_clamp; - - struct { - u32 is_instanced[NumVertexArrays]; - - /// Returns whether the vertex array specified by index is supposed to be - /// accessed per instance or not. - bool IsInstancingEnabled(std::size_t index) const { - return is_instanced[index]; - } - } instanced_arrays; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - union { - BitField<0, 1, u32> enable; - BitField<4, 8, u32> unk4; - } vp_point_size; - - INSERT_PADDING_WORDS_NOINIT(1); - - u32 cull_test_enabled; - FrontFace front_face; - CullFace cull_face; - - u32 pixel_center_integer; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 viewport_transform_enabled; - - INSERT_PADDING_WORDS_NOINIT(0x3); - - union { - BitField<0, 1, u32> depth_range_0_1; - BitField<3, 1, u32> depth_clamp_near; - BitField<4, 1, u32> depth_clamp_far; - BitField<11, 1, u32> depth_clamp_disabled; - } view_volume_clip_control; - - INSERT_PADDING_WORDS_NOINIT(0xC); - - PrimitiveTopologyOverride topology_override; - - INSERT_PADDING_WORDS_NOINIT(0x12); - - u32 depth_bounds_enable; - - INSERT_PADDING_WORDS_NOINIT(1); - - struct { - u32 enable; - LogicOperation operation; - } logic_op; - - INSERT_PADDING_WORDS_NOINIT(0x1); + bool AnyEnabled() const { + return plane0 || plane1 || plane2 || plane3 || plane4 || plane5 || plane6 || + plane7; + } + }; + + struct Op { + enum class ClipOrCull : u32 { + Clip = 0, + Cull = 1, + }; union { u32 raw; - BitField<0, 1, u32> Z; - BitField<1, 1, u32> S; - BitField<2, 1, u32> R; - BitField<3, 1, u32> G; - BitField<4, 1, u32> B; - BitField<5, 1, u32> A; - BitField<6, 4, u32> RT; - BitField<10, 11, u32> layer; - } clear_buffers; - INSERT_PADDING_WORDS_NOINIT(0xB); - std::array color_mask; - INSERT_PADDING_WORDS_NOINIT(0x38); + BitField<0, 1, ClipOrCull> plane0; + BitField<4, 1, ClipOrCull> plane1; + BitField<8, 1, ClipOrCull> plane2; + BitField<12, 1, ClipOrCull> plane3; + BitField<16, 1, ClipOrCull> plane4; + BitField<20, 1, ClipOrCull> plane5; + BitField<24, 1, ClipOrCull> plane6; + BitField<28, 1, ClipOrCull> plane7; + }; + }; + }; - struct { - u32 query_address_high; - u32 query_address_low; - u32 query_sequence; - union { - u32 raw; - BitField<0, 2, QueryOperation> operation; - BitField<4, 1, u32> fence; - BitField<12, 4, QueryUnit> unit; - BitField<16, 1, QuerySyncCondition> sync_cond; - BitField<23, 5, QuerySelect> select; - BitField<28, 1, u32> short_query; - } query_get; + struct AntiAliasAlphaControl { + union { + BitField<0, 1, u32> alpha_to_coverage; + BitField<4, 1, u32> alpha_to_one; + }; + }; - GPUVAddr QueryAddress() const { - return static_cast( - (static_cast(query_address_high) << 32) | query_address_low); - } - } query; + struct RenderEnable { + enum class Override : u32 { + UseRenderEnable = 0, + AlwaysRender = 1, + NeverRender = 2, + }; - INSERT_PADDING_WORDS_NOINIT(0x3C); + enum class Mode : u32 { + False = 0, + True = 1, + Conditional = 2, + IfEqual = 3, + IfNotEqual = 4, + }; - struct { - union { - BitField<0, 12, u32> stride; - BitField<12, 1, u32> enable; - }; - u32 start_high; - u32 start_low; - u32 divisor; + u32 address_high; + u32 address_low; + Mode mode; - GPUVAddr StartAddress() const { - return static_cast((static_cast(start_high) << 32) | - start_low); - } + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; - bool IsEnabled() const { - return enable != 0 && StartAddress() != 0; - } + struct TexSampler { + u32 address_high; + u32 address_low; + u32 limit; - } vertex_array[NumVertexArrays]; + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; - Blend independent_blend[NumRenderTargets]; + struct TexHeader { + u32 address_high; + u32 address_low; + u32 limit; - struct { - u32 limit_high; - u32 limit_low; + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; - GPUVAddr LimitAddress() const { - return static_cast((static_cast(limit_high) << 32) | - limit_low); - } - } vertex_array_limit[NumVertexArrays]; + enum class ZCullRegionFormat : u32 { + Z_4x4 = 0, + ZS_4x4 = 1, + Z_4x2 = 2, + Z_2x4 = 3, + Z_16x8_4x4 = 4, + Z_8x8_4x2 = 5, + Z_8x8_2x4 = 6, + Z_16x16_4x8 = 7, + Z_4x8_2x2 = 8, + ZS_16x8_4x2 = 9, + ZS_16x8_2x4 = 10, + ZS_8x8_2x2 = 11, + Z_4x8_1x1 = 12, + }; - struct { - union { - BitField<0, 1, u32> enable; - BitField<4, 4, ShaderProgram> program; - }; - u32 offset; - INSERT_PADDING_WORDS_NOINIT(14); - } shader_config[MaxShaderProgram]; + struct RtLayer { + enum class Control { + LayerSelectsLayer = 0, + GeometryShaderSelectsLayer = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x60); + union { + BitField<0, 16, u32> layer; + BitField<16, 1, u32> control; + }; + }; - u32 firmware[0x20]; + struct InlineIndex2x16 { + union { + BitField<0, 31, u32> count; + BitField<31, 1, u32> start_odd; + }; + union { + BitField<0, 16, u32> even; + BitField<16, 16, u32> odd; + }; + }; - struct { - u32 cb_size; - u32 cb_address_high; - u32 cb_address_low; - u32 cb_pos; - std::array cb_data; + struct VertexGlobalBaseOffset { + u32 address_high; + u32 address_low; - GPUVAddr BufferAddress() const { - return static_cast( - (static_cast(cb_address_high) << 32) | cb_address_low); - } - } const_buffer; + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; - INSERT_PADDING_WORDS_NOINIT(0x10); + struct ZCullRegionPixelOffset { + u32 width; + u32 height; + }; - struct { - union { - u32 raw_config; - BitField<0, 1, u32> valid; - BitField<4, 5, u32> index; - }; - INSERT_PADDING_WORDS_NOINIT(7); - } cb_bind[MaxShaderStage]; + struct PointSprite { + enum class RMode : u32 { + Zero = 0, + FromR = 1, + FromS = 2, + }; + enum class Origin : u32 { + Bottom = 0, + Top = 1, + }; + enum class Texture : u32 { + Passthrough = 0, + Generate = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x56); + union { + BitField<0, 2, RMode> rmode; + BitField<2, 1, Origin> origin; + BitField<3, 1, Texture> texture0; + BitField<4, 1, Texture> texture1; + BitField<5, 1, Texture> texture2; + BitField<6, 1, Texture> texture3; + BitField<7, 1, Texture> texture4; + BitField<8, 1, Texture> texture5; + BitField<9, 1, Texture> texture6; + BitField<10, 1, Texture> texture7; + BitField<11, 1, Texture> texture8; + BitField<12, 1, Texture> texture9; + }; + }; - u32 tex_cb_index; + struct ProgramRegion { + u32 address_high; + u32 address_low; - INSERT_PADDING_WORDS_NOINIT(0x7D); + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; - std::array, NumTransformFeedbackBuffers> tfb_varying_locs; + struct DefaultAttributes { + enum class Diffuse : u32 { + Vector_0001 = 0, + Vector_1111 = 1, + }; + enum class Specular : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; + enum class Vector : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; + enum class FixedFncTexture : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; + enum class DX9Color0 : u32 { + Vector_0000 = 0, + Vector_1111 = 1, + }; + enum class DX9Color1To15 : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x298); + union { + BitField<0, 1, Diffuse> color_front_diffuse; + BitField<1, 1, Specular> color_front_specular; + BitField<2, 1, Vector> generic_vector; + BitField<3, 1, FixedFncTexture> fixed_fnc_texture; + BitField<4, 1, DX9Color0> dx9_color0; + BitField<5, 1, DX9Color1To15> dx9_color1_to_15; + }; + }; - struct { - /// Compressed address of a buffer that holds information about bound SSBOs. - /// This address is usually bound to c0 in the shaders. - u32 buffer_address; + struct Draw { + enum class PrimitiveId : u32 { + First = 0, + Unchanged = 1, + }; + enum class InstanceId : u32 { + First = 0, + Subsequent = 1, + Unchanged = 2, + }; + enum class SplitMode : u32 { + NormalBeginNormal = 0, + NormalBeginOpen = 1, + OpenBeginOpen = 2, + OpenBeginNormal = 3, + }; - GPUVAddr BufferAddress() const { - return static_cast(buffer_address) << 8; - } - } ssbo_info; + u32 end; + union { + u32 begin; + BitField<0, 16, PrimitiveTopology> topology; + BitField<24, 1, PrimitiveId> primitive_id; + BitField<26, 2, InstanceId> instance_id; + BitField<29, 2, SplitMode> split_mode; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x11); + struct VertexIdCopy { + union { + BitField<0, 1, u32> enable; + BitField<4, 8, u32> attribute_slot; + }; + }; - struct { - u32 address[MaxShaderStage]; - u32 size[MaxShaderStage]; - } tex_info_buffers; + struct ShaderBasedCull { + union { + BitField<1, 1, u32> batch_cull_enable; + BitField<0, 1, u32> before_fetch_enable; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0xCC); + struct ClassVersion { + union { + BitField<0, 16, u32> current; + BitField<16, 16, u32> oldest_supported; + }; + }; + + struct PrimitiveRestart { + u32 enabled; + u32 index; + }; + + struct OutputVertexId { + union { + BitField<12, 1, u32> uses_array_start; + }; + }; + + enum class PointCenterMode : u32 { + GL = 0, + D3D = 1, + }; + + enum class LineSmoothParams : u32 { + Falloff_1_00 = 0, + Falloff_1_33 = 1, + Falloff_1_66 = 2, + }; + + struct LineSmoothEdgeTable { + union { + BitField<0, 8, u32> v0; + BitField<8, 8, u32> v1; + BitField<16, 8, u32> v2; + BitField<24, 8, u32> v3; + }; + }; + + struct LineStippleParams { + union { + BitField<0, 8, u32> factor; + BitField<8, 16, u32> pattern; + }; + }; + + enum class ProvokingVertex : u32 { + First = 0, + Last = 1, + }; + + struct ShaderControl { + enum class Partial : u32 { + Zero = 0, + Infinity = 1, + }; + enum class FP32NanBehavior : u32 { + Legacy = 0, + FP64Compatible = 1, + }; + enum class FP32F2INanBehavior : u32 { + PassZero = 0, + PassIndefinite = 1, + }; + + union { + BitField<0, 1, Partial> default_partial; + BitField<1, 1, FP32NanBehavior> fp32_nan_behavior; + BitField<2, 1, FP32F2INanBehavior> fp32_f2i_nan_behavior; + }; + }; + + struct SphVersion { + union { + BitField<0, 16, u32> current; + BitField<16, 16, u32> oldest_supported; + }; + }; + + struct AlphaToCoverageOverride { + union { + BitField<0, 1, u32> qualify_by_anti_alias_enable; + BitField<1, 1, u32> qualify_by_ps_sample_mask_enable; + }; + }; + + struct AamVersion { + union { + BitField<0, 16, u32> current; + BitField<16, 16, u32> oldest_supported; + }; + }; + + struct IndexBuffer { + u32 start_addr_high; + u32 start_addr_low; + u32 limit_addr_high; + u32 limit_addr_low; + IndexFormat format; + u32 first; + u32 count; + + unsigned FormatSizeInBytes() const { + switch (format) { + case IndexFormat::UnsignedByte: + return 1; + case IndexFormat::UnsignedShort: + return 2; + case IndexFormat::UnsignedInt: + return 4; + } + ASSERT(false); + return 1; + } + + GPUVAddr StartAddress() const { + return static_cast((static_cast(start_addr_high) << 32) | + start_addr_low); + } + + GPUVAddr EndAddress() const { + return static_cast((static_cast(limit_addr_high) << 32) | + limit_addr_low); + } + + /// Adjust the index buffer offset so it points to the first desired index. + GPUVAddr IndexStart() const { + return StartAddress() + + static_cast(first) * static_cast(FormatSizeInBytes()); + } + }; + + struct IndexBufferSmall { + union { + BitField<0, 16, u32> first; + BitField<16, 12, u32> count; + BitField<28, 4, PrimitiveTopology> topology; + }; + }; + + struct VertexStreamInstances { + std::array is_instanced; + + /// Returns whether the vertex array specified by index is supposed to be + /// accessed per instance or not. + bool IsInstancingEnabled(std::size_t index) const { + return is_instanced[index]; + } + }; + + struct AttributePointSize { + union { + BitField<0, 1, u32> enabled; + BitField<4, 8, u32> slot; + }; + }; + + struct ViewportClipControl { + enum class GeometryGuardband : u32 { + Scale256 = 0, + Scale1 = 1, + }; + enum class GeometryClip : u32 { + WZero = 0, + Passthrough = 1, + FrustumXY = 2, + FrustumXYZ = 3, + WZeroNoZCull = 4, + FrustumZ = 5, + WZeroTriFillOrClip = 6, + }; + enum class GeometryGuardbandZ : u32 { + SameAsXY = 0, + Scale256 = 1, + Scale1 = 2, + }; + + union { + BitField<0, 1, u32> depth_0_to_1; + BitField<3, 1, u32> pixel_min_z; + BitField<4, 1, u32> pixel_max_z; + BitField<7, 1, GeometryGuardband> geometry_guardband; + BitField<11, 3, GeometryClip> geometry_clip; + BitField<1, 2, GeometryGuardbandZ> geometry_guardband_z; + }; + }; + + enum class PrimitiveTopologyControl : u32 { + UseInBeginMethods = 0, + UseSeparateState = 1, + }; + + struct WindowClip { + enum class Type : u32 { + Inclusive = 0, + Exclusive = 1, + ClipAll = 2, + }; + + u32 enable; + Type type; + }; + + enum class InvalidateZCull : u32 { + Invalidate = 0, + }; + + struct ZCull { + union { + BitField<0, 1, u32> z_enable; + BitField<1, 1, u32> stencil_enable; + }; + union { + BitField<0, 1, u32> z_min_enbounded; + BitField<1, 1, u32> z_max_unbounded; + }; + }; + + struct LogicOp { + enum class Op : u32 { + Clear = 0x1500, + And = 0x1501, + AndReverse = 0x1502, + Copy = 0x1503, + AndInverted = 0x1504, + NoOp = 0x1505, + Xor = 0x1506, + Or = 0x1507, + Nor = 0x1508, + Equiv = 0x1509, + Invert = 0x150A, + OrReverse = 0x150B, + CopyInverted = 0x150C, + OrInverted = 0x150D, + Nand = 0x150E, + Set = 0x150F, + }; + + u32 enable; + Op op; + }; + + struct ClearSurface { + union { + u32 raw; + BitField<0, 1, u32> Z; + BitField<1, 1, u32> S; + BitField<2, 1, u32> R; + BitField<3, 1, u32> G; + BitField<4, 1, u32> B; + BitField<5, 1, u32> A; + BitField<6, 4, u32> RT; + BitField<10, 16, u32> layer; + }; + }; + + struct ReportSemaphore { + struct Compare { + u32 initial_sequence; + u32 initial_mode; + u32 unknown1; + u32 unknown2; + u32 current_sequence; + u32 current_mode; + }; + + enum class Operation : u32 { + Release = 0, + Acquire = 1, + ReportOnly = 2, + Trap = 3, + }; + + enum class Release : u32 { + AfterAllPreceedingReads = 0, + AfterAllPreceedingWrites = 1, + }; + + enum class Acquire : u32 { + BeforeAnyFollowingWrites = 0, + BeforeAnyFollowingReads = 1, + }; + + enum class Location : u32 { + None = 0, + VertexFetch = 1, + VertexShader = 2, + VPC = 4, + StreamingOutput = 5, + GeometryShader = 6, + ZCull = 7, + TessellationInit = 8, + TessellationShader = 9, + PixelShader = 10, + DepthTest = 12, + All = 15, + }; + + enum class Comparison : u32 { + NotEqual = 0, + GreaterOrEqual = 1, + }; + + enum class Report : u32 { + Payload = 0, // "None" in docs, but confirmed via hardware to return the payload + VerticesGenerated = 1, + ZPassPixelCount = 2, + PrimitivesGenerated = 3, + AlphaBetaClocks = 4, + VertexShaderInvocations = 5, + StreamingPrimitivesNeededMinusSucceeded = 6, + GeometryShaderInvocations = 7, + GeometryShaderPrimitivesGenerated = 9, + ZCullStats0 = 10, + StreamingPrimitivesSucceeded = 11, + ZCullStats1 = 12, + StreamingPrimitivesNeeded = 13, + ZCullStats2 = 14, + ClipperInvocations = 15, + ZCullStats3 = 16, + ClipperPrimitivesGenerated = 17, + VtgPrimitivesOut = 18, + PixelShaderInvocations = 19, + ZPassPixelCount64 = 21, + IEEECleanColorTarget = 24, + IEEECleanZetaTarget = 25, + StreamingByteCount = 26, + TessellationInitInvocations = 27, + BoundingRectangle = 28, + TessellationShaderInvocations = 29, + TotalStreamingPrimitivesNeededMinusSucceeded = 30, + TessellationShaderPrimitivesGenerated = 31, + }; + + u32 address_high; + u32 address_low; + u32 payload; + union { + u32 raw; + BitField<0, 2, Operation> operation; + BitField<4, 1, Release> release; + BitField<8, 1, Acquire> acquire; + BitField<12, 4, Location> location; + BitField<16, 1, Comparison> comparison; + BitField<20, 1, u32> awaken_enable; + BitField<23, 5, Report> report; + BitField<28, 1, u32> short_query; + BitField<5, 3, u32> sub_report; + BitField<21, 1, u32> dword_number; + BitField<2, 1, u32> disable_flush; + BitField<3, 1, u32> reduction_enable; + BitField<9, 3, ReductionOp> reduction_op; + BitField<17, 2, u32> format_signed; + } query; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; + + struct VertexStream { + union { + BitField<0, 12, u32> stride; + BitField<12, 1, u32> enable; + }; + u32 address_high; + u32 address_low; + u32 frequency; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + + bool IsEnabled() const { + return enable != 0 && Address() != 0; + } + }; + static_assert(sizeof(VertexStream) == 0x10); + + struct VertexStreamLimit { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; + static_assert(sizeof(VertexStreamLimit) == 0x8); + + enum class ShaderType : u32 { + VertexA = 0, + VertexB = 1, + TessellationInit = 2, + Tessellation = 3, + Geometry = 4, + Pixel = 5, + }; + + struct Pipeline { + union { + BitField<0, 1, u32> enable; + BitField<4, 4, ShaderType> program; + }; + u32 offset; + u32 reservedA; + u32 register_count; + u32 binding_group; + std::array reserved; + INSERT_PADDING_BYTES_NOINIT(0x1C); + }; + static_assert(sizeof(Pipeline) == 0x40); + + bool IsShaderConfigEnabled(std::size_t index) const { + // The VertexB is always enabled. + if (index == static_cast(ShaderType::VertexB)) { + return true; + } + return pipelines[index].enable != 0; + } + + bool IsShaderConfigEnabled(ShaderType type) const { + return IsShaderConfigEnabled(static_cast(type)); + } + + struct ConstantBuffer { + u32 size; + u32 address_high; + u32 address_low; + u32 offset; + std::array buffer; + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + }; + + struct BindGroup { + std::array reserved; + union { + u32 raw_config; + BitField<0, 1, u32> valid; + BitField<4, 5, u32> shader_slot; + }; + INSERT_PADDING_BYTES_NOINIT(0xC); + }; + static_assert(sizeof(BindGroup) == 0x20); + + struct StreamOutLayout { + union { + BitField<0, 8, u32> attribute0; + BitField<8, 8, u32> attribute1; + BitField<16, 8, u32> attribute2; + BitField<24, 8, u32> attribute3; + }; + }; + + struct ShaderPerformance { + struct ControlA { + union { + BitField<0, 2, u32> event0; + BitField<2, 3, u32> bit0; + BitField<5, 2, u32> event1; + BitField<7, 3, u32> bit1; + BitField<10, 2, u32> event2; + BitField<12, 3, u32> bit2; + BitField<15, 2, u32> event3; + BitField<17, 3, u32> bit3; + BitField<20, 2, u32> event4; + BitField<22, 3, u32> bit4; + BitField<25, 2, u32> event5; + BitField<27, 3, u32> bit5; + BitField<30, 2, u32> spare; + }; + }; + + struct ControlB { + union { + BitField<0, 1, u32> edge; + BitField<1, 2, u32> mode; + BitField<3, 1, u32> windowed; + BitField<4, 16, u32> func; + }; + }; + + std::array values_upper; + std::array values; + std::array events; + std::array control_a; + std::array control_b; + u32 trap_control_mask; + u32 start_shader_mask; + u32 stop_shader_mask; + }; + + // clang-format off + union { + struct { + ID object_id; ///< 0x0000 + INSERT_PADDING_BYTES_NOINIT(0xFC); + u32 nop; ///< 0x0100 + Notify notify; ///< 0x0104 + u32 wait_for_idle; ///< 0x0110 + LoadMME load_mme; ///< 0x0114 + ShadowRamControl shadow_ram_control; ///< 0x0124 + PeerSemaphore peer; ///< 0x0128 + GlobalRender global_render; ///< 0x0130 + u32 go_idle; ///< 0x013C + u32 trigger; ///< 0x0140 + u32 trigger_wfi; ///< 0x0144 + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 instrumentation_method_header; ///< 0x0150 + u32 instrumentation_method_data; ///< 0x0154 + INSERT_PADDING_BYTES_NOINIT(0x28); + Upload::Registers upload; ///< 0x0180 + LaunchDMA launch_dma; ///< 0x01B0 + u32 inline_data; ///< 0x01B4 + INSERT_PADDING_BYTES_NOINIT(0x24); + I2M i2m; ///< 0x01DC + u32 run_ds_now; ///< 0x0200 + OpportunisticEarlyZ opportunistic_early_z; ///< 0x0204 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 aliased_line_width_enabled; ///< 0x020C + u32 mandated_early_z; ///< 0x0210 + GeometryShaderDmFifo gs_dm_fifo; ///< 0x0214 + L2CacheControl l2_cache_control; ///< 0x0218 + InvalidateShaderCache invalidate_shader_cache; ///< 0x021C + INSERT_PADDING_BYTES_NOINIT(0xA8); + SyncInfo sync_info; ///< 0x02C8 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 prim_circular_buffer_throttle; ///< 0x02D0 + u32 flush_invalidate_rop_mini_cache; ///< 0x02D4 + SurfaceClipBlockId surface_clip_block_id; ///< 0x02D8 + u32 alpha_circular_buffer_size; ///< 0x02DC + DecompressSurface decompress_surface; ///< 0x02E0 + ZCullRopBypass zcull_rop_bypass; ///< 0x02E4 + ZCullSubregion zcull_subregion; ///< 0x02E8 + RasterBoundingBox raster_bounding_box; ///< 0x02EC + u32 peer_semaphore_release; ///< 0x02F0 + u32 iterated_blend_optimization; ///< 0x02F4 + ZCullSubregionAllocation zcull_subregion_allocation; ///< 0x02F8 + ZCullSubregionAlgorithm zcull_subregion_algorithm; ///< 0x02FC + PixelShaderOutputSampleMaskUsage ps_output_sample_mask_usage; ///< 0x0300 + u32 draw_zero_index; ///< 0x0304 + L1Configuration l1_configuration; ///< 0x0308 + u32 render_enable_control_load_const_buffer; ///< 0x030C + SPAVersion spa_version; ///< 0x0310 + u32 ieee_clean_update; ///< 0x0314 + SnapGrid snap_grid; ///< 0x0318 + Tessellation tessellation; ///< 0x0320 + SubTilingPerf sub_tiling_perf; ///< 0x0360 + ZCullSubregionReport zcull_subregion_report; ///< 0x036C + BalancedPrimitiveWorkload balanced_primitive_workload; ///< 0x0374 + u32 max_patches_per_batch; ///< 0x0378 + u32 rasterize_enable; ///< 0x037C + TransformFeedback transform_feedback; ///< 0x0380 + u32 raster_input; ///< 0x0740 + u32 transform_feedback_enabled; ///< 0x0744 + u32 primitive_restart_topology_change_enable; ///< 0x0748 + u32 alpha_fraction; ///< 0x074C + INSERT_PADDING_BYTES_NOINIT(0x4); + HybridAntiAliasControl hybrid_aa_control; ///< 0x0754 + INSERT_PADDING_BYTES_NOINIT(0x24); + ShaderLocalMemory shader_local_memory; ///< 0x077C + u32 color_zero_bandwidth_clear; ///< 0x07A4 + u32 z_zero_bandwidth_clear; ///< 0x07A8 + u32 isbe_save_restore_program_offset; ///< 0x07AC + INSERT_PADDING_BYTES_NOINIT(0x10); + ZCullRegion zcull_region; ///< 0x07C0 + ZetaReadOnly zeta_read_only; ///< 0x07F8 + INSERT_PADDING_BYTES_NOINIT(0x4); + std::array rt; ///< 0x0800 + std::array viewport_transform; ///< 0x0A00 + std::array viewports; ///< 0x0C00 + std::array windows; ///< 0x0D00 + std::array clip_id_extent; ///< 0x0D40 + u32 max_geometry_instances_per_task; ///< 0x0D60 + VisibleCallLimit visible_call_limit; ///< 0x0D64 + StatisticsCounter statistics_count; ///< 0x0D68 + ClearRect clear_rect; ///< 0x0D6C + VertexBuffer vertex_buffer; ///< 0x0D74 + DepthMode depth_mode; ///< 0x0D7C + std::array clear_color; ///< 0x0D80 + f32 clear_depth; ///< 0x0D90 + u32 shader_cache_icache_prefetch; ///< 0x0D94 + u32 force_transition_to_beta; ///< 0x0D98 + u32 reduce_colour_thresholds; ///< 0x0D9C + s32 clear_stencil; ///< 0x0DA0 + InvalidateShaderCacheNoWFI invalidate_shader_cache_no_wfi; ///< 0x0DA4 + ZCullSerialization zcull_serialization; ///< 0x0DA8 + PolygonMode polygon_mode_front; ///< 0x0DAC + PolygonMode polygon_mode_back; ///< 0x0DB0 + u32 polygon_smooth; ///< 0x0DB4 + u32 zeta_mark_clean_ieee; ///< 0x0DB8 + ZCullDirFormat zcull_dir_format; ///< 0x0DBC + u32 polygon_offset_point_enable; ///< 0x0DC0 + u32 polygon_offset_line_enable; ///< 0x0DC4 + u32 polygon_offset_fill_enable; ///< 0x0DC8 + u32 patch_vertices; ///< 0x0DCC + IteratedBlend iterated_blend; ///< 0x0DD0 + ZCullCriterion zcull_criteria; ///< 0x0DD8 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 fragment_barrier; ///< 0x0DE0 + u32 sm_timeout; ///< 0x0DE4 + u32 primitive_restart_array; ///< 0x0DE8 + INSERT_PADDING_BYTES_NOINIT(0x4); + LoadIteratedBlend load_iterated_blend; ///< 0x0DF0 + u32 window_offset_x; ///< 0x0DF8 + u32 window_offset_y; ///< 0x0DFC + std::array scissor_test; ///< 0x0E00 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 select_texture_headers; ///< 0x0F10 + VPCPerf vpc_perf; ///< 0x0F14 + u32 pm_local_trigger; ///< 0x0F18 + u32 post_z_pixel_imask; ///< 0x0F1C + INSERT_PADDING_BYTES_NOINIT(0x20); + ConstantColorRendering const_color_rendering; ///< 0x0F40 + StencilFunc stencil_back_func; ///< 0x0F54 + INSERT_PADDING_BYTES_NOINIT(0x24); + VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84 + u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C + u32 color_mask_common; ///< 0x0F90 + INSERT_PADDING_BYTES_NOINIT(0x4); + VTGWarpWatermarks vtg_warp_watermarks; ///< 0x0F98 + f32 depth_bounds[2]; ///< 0x0F9C + SampleMask::Target sample_mask_target; ///< 0x0FA4 + u32 color_target_mrt_enable; ///< 0x0FAC + NonMultisampledZ non_multisampled_z; ///< 0x0FB0 + TIRMode tir_mode; ///< 0x0FB4 + AntiAliasRaster anti_alias_raster; ///< 0x0FB8 + SampleMask::Pos sample_mask_pos; ///< 0x0FBC + SurfaceClipIDMemory surface_clip_id_memory; ///< 0x0FCC + TIRModulation tir_modulation; ///< 0x0FD4 + u32 blend_control_allow_float_pixel_kills; ///< 0x0FDC + Zeta zeta; ///< 0x0FE0 + SurfaceClip surface_clip; ///< 0x0FF4 + u32 tiled_cache_treat_heavy_as_light; ///< 0x0FFC + L2CacheVAFRequests l2_cache_vaf; ///< 0x1000 + ViewportMulticast viewport_multicast; ///< 0x1004 + u32 tessellation_cut_height; ///< 0x1008 + u32 max_gs_instances_per_task; ///< 0x100C + u32 max_gs_output_vertices_per_task; ///< 0x1010 + u32 reserved_sw_method0; ///< 0x1014 + u32 gs_output_cb_storage_multiplier; ///< 0x1018 + u32 beta_cb_storage_constant; ///< 0x101C + u32 ti_output_cb_storage_multiplier; ///< 0x1020 + u32 alpha_cb_storage_constraint; ///< 0x1024 + u32 reserved_sw_method1; ///< 0x1028 + u32 reserved_sw_method2; ///< 0x102C + std::array tir_modulation_coeff; ///< 0x1030 + std::array spare_nop; ///< 0x1044 + INSERT_PADDING_BYTES_NOINIT(0x30); + std::array reserved_sw_method3_to_7; ///< 0x10B0 + ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC + std::array reserved_sw_method10_to_13; ///< 0x10D0 + ReduceColorThreshold reduce_color_thresholds_unorm10; ///< 0x10E0 + ReduceColorThreshold reduce_color_thresholds_unorm16; ///< 0x10E4 + ReduceColorThreshold reduce_color_thresholds_fp11; ///< 0x10E8 + ReduceColorThreshold reduce_color_thresholds_fp16; ///< 0x10EC + ReduceColorThreshold reduce_color_thresholds_srgb8; ///< 0x10F0 + u32 unbind_all_constant_buffers; ///< 0x10F4 + ClearControl clear_control; ///< 0x10F8 + L2CacheRopNonInterlockedReads l2_cache_rop_non_interlocked_reads; ///< 0x10FC + u32 reserved_sw_method14; ///< 0x1100 + u32 reserved_sw_method15; ///< 0x1104 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 no_operation_data_high; ///< 0x110C + u32 depth_bias_control; ///< 0x1110 + u32 pm_trigger_end; ///< 0x1114 + u32 vertex_id_base; ///< 0x1118 + u32 stencil_compression_enabled; ///< 0x111C + VertexOutputAttributeSkipMasks vertex_output_attribute_skip_masks; ///< 0x1120 + TIRControl tir_control; ///< 0x1130 + u32 mutable_method_treat_mutable_as_heavy; ///< 0x1134 + u32 post_ps_use_pre_ps_coverage; ///< 0x1138 + FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C + u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140 + u32 flush_pending_writes_sm_gloal_store; ///< 0x1144 + INSERT_PADDING_BYTES_NOINIT(0x18); + std::array vertex_attrib_format; ///< 0x1160 + std::array multisample_sample_locations; ///< 0x11E0 + u32 offset_render_target_index_by_viewport_index; ///< 0x11F0 + u32 force_heavy_method_sync; ///< 0x11F4 + MultisampleCoverageToColor multisample_coverage_to_color; ///< 0x11F8 + DecompressZetaSurface decompress_zeta_surface; ///< 0x11FC + INSERT_PADDING_BYTES_NOINIT(0x8); + ZetaSparse zeta_sparse; ///< 0x1208 + u32 invalidate_sampler_cache; ///< 0x120C + u32 invalidate_texture_header_cache; ///< 0x1210 + VertexArray vertex_array_instance_first; ///< 0x1214 + VertexArray vertex_array_instance_subsequent; ///< 0x1218 + RtControl rt_control; ///< 0x121C + CompressionThresholdSamples compression_threshold_samples; ///< 0x1220 + PixelShaderInterlockControl ps_interlock_control; ///< 0x1224 + ZetaSize zeta_size; ///< 0x1228 + SamplerBinding sampler_binding; ///< 0x1234 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 draw_auto_byte_count; ///< 0x123C + std::array post_vtg_shader_attrib_skip_mask; ///< 0x1240 + PsTicketDispenserValue ps_ticket_dispenser_value; ///< 0x1260 + INSERT_PADDING_BYTES_NOINIT(0x1C); + u32 circular_buffer_size; ///< 0x1280 + RegisterWatermarks vtg_register_watermarks; ///< 0x1284 + InvalidateTextureDataCacheNoWfi invalidate_texture_cache_no_wfi; ///< 0x1288 + INSERT_PADDING_BYTES_NOINIT(0x4); + L2CacheRopNonInterlockedReads l2_cache_rop_interlocked_reads; ///< 0x1290 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 primitive_restart_topology_change_index; ///< 0x12A4 + INSERT_PADDING_BYTES_NOINIT(0x20); + ZCullRegionEnable zcull_region_enable; ///< 0x12C8 + u32 depth_test_enable; ///< 0x12CC + FillMode fill_mode; ///< 0x12D0 + ShadeMode shade_mode; ///< 0x12D4 + L2CacheRopNonInterlockedReads l2_cache_rop_non_interlocked_writes; ///< 0x12D8 + L2CacheRopNonInterlockedReads l2_cache_rop_interlocked_writes; ///< 0x12DC + AlphaToCoverageDither alpha_to_coverage_dither; ///< 0x12E0 + u32 blend_per_target_enabled; ///< 0x12E4 + 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 + D3DCullMode d3d_cull_mode; ///< 0x1308 + ComparisonOp depth_test_func; ///< 0x130C + f32 alpha_test_ref; ///< 0x1310 + ComparisonOp alpha_test_func; ///< 0x1314 + u32 draw_auto_stride; ///< 0x1318 + BlendColor blend_color; ///< 0x131C + INSERT_PADDING_BYTES_NOINIT(0x4); + InvalidateCacheLines invalidate_sampler_cache_lines; ///< 0x1330 + InvalidateCacheLines invalidate_texture_header_cache_lines; ///< 0x1334 + InvalidateCacheLines invalidate_texture_data_cache_lines; ///< 0x1338 + Blend blend; ///< 0x133C + u32 stencil_enable; ///< 0x1380 + StencilOp stencil_front_op; ///< 0x1384 + StencilFunc stencil_front_func; ///< 0x1394 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 draw_auto_start_byte_count; ///< 0x13A4 + PsSaturate frag_color_clamp; ///< 0x13A8 + WindowOrigin window_origin; ///< 0x13AC + f32 line_width_smooth; ///< 0x13B0 + f32 line_width_aliased; ///< 0x13B4 + INSERT_PADDING_BYTES_NOINIT(0x60); + u32 line_override_multisample; ///< 0x1418 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 alpha_hysteresis_rounds; ///< 0x1420 + InvalidateCacheLines invalidate_sampler_cache_no_wfi; ///< 0x1424 + InvalidateCacheLines invalidate_texture_header_cache_no_wfi; ///< 0x1428 + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 global_base_vertex_index; ///< 0x1434 + u32 global_base_instance_index; ///< 0x1438 + INSERT_PADDING_BYTES_NOINIT(0x14); + RegisterWatermarks ps_warp_watermarks; ///< 0x1450 + RegisterWatermarks ps_regster_watermarks; ///< 0x1454 + INSERT_PADDING_BYTES_NOINIT(0xC); + u32 store_zcull; ///< 0x1464 + INSERT_PADDING_BYTES_NOINIT(0x18); + std::array + iterated_blend_constants; ///< 0x1480 + u32 load_zcull; ///< 0x1500 + u32 surface_clip_id_height; ///< 0x1504 + Window surface_clip_id_clear_rect; ///< 0x1508 + UserClip::Enable user_clip_enable; ///< 0x1510 + u32 zpass_pixel_count_enable; ///< 0x1514 + f32 point_size; ///< 0x1518 + u32 zcull_stats_enable; ///< 0x151C + u32 point_sprite_enable; ///< 0x1520 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 shader_exceptions_enable; ///< 0x1528 + INSERT_PADDING_BYTES_NOINIT(0x4); + ClearReport clear_report_value; ///< 0x1530 + u32 anti_alias_enable; ///< 0x1534 + u32 zeta_enable; ///< 0x1538 + AntiAliasAlphaControl anti_alias_alpha_control; ///< 0x153C + INSERT_PADDING_BYTES_NOINIT(0x10); + RenderEnable render_enable; ///< 0x1550 + TexSampler tex_sampler; ///< 0x155C + INSERT_PADDING_BYTES_NOINIT(0x4); + f32 slope_scale_depth_bias; ///< 0x156C + u32 line_anti_alias_enable; ///< 0x1570 + TexHeader tex_header; ///< 0x1574 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 active_zcull_region_id; ///< 0x1590 + u32 stencil_two_side_enable; ///< 0x1594 + StencilOp stencil_back_op; ///< 0x1598 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 framebuffer_srgb; ///< 0x15B8 + f32 depth_bias; ///< 0x15BC + INSERT_PADDING_BYTES_NOINIT(0x8); + ZCullRegionFormat zcull_region_format; ///< 0x15C8 + RtLayer rt_layer; ///< 0x15CC + Tegra::Texture::MsaaMode anti_alias_samples_mode; ///< 0x15D0 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 edge_flag; ///< 0x15E4 + u32 draw_inline_index; ///< 0x15E8 + InlineIndex2x16 inline_index_2x16; ///< 0x15EC + VertexGlobalBaseOffset vertex_global_base_offset; ///< 0x15F4 + ZCullRegionPixelOffset zcull_region_pixel_offset; ///< 0x15FC + PointSprite point_sprite; ///< 0x1604 + ProgramRegion program_region; ///< 0x1608 + DefaultAttributes default_attributes; ///< 0x1610 + Draw draw; ///< 0x1614 + VertexIdCopy vertex_id_copy; ///< 0x161C + u32 add_to_primitive_id; ///< 0x1620 + u32 load_to_primitive_id; ///< 0x1624 + INSERT_PADDING_BYTES_NOINIT(0x4); + ShaderBasedCull shader_based_cull; ///< 0x162C + INSERT_PADDING_BYTES_NOINIT(0x8); + ClassVersion class_version; ///< 0x1638 + INSERT_PADDING_BYTES_NOINIT(0x8); + PrimitiveRestart primitive_restart; ///< 0x1644 + OutputVertexId output_vertex_id; ///< 0x164C + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 anti_alias_point_enable; ///< 0x1658 + PointCenterMode point_center_mode; ///< 0x165C + INSERT_PADDING_BYTES_NOINIT(0x8); + LineSmoothParams line_smooth_params; ///< 0x1668 + u32 line_stipple_enable; ///< 0x166C + std::array line_smooth_edge_table; ///< 0x1670 + LineStippleParams line_stipple_params; ///< 0x1680 + ProvokingVertex provoking_vertex; ///< 0x1684 + u32 two_sided_light_enabled; ///< 0x1688 + u32 polygon_stipple_enabled; ///< 0x168C + ShaderControl shader_control; ///< 0x1690 + INSERT_PADDING_BYTES_NOINIT(0xC); + ClassVersion class_version_check; ///< 0x16A0 + SphVersion sph_version; ///< 0x16A4 + SphVersion sph_version_check; ///< 0x16A8 + INSERT_PADDING_BYTES_NOINIT(0x8); + AlphaToCoverageOverride alpha_to_coverage_override; ///< 0x16B4 + INSERT_PADDING_BYTES_NOINIT(0x48); + std::array polygon_stipple_pattern; ///< 0x1700 + INSERT_PADDING_BYTES_NOINIT(0x10); + AamVersion aam_version; ///< 0x1790 + AamVersion aam_version_check; ///< 0x1794 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 zeta_layer_offset; ///< 0x179C + INSERT_PADDING_BYTES_NOINIT(0x28); + IndexBuffer index_buffer; ///< 0x17C8 + IndexBufferSmall index_buffer32_first; ///< 0x17E4 + IndexBufferSmall index_buffer16_first; ///< 0x17E8 + IndexBufferSmall index_buffer8_first; ///< 0x17EC + IndexBufferSmall index_buffer32_subsequent; ///< 0x17F0 + IndexBufferSmall index_buffer16_subsequent; ///< 0x17F4 + IndexBufferSmall index_buffer8_subsequent; ///< 0x17F8 + INSERT_PADDING_BYTES_NOINIT(0x80); + f32 depth_bias_clamp; ///< 0x187C + VertexStreamInstances vertex_stream_instances; ///< 0x1880 + INSERT_PADDING_BYTES_NOINIT(0x10); + AttributePointSize point_size_attribute; ///< 0x1910 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 gl_cull_test_enabled; ///< 0x1918 + FrontFace gl_front_face; ///< 0x191C + CullFace gl_cull_face; ///< 0x1920 + Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 viewport_scale_offset_enbled; ///< 0x192C + INSERT_PADDING_BYTES_NOINIT(0xC); + ViewportClipControl viewport_clip_control; ///< 0x193C + UserClip::Op user_clip_op; ///< 0x1940 + RenderEnable::Override render_enable_override; ///< 0x1944 + PrimitiveTopologyControl primitive_topology_control; ///< 0x1948 + WindowClip window_clip_enable; ///< 0x194C + INSERT_PADDING_BYTES_NOINIT(0x4); + InvalidateZCull invalidate_zcull; ///< 0x1958 + INSERT_PADDING_BYTES_NOINIT(0xC); + ZCull zcull; ///< 0x1968 + PrimitiveTopologyOverride topology_override; ///< 0x1970 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 zcull_sync; ///< 0x1978 + u32 clip_id_test_enable; ///< 0x197C + u32 surface_clip_id_width; ///< 0x1980 + u32 clip_id; ///< 0x1984 + INSERT_PADDING_BYTES_NOINIT(0x34); + u32 depth_bounds_enable; ///< 0x19BC + u32 blend_float_zero_times_anything_is_zero; ///< 0x19C0 + LogicOp logic_op; ///< 0x19C4 + u32 z_compression_enable; ///< 0x19CC + ClearSurface clear_surface; ///< 0x19D0 + u32 clear_clip_id_surface; ///< 0x19D4 + INSERT_PADDING_BYTES_NOINIT(0x8); + std::array color_compression_enable; ///< 0x19E0 + std::array color_mask; ///< 0x1A00 + INSERT_PADDING_BYTES_NOINIT(0xC); + u32 pipe_nop; ///< 0x1A2C + std::array spare; ///< 0x1A30 + INSERT_PADDING_BYTES_NOINIT(0xC0); + ReportSemaphore report_semaphore; ///< 0x1B00 + INSERT_PADDING_BYTES_NOINIT(0xF0); + std::array vertex_streams; ///< 0x1C00 + BlendPerTarget blend_per_target[NumRenderTargets]; ///< 0x1E00 + std::array vertex_stream_limits; ///< 0x1F00 + std::array pipelines; ///< 0x2000 + INSERT_PADDING_BYTES_NOINIT(0x180); + u32 falcon[32]; ///< 0x2300 + ConstantBuffer const_buffer; ///< 0x2380 + INSERT_PADDING_BYTES_NOINIT(0x30); + BindGroup bind_groups[MaxShaderStage]; ///< 0x2400 + INSERT_PADDING_BYTES_NOINIT(0x160); + u32 color_clamp_enable; ///< 0x2600 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 bindless_texture_const_buffer_slot; ///< 0x2608 + u32 trap_handler; ///< 0x260C + INSERT_PADDING_BYTES_NOINIT(0x1F0); + std::array, NumTransformFeedbackBuffers> + stream_out_layout; ///< 0x2800 + INSERT_PADDING_BYTES_NOINIT(0x93C); + ShaderPerformance shader_performance; ///< 0x333C + INSERT_PADDING_BYTES_NOINIT(0x18); + std::array shadow_scratch; ///< 0x3400 }; std::array reg_array; }; }; + // clang-format on Regs regs{}; @@ -1591,147 +3203,346 @@ private: }; #define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \ + static_assert(offsetof(Maxwell3D::Regs, field_name) == position, \ "Field " #field_name " has invalid position") -ASSERT_REG_POSITION(wait_for_idle, 0x44); -ASSERT_REG_POSITION(macros, 0x45); -ASSERT_REG_POSITION(shadow_ram_control, 0x49); -ASSERT_REG_POSITION(upload, 0x60); -ASSERT_REG_POSITION(exec_upload, 0x6C); -ASSERT_REG_POSITION(data_upload, 0x6D); -ASSERT_REG_POSITION(force_early_fragment_tests, 0x84); -ASSERT_REG_POSITION(sync_info, 0xB2); -ASSERT_REG_POSITION(tess_mode, 0xC8); -ASSERT_REG_POSITION(tess_level_outer, 0xC9); -ASSERT_REG_POSITION(tess_level_inner, 0xCD); -ASSERT_REG_POSITION(rasterize_enable, 0xDF); -ASSERT_REG_POSITION(tfb_bindings, 0xE0); -ASSERT_REG_POSITION(tfb_layouts, 0x1C0); -ASSERT_REG_POSITION(tfb_enabled, 0x1D1); -ASSERT_REG_POSITION(rt, 0x200); -ASSERT_REG_POSITION(viewport_transform, 0x280); -ASSERT_REG_POSITION(viewports, 0x300); -ASSERT_REG_POSITION(vertex_buffer, 0x35D); -ASSERT_REG_POSITION(depth_mode, 0x35F); -ASSERT_REG_POSITION(clear_color[0], 0x360); -ASSERT_REG_POSITION(clear_depth, 0x364); -ASSERT_REG_POSITION(clear_stencil, 0x368); -ASSERT_REG_POSITION(polygon_mode_front, 0x36B); -ASSERT_REG_POSITION(polygon_mode_back, 0x36C); -ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370); -ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371); -ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372); -ASSERT_REG_POSITION(patch_vertices, 0x373); -ASSERT_REG_POSITION(fragment_barrier, 0x378); -ASSERT_REG_POSITION(scissor_test, 0x380); -ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); -ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); -ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); -ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x3DD); -ASSERT_REG_POSITION(tiled_cache_barrier, 0x3DF); -ASSERT_REG_POSITION(color_mask_common, 0x3E4); -ASSERT_REG_POSITION(depth_bounds, 0x3E7); -ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); -ASSERT_REG_POSITION(multisample_raster_enable, 0x3ED); -ASSERT_REG_POSITION(multisample_raster_samples, 0x3EE); -ASSERT_REG_POSITION(multisample_sample_mask, 0x3EF); -ASSERT_REG_POSITION(zeta, 0x3F8); -ASSERT_REG_POSITION(render_area, 0x3FD); -ASSERT_REG_POSITION(clear_flags, 0x43E); -ASSERT_REG_POSITION(fill_rectangle, 0x44F); -ASSERT_REG_POSITION(conservative_raster_enable, 0x452); -ASSERT_REG_POSITION(vertex_attrib_format, 0x458); -ASSERT_REG_POSITION(multisample_sample_locations, 0x478); -ASSERT_REG_POSITION(multisample_coverage_to_color, 0x47E); -ASSERT_REG_POSITION(rt_control, 0x487); -ASSERT_REG_POSITION(zeta_width, 0x48a); -ASSERT_REG_POSITION(zeta_height, 0x48b); -ASSERT_REG_POSITION(zeta_depth, 0x48c); -ASSERT_REG_POSITION(sampler_index, 0x48D); -ASSERT_REG_POSITION(gp_passthrough_mask, 0x490); -ASSERT_REG_POSITION(depth_test_enable, 0x4B3); -ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); -ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); -ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); -ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); -ASSERT_REG_POSITION(depth_test_func, 0x4C3); -ASSERT_REG_POSITION(alpha_test_ref, 0x4C4); -ASSERT_REG_POSITION(alpha_test_func, 0x4C5); -ASSERT_REG_POSITION(draw_tfb_stride, 0x4C6); -ASSERT_REG_POSITION(blend_color, 0x4C7); -ASSERT_REG_POSITION(blend, 0x4CF); -ASSERT_REG_POSITION(stencil_enable, 0x4E0); -ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1); -ASSERT_REG_POSITION(stencil_front_op_zfail, 0x4E2); -ASSERT_REG_POSITION(stencil_front_op_zpass, 0x4E3); -ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4); -ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); -ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); -ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); -ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); -ASSERT_REG_POSITION(screen_y_control, 0x4EB); -ASSERT_REG_POSITION(line_width_smooth, 0x4EC); -ASSERT_REG_POSITION(line_width_aliased, 0x4ED); -ASSERT_REG_POSITION(invalidate_sampler_cache_no_wfi, 0x509); -ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x50A); -ASSERT_REG_POSITION(vb_element_base, 0x50D); -ASSERT_REG_POSITION(vb_base_instance, 0x50E); -ASSERT_REG_POSITION(clip_distance_enabled, 0x544); -ASSERT_REG_POSITION(samplecnt_enable, 0x545); -ASSERT_REG_POSITION(point_size, 0x546); -ASSERT_REG_POSITION(point_sprite_enable, 0x548); -ASSERT_REG_POSITION(counter_reset, 0x54C); -ASSERT_REG_POSITION(multisample_enable, 0x54D); -ASSERT_REG_POSITION(zeta_enable, 0x54E); -ASSERT_REG_POSITION(multisample_control, 0x54F); -ASSERT_REG_POSITION(condition, 0x554); -ASSERT_REG_POSITION(tsc, 0x557); -ASSERT_REG_POSITION(polygon_offset_factor, 0x55B); -ASSERT_REG_POSITION(line_smooth_enable, 0x55C); -ASSERT_REG_POSITION(tic, 0x55D); -ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); -ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); -ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); -ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); -ASSERT_REG_POSITION(stencil_back_func_func, 0x569); -ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); -ASSERT_REG_POSITION(polygon_offset_units, 0x56F); -ASSERT_REG_POSITION(multisample_mode, 0x574); -ASSERT_REG_POSITION(point_coord_replace, 0x581); -ASSERT_REG_POSITION(code_address, 0x582); -ASSERT_REG_POSITION(draw, 0x585); -ASSERT_REG_POSITION(primitive_restart, 0x591); -ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); -ASSERT_REG_POSITION(index_array, 0x5F2); -ASSERT_REG_POSITION(small_index, 0x5F9); -ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); -ASSERT_REG_POSITION(instanced_arrays, 0x620); -ASSERT_REG_POSITION(vp_point_size, 0x644); -ASSERT_REG_POSITION(cull_test_enabled, 0x646); -ASSERT_REG_POSITION(front_face, 0x647); -ASSERT_REG_POSITION(cull_face, 0x648); -ASSERT_REG_POSITION(pixel_center_integer, 0x649); -ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); -ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); -ASSERT_REG_POSITION(topology_override, 0x65C); -ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); -ASSERT_REG_POSITION(logic_op, 0x671); -ASSERT_REG_POSITION(clear_buffers, 0x674); -ASSERT_REG_POSITION(color_mask, 0x680); -ASSERT_REG_POSITION(query, 0x6C0); -ASSERT_REG_POSITION(vertex_array[0], 0x700); -ASSERT_REG_POSITION(independent_blend, 0x780); -ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0); -ASSERT_REG_POSITION(shader_config[0], 0x800); -ASSERT_REG_POSITION(firmware, 0x8C0); -ASSERT_REG_POSITION(const_buffer, 0x8E0); -ASSERT_REG_POSITION(cb_bind[0], 0x904); -ASSERT_REG_POSITION(tex_cb_index, 0x982); -ASSERT_REG_POSITION(tfb_varying_locs, 0xA00); -ASSERT_REG_POSITION(ssbo_info, 0xD18); -ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A); -ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F); +ASSERT_REG_POSITION(object_id, 0x0000); +ASSERT_REG_POSITION(nop, 0x0100); +ASSERT_REG_POSITION(notify, 0x0104); +ASSERT_REG_POSITION(wait_for_idle, 0x0110); +ASSERT_REG_POSITION(load_mme, 0x0114); +ASSERT_REG_POSITION(shadow_ram_control, 0x0124); +ASSERT_REG_POSITION(peer, 0x0128); +ASSERT_REG_POSITION(global_render, 0x0130); +ASSERT_REG_POSITION(go_idle, 0x013C); +ASSERT_REG_POSITION(trigger, 0x0140); +ASSERT_REG_POSITION(trigger_wfi, 0x0144); +ASSERT_REG_POSITION(instrumentation_method_header, 0x0150); +ASSERT_REG_POSITION(instrumentation_method_data, 0x0154); +ASSERT_REG_POSITION(upload, 0x0180); +ASSERT_REG_POSITION(launch_dma, 0x01B0); +ASSERT_REG_POSITION(inline_data, 0x01B4); +ASSERT_REG_POSITION(i2m, 0x01DC); +ASSERT_REG_POSITION(run_ds_now, 0x0200); +ASSERT_REG_POSITION(opportunistic_early_z, 0x0204); +ASSERT_REG_POSITION(aliased_line_width_enabled, 0x020C); +ASSERT_REG_POSITION(mandated_early_z, 0x0210); +ASSERT_REG_POSITION(gs_dm_fifo, 0x0214); +ASSERT_REG_POSITION(l2_cache_control, 0x0218); +ASSERT_REG_POSITION(invalidate_shader_cache, 0x021C); +ASSERT_REG_POSITION(sync_info, 0x02C8); +ASSERT_REG_POSITION(prim_circular_buffer_throttle, 0x02D0); +ASSERT_REG_POSITION(flush_invalidate_rop_mini_cache, 0x02D4); +ASSERT_REG_POSITION(surface_clip_block_id, 0x02D8); +ASSERT_REG_POSITION(alpha_circular_buffer_size, 0x02DC); +ASSERT_REG_POSITION(decompress_surface, 0x02E0); +ASSERT_REG_POSITION(zcull_rop_bypass, 0x02E4); +ASSERT_REG_POSITION(zcull_subregion, 0x02E8); +ASSERT_REG_POSITION(raster_bounding_box, 0x02EC); +ASSERT_REG_POSITION(peer_semaphore_release, 0x02F0); +ASSERT_REG_POSITION(iterated_blend_optimization, 0x02F4); +ASSERT_REG_POSITION(zcull_subregion_allocation, 0x02F8); +ASSERT_REG_POSITION(zcull_subregion_algorithm, 0x02FC); +ASSERT_REG_POSITION(ps_output_sample_mask_usage, 0x0300); +ASSERT_REG_POSITION(draw_zero_index, 0x0304); +ASSERT_REG_POSITION(l1_configuration, 0x0308); +ASSERT_REG_POSITION(render_enable_control_load_const_buffer, 0x030C); +ASSERT_REG_POSITION(spa_version, 0x0310); +ASSERT_REG_POSITION(ieee_clean_update, 0x0314); +ASSERT_REG_POSITION(snap_grid, 0x0318); +ASSERT_REG_POSITION(tessellation, 0x0320); +ASSERT_REG_POSITION(sub_tiling_perf, 0x0360); +ASSERT_REG_POSITION(zcull_subregion_report, 0x036C); +ASSERT_REG_POSITION(balanced_primitive_workload, 0x0374); +ASSERT_REG_POSITION(max_patches_per_batch, 0x0378); +ASSERT_REG_POSITION(rasterize_enable, 0x037C); +ASSERT_REG_POSITION(transform_feedback, 0x0380); +ASSERT_REG_POSITION(transform_feedback.controls, 0x0700); +ASSERT_REG_POSITION(raster_input, 0x0740); +ASSERT_REG_POSITION(transform_feedback_enabled, 0x0744); +ASSERT_REG_POSITION(primitive_restart_topology_change_enable, 0x0748); +ASSERT_REG_POSITION(alpha_fraction, 0x074C); +ASSERT_REG_POSITION(hybrid_aa_control, 0x0754); +ASSERT_REG_POSITION(shader_local_memory, 0x077C); +ASSERT_REG_POSITION(color_zero_bandwidth_clear, 0x07A4); +ASSERT_REG_POSITION(z_zero_bandwidth_clear, 0x07A8); +ASSERT_REG_POSITION(isbe_save_restore_program_offset, 0x07AC); +ASSERT_REG_POSITION(zcull_region, 0x07C0); +ASSERT_REG_POSITION(zeta_read_only, 0x07F8); +ASSERT_REG_POSITION(rt, 0x0800); +ASSERT_REG_POSITION(viewport_transform, 0x0A00); +ASSERT_REG_POSITION(viewports, 0x0C00); +ASSERT_REG_POSITION(windows, 0x0D00); +ASSERT_REG_POSITION(clip_id_extent, 0x0D40); +ASSERT_REG_POSITION(max_geometry_instances_per_task, 0x0D60); +ASSERT_REG_POSITION(visible_call_limit, 0x0D64); +ASSERT_REG_POSITION(statistics_count, 0x0D68); +ASSERT_REG_POSITION(clear_rect, 0x0D6C); +ASSERT_REG_POSITION(vertex_buffer, 0x0D74); +ASSERT_REG_POSITION(depth_mode, 0x0D7C); +ASSERT_REG_POSITION(clear_color, 0x0D80); +ASSERT_REG_POSITION(clear_depth, 0x0D90); +ASSERT_REG_POSITION(shader_cache_icache_prefetch, 0x0D94); +ASSERT_REG_POSITION(force_transition_to_beta, 0x0D98); +ASSERT_REG_POSITION(reduce_colour_thresholds, 0x0D9C); +ASSERT_REG_POSITION(clear_stencil, 0x0DA0); +ASSERT_REG_POSITION(invalidate_shader_cache_no_wfi, 0x0DA4); +ASSERT_REG_POSITION(zcull_serialization, 0x0DA8); +ASSERT_REG_POSITION(polygon_mode_front, 0x0DAC); +ASSERT_REG_POSITION(polygon_mode_back, 0x0DB0); +ASSERT_REG_POSITION(polygon_smooth, 0x0DB4); +ASSERT_REG_POSITION(zeta_mark_clean_ieee, 0x0DB8); +ASSERT_REG_POSITION(zcull_dir_format, 0x0DBC); +ASSERT_REG_POSITION(polygon_offset_point_enable, 0x0DC0); +ASSERT_REG_POSITION(polygon_offset_line_enable, 0x0DC4); +ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x0DC8); +ASSERT_REG_POSITION(patch_vertices, 0x0DCC); +ASSERT_REG_POSITION(iterated_blend, 0x0DD0); +ASSERT_REG_POSITION(zcull_criteria, 0x0DD8); +ASSERT_REG_POSITION(fragment_barrier, 0x0DE0); +ASSERT_REG_POSITION(sm_timeout, 0x0DE4); +ASSERT_REG_POSITION(primitive_restart_array, 0x0DE8); +ASSERT_REG_POSITION(load_iterated_blend, 0x0DF0); +ASSERT_REG_POSITION(window_offset_x, 0x0DF8); +ASSERT_REG_POSITION(window_offset_y, 0x0DFC); +ASSERT_REG_POSITION(scissor_test, 0x0E00); +ASSERT_REG_POSITION(select_texture_headers, 0x0F10); +ASSERT_REG_POSITION(vpc_perf, 0x0F14); +ASSERT_REG_POSITION(pm_local_trigger, 0x0F18); +ASSERT_REG_POSITION(post_z_pixel_imask, 0x0F1C); +ASSERT_REG_POSITION(const_color_rendering, 0x0F40); +ASSERT_REG_POSITION(stencil_back_func, 0x0F54); +ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84); +ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C); +ASSERT_REG_POSITION(color_mask_common, 0x0F90); +ASSERT_REG_POSITION(vtg_warp_watermarks, 0x0F98); +ASSERT_REG_POSITION(depth_bounds, 0x0F9C); +ASSERT_REG_POSITION(sample_mask_target, 0x0FA4); +ASSERT_REG_POSITION(color_target_mrt_enable, 0x0FAC); +ASSERT_REG_POSITION(non_multisampled_z, 0x0FB0); +ASSERT_REG_POSITION(tir_mode, 0x0FB4); +ASSERT_REG_POSITION(anti_alias_raster, 0x0FB8); +ASSERT_REG_POSITION(sample_mask_pos, 0x0FBC); +ASSERT_REG_POSITION(surface_clip_id_memory, 0x0FCC); +ASSERT_REG_POSITION(tir_modulation, 0x0FD4); +ASSERT_REG_POSITION(blend_control_allow_float_pixel_kills, 0x0FDC); +ASSERT_REG_POSITION(zeta, 0x0FE0); +ASSERT_REG_POSITION(surface_clip, 0x0FF4); +ASSERT_REG_POSITION(tiled_cache_treat_heavy_as_light, 0x0FFC); +ASSERT_REG_POSITION(l2_cache_vaf, 0x1000); +ASSERT_REG_POSITION(viewport_multicast, 0x1004); +ASSERT_REG_POSITION(tessellation_cut_height, 0x1008); +ASSERT_REG_POSITION(max_gs_instances_per_task, 0x100C); +ASSERT_REG_POSITION(max_gs_output_vertices_per_task, 0x1010); +ASSERT_REG_POSITION(reserved_sw_method0, 0x1014); +ASSERT_REG_POSITION(gs_output_cb_storage_multiplier, 0x1018); +ASSERT_REG_POSITION(beta_cb_storage_constant, 0x101C); +ASSERT_REG_POSITION(ti_output_cb_storage_multiplier, 0x1020); +ASSERT_REG_POSITION(alpha_cb_storage_constraint, 0x1024); +ASSERT_REG_POSITION(reserved_sw_method1, 0x1028); +ASSERT_REG_POSITION(reserved_sw_method2, 0x102C); +ASSERT_REG_POSITION(tir_modulation_coeff, 0x1030); +ASSERT_REG_POSITION(spare_nop, 0x1044); +ASSERT_REG_POSITION(reserved_sw_method3_to_7, 0x10B0); +ASSERT_REG_POSITION(reduce_color_thresholds_unorm8, 0x10CC); +ASSERT_REG_POSITION(reserved_sw_method10_to_13, 0x10D0); +ASSERT_REG_POSITION(reduce_color_thresholds_unorm10, 0x10E0); +ASSERT_REG_POSITION(reduce_color_thresholds_unorm16, 0x10E4); +ASSERT_REG_POSITION(reduce_color_thresholds_fp11, 0x10E8); +ASSERT_REG_POSITION(reduce_color_thresholds_fp16, 0x10EC); +ASSERT_REG_POSITION(reduce_color_thresholds_srgb8, 0x10F0); +ASSERT_REG_POSITION(unbind_all_constant_buffers, 0x10F4); +ASSERT_REG_POSITION(clear_control, 0x10F8); +ASSERT_REG_POSITION(l2_cache_rop_non_interlocked_reads, 0x10FC); +ASSERT_REG_POSITION(reserved_sw_method14, 0x1100); +ASSERT_REG_POSITION(reserved_sw_method15, 0x1104); +ASSERT_REG_POSITION(no_operation_data_high, 0x110C); +ASSERT_REG_POSITION(depth_bias_control, 0x1110); +ASSERT_REG_POSITION(pm_trigger_end, 0x1114); +ASSERT_REG_POSITION(vertex_id_base, 0x1118); +ASSERT_REG_POSITION(stencil_compression_enabled, 0x111C); +ASSERT_REG_POSITION(vertex_output_attribute_skip_masks, 0x1120); +ASSERT_REG_POSITION(tir_control, 0x1130); +ASSERT_REG_POSITION(mutable_method_treat_mutable_as_heavy, 0x1134); +ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138); +ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C); +ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140); +ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144); +ASSERT_REG_POSITION(vertex_attrib_format, 0x1160); +ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0); +ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0); +ASSERT_REG_POSITION(force_heavy_method_sync, 0x11F4); +ASSERT_REG_POSITION(multisample_coverage_to_color, 0x11F8); +ASSERT_REG_POSITION(decompress_zeta_surface, 0x11FC); +ASSERT_REG_POSITION(zeta_sparse, 0x1208); +ASSERT_REG_POSITION(invalidate_sampler_cache, 0x120C); +ASSERT_REG_POSITION(invalidate_texture_header_cache, 0x1210); +ASSERT_REG_POSITION(vertex_array_instance_first, 0x1214); +ASSERT_REG_POSITION(vertex_array_instance_subsequent, 0x1218); +ASSERT_REG_POSITION(rt_control, 0x121C); +ASSERT_REG_POSITION(compression_threshold_samples, 0x1220); +ASSERT_REG_POSITION(ps_interlock_control, 0x1224); +ASSERT_REG_POSITION(zeta_size, 0x1228); +ASSERT_REG_POSITION(sampler_binding, 0x1234); +ASSERT_REG_POSITION(draw_auto_byte_count, 0x123C); +ASSERT_REG_POSITION(post_vtg_shader_attrib_skip_mask, 0x1240); +ASSERT_REG_POSITION(ps_ticket_dispenser_value, 0x1260); +ASSERT_REG_POSITION(circular_buffer_size, 0x1280); +ASSERT_REG_POSITION(vtg_register_watermarks, 0x1284); +ASSERT_REG_POSITION(invalidate_texture_cache_no_wfi, 0x1288); +ASSERT_REG_POSITION(l2_cache_rop_interlocked_reads, 0x1290); +ASSERT_REG_POSITION(primitive_restart_topology_change_index, 0x12A4); +ASSERT_REG_POSITION(zcull_region_enable, 0x12C8); +ASSERT_REG_POSITION(depth_test_enable, 0x12CC); +ASSERT_REG_POSITION(fill_mode, 0x12D0); +ASSERT_REG_POSITION(shade_mode, 0x12D4); +ASSERT_REG_POSITION(l2_cache_rop_non_interlocked_writes, 0x12D8); +ASSERT_REG_POSITION(l2_cache_rop_interlocked_writes, 0x12DC); +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(d3d_cull_mode, 0x1308); +ASSERT_REG_POSITION(depth_test_func, 0x130C); +ASSERT_REG_POSITION(alpha_test_ref, 0x1310); +ASSERT_REG_POSITION(alpha_test_func, 0x1314); +ASSERT_REG_POSITION(draw_auto_stride, 0x1318); +ASSERT_REG_POSITION(blend_color, 0x131C); +ASSERT_REG_POSITION(invalidate_sampler_cache_lines, 0x1330); +ASSERT_REG_POSITION(invalidate_texture_header_cache_lines, 0x1334); +ASSERT_REG_POSITION(invalidate_texture_data_cache_lines, 0x1338); +ASSERT_REG_POSITION(blend, 0x133C); +ASSERT_REG_POSITION(stencil_enable, 0x1380); +ASSERT_REG_POSITION(stencil_front_op, 0x1384); +ASSERT_REG_POSITION(stencil_front_func, 0x1394); +ASSERT_REG_POSITION(draw_auto_start_byte_count, 0x13A4); +ASSERT_REG_POSITION(frag_color_clamp, 0x13A8); +ASSERT_REG_POSITION(window_origin, 0x13AC); +ASSERT_REG_POSITION(line_width_smooth, 0x13B0); +ASSERT_REG_POSITION(line_width_aliased, 0x13B4); +ASSERT_REG_POSITION(line_override_multisample, 0x1418); +ASSERT_REG_POSITION(alpha_hysteresis_rounds, 0x1420); +ASSERT_REG_POSITION(invalidate_sampler_cache_no_wfi, 0x1424); +ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x1428); +ASSERT_REG_POSITION(global_base_vertex_index, 0x1434); +ASSERT_REG_POSITION(global_base_instance_index, 0x1438); +ASSERT_REG_POSITION(ps_warp_watermarks, 0x1450); +ASSERT_REG_POSITION(ps_regster_watermarks, 0x1454); +ASSERT_REG_POSITION(store_zcull, 0x1464); +ASSERT_REG_POSITION(iterated_blend_constants, 0x1480); +ASSERT_REG_POSITION(load_zcull, 0x1500); +ASSERT_REG_POSITION(surface_clip_id_height, 0x1504); +ASSERT_REG_POSITION(surface_clip_id_clear_rect, 0x1508); +ASSERT_REG_POSITION(user_clip_enable, 0x1510); +ASSERT_REG_POSITION(zpass_pixel_count_enable, 0x1514); +ASSERT_REG_POSITION(point_size, 0x1518); +ASSERT_REG_POSITION(zcull_stats_enable, 0x151C); +ASSERT_REG_POSITION(point_sprite_enable, 0x1520); +ASSERT_REG_POSITION(shader_exceptions_enable, 0x1528); +ASSERT_REG_POSITION(clear_report_value, 0x1530); +ASSERT_REG_POSITION(anti_alias_enable, 0x1534); +ASSERT_REG_POSITION(zeta_enable, 0x1538); +ASSERT_REG_POSITION(anti_alias_alpha_control, 0x153C); +ASSERT_REG_POSITION(render_enable, 0x1550); +ASSERT_REG_POSITION(tex_sampler, 0x155C); +ASSERT_REG_POSITION(slope_scale_depth_bias, 0x156C); +ASSERT_REG_POSITION(line_anti_alias_enable, 0x1570); +ASSERT_REG_POSITION(tex_header, 0x1574); +ASSERT_REG_POSITION(active_zcull_region_id, 0x1590); +ASSERT_REG_POSITION(stencil_two_side_enable, 0x1594); +ASSERT_REG_POSITION(stencil_back_op, 0x1598); +ASSERT_REG_POSITION(framebuffer_srgb, 0x15B8); +ASSERT_REG_POSITION(depth_bias, 0x15BC); +ASSERT_REG_POSITION(zcull_region_format, 0x15C8); +ASSERT_REG_POSITION(rt_layer, 0x15CC); +ASSERT_REG_POSITION(anti_alias_samples_mode, 0x15D0); +ASSERT_REG_POSITION(edge_flag, 0x15E4); +ASSERT_REG_POSITION(draw_inline_index, 0x15E8); +ASSERT_REG_POSITION(inline_index_2x16, 0x15EC); +ASSERT_REG_POSITION(vertex_global_base_offset, 0x15F4); +ASSERT_REG_POSITION(zcull_region_pixel_offset, 0x15FC); +ASSERT_REG_POSITION(point_sprite, 0x1604); +ASSERT_REG_POSITION(program_region, 0x1608); +ASSERT_REG_POSITION(default_attributes, 0x1610); +ASSERT_REG_POSITION(draw, 0x1614); +ASSERT_REG_POSITION(vertex_id_copy, 0x161C); +ASSERT_REG_POSITION(add_to_primitive_id, 0x1620); +ASSERT_REG_POSITION(load_to_primitive_id, 0x1624); +ASSERT_REG_POSITION(shader_based_cull, 0x162C); +ASSERT_REG_POSITION(class_version, 0x1638); +ASSERT_REG_POSITION(primitive_restart, 0x1644); +ASSERT_REG_POSITION(output_vertex_id, 0x164C); +ASSERT_REG_POSITION(anti_alias_point_enable, 0x1658); +ASSERT_REG_POSITION(point_center_mode, 0x165C); +ASSERT_REG_POSITION(line_smooth_params, 0x1668); +ASSERT_REG_POSITION(line_stipple_enable, 0x166C); +ASSERT_REG_POSITION(line_smooth_edge_table, 0x1670); +ASSERT_REG_POSITION(line_stipple_params, 0x1680); +ASSERT_REG_POSITION(provoking_vertex, 0x1684); +ASSERT_REG_POSITION(two_sided_light_enabled, 0x1688); +ASSERT_REG_POSITION(polygon_stipple_enabled, 0x168C); +ASSERT_REG_POSITION(shader_control, 0x1690); +ASSERT_REG_POSITION(class_version_check, 0x16A0); +ASSERT_REG_POSITION(sph_version, 0x16A4); +ASSERT_REG_POSITION(sph_version_check, 0x16A8); +ASSERT_REG_POSITION(alpha_to_coverage_override, 0x16B4); +ASSERT_REG_POSITION(polygon_stipple_pattern, 0x1700); +ASSERT_REG_POSITION(aam_version, 0x1790); +ASSERT_REG_POSITION(aam_version_check, 0x1794); +ASSERT_REG_POSITION(zeta_layer_offset, 0x179C); +ASSERT_REG_POSITION(index_buffer, 0x17C8); +ASSERT_REG_POSITION(index_buffer32_first, 0x17E4); +ASSERT_REG_POSITION(index_buffer16_first, 0x17E8); +ASSERT_REG_POSITION(index_buffer8_first, 0x17EC); +ASSERT_REG_POSITION(index_buffer32_subsequent, 0x17F0); +ASSERT_REG_POSITION(index_buffer16_subsequent, 0x17F4); +ASSERT_REG_POSITION(index_buffer8_subsequent, 0x17F8); +ASSERT_REG_POSITION(depth_bias_clamp, 0x187C); +ASSERT_REG_POSITION(vertex_stream_instances, 0x1880); +ASSERT_REG_POSITION(point_size_attribute, 0x1910); +ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918); +ASSERT_REG_POSITION(gl_front_face, 0x191C); +ASSERT_REG_POSITION(gl_cull_face, 0x1920); +ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); +ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C); +ASSERT_REG_POSITION(viewport_clip_control, 0x193C); +ASSERT_REG_POSITION(user_clip_op, 0x1940); +ASSERT_REG_POSITION(render_enable_override, 0x1944); +ASSERT_REG_POSITION(primitive_topology_control, 0x1948); +ASSERT_REG_POSITION(window_clip_enable, 0x194C); +ASSERT_REG_POSITION(invalidate_zcull, 0x1958); +ASSERT_REG_POSITION(zcull, 0x1968); +ASSERT_REG_POSITION(topology_override, 0x1970); +ASSERT_REG_POSITION(zcull_sync, 0x1978); +ASSERT_REG_POSITION(clip_id_test_enable, 0x197C); +ASSERT_REG_POSITION(surface_clip_id_width, 0x1980); +ASSERT_REG_POSITION(clip_id, 0x1984); +ASSERT_REG_POSITION(depth_bounds_enable, 0x19BC); +ASSERT_REG_POSITION(blend_float_zero_times_anything_is_zero, 0x19C0); +ASSERT_REG_POSITION(logic_op, 0x19C4); +ASSERT_REG_POSITION(z_compression_enable, 0x19CC); +ASSERT_REG_POSITION(clear_surface, 0x19D0); +ASSERT_REG_POSITION(clear_clip_id_surface, 0x19D4); +ASSERT_REG_POSITION(color_compression_enable, 0x19E0); +ASSERT_REG_POSITION(color_mask, 0x1A00); +ASSERT_REG_POSITION(pipe_nop, 0x1A2C); +ASSERT_REG_POSITION(spare, 0x1A30); +ASSERT_REG_POSITION(report_semaphore, 0x1B00); +ASSERT_REG_POSITION(vertex_streams, 0x1C00); +ASSERT_REG_POSITION(blend_per_target, 0x1E00); +ASSERT_REG_POSITION(vertex_stream_limits, 0x1F00); +ASSERT_REG_POSITION(pipelines, 0x2000); +ASSERT_REG_POSITION(falcon, 0x2300); +ASSERT_REG_POSITION(const_buffer, 0x2380); +ASSERT_REG_POSITION(bind_groups, 0x2400); +ASSERT_REG_POSITION(color_clamp_enable, 0x2600); +ASSERT_REG_POSITION(bindless_texture_const_buffer_slot, 0x2608); +ASSERT_REG_POSITION(trap_handler, 0x260C); +ASSERT_REG_POSITION(stream_out_layout, 0x2800); +ASSERT_REG_POSITION(shader_performance, 0x333C); +ASSERT_REG_POSITION(shadow_scratch, 0x3400); #undef ASSERT_REG_POSITION diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 0a4a8b14fa..d0709dc696 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -24,11 +24,15 @@ namespace Tegra { class DmaPusher; struct CommandList; +// TODO: Implement the commented ones enum class RenderTargetFormat : u32 { NONE = 0x0, R32B32G32A32_FLOAT = 0xC0, R32G32B32A32_SINT = 0xC1, R32G32B32A32_UINT = 0xC2, + // R32G32B32X32_FLOAT = 0xC3, + // R32G32B32X32_SINT = 0xC4, + // R32G32B32X32_UINT = 0xC5, R16G16B16A16_UNORM = 0xC6, R16G16B16A16_SNORM = 0xC7, R16G16B16A16_SINT = 0xC8, @@ -38,8 +42,8 @@ enum class RenderTargetFormat : u32 { R32G32_SINT = 0xCC, R32G32_UINT = 0xCD, R16G16B16X16_FLOAT = 0xCE, - B8G8R8A8_UNORM = 0xCF, - B8G8R8A8_SRGB = 0xD0, + A8R8G8B8_UNORM = 0xCF, + A8R8G8B8_SRGB = 0xD0, A2B10G10R10_UNORM = 0xD1, A2B10G10R10_UINT = 0xD2, A8B8G8R8_UNORM = 0xD5, @@ -52,10 +56,13 @@ enum class RenderTargetFormat : u32 { R16G16_SINT = 0xDC, R16G16_UINT = 0xDD, R16G16_FLOAT = 0xDE, + // A2R10G10B10_UNORM = 0xDF, B10G11R11_FLOAT = 0xE0, R32_SINT = 0xE3, R32_UINT = 0xE4, R32_FLOAT = 0xE5, + // X8R8G8B8_UNORM = 0xE6, + // X8R8G8B8_SRGB = 0xE7, R5G6B5_UNORM = 0xE8, A1R5G5B5_UNORM = 0xE9, R8G8_UNORM = 0xEA, @@ -71,17 +78,42 @@ enum class RenderTargetFormat : u32 { R8_SNORM = 0xF4, R8_SINT = 0xF5, R8_UINT = 0xF6, + + /* + A8_UNORM = 0xF7, + X1R5G5B5_UNORM = 0xF8, + X8B8G8R8_UNORM = 0xF9, + X8B8G8R8_SRGB = 0xFA, + Z1R5G5B5_UNORM = 0xFB, + O1R5G5B5_UNORM = 0xFC, + Z8R8G8B8_UNORM = 0xFD, + O8R8G8B8_UNORM = 0xFE, + R32_UNORM = 0xFF, + A16_UNORM = 0x40, + A16_FLOAT = 0x41, + A32_FLOAT = 0x42, + A8R8_UNORM = 0x43, + R16A16_UNORM = 0x44, + R16A16_FLOAT = 0x45, + R32A32_FLOAT = 0x46, + B8G8R8A8_UNORM = 0x47, + */ }; enum class DepthFormat : u32 { - D32_FLOAT = 0xA, - D16_UNORM = 0x13, - S8_UINT_Z24_UNORM = 0x14, - D24X8_UNORM = 0x15, - D24S8_UNORM = 0x16, + Z32_FLOAT = 0xA, + Z16_UNORM = 0x13, + Z24_UNORM_S8_UINT = 0x14, + X8Z24_UNORM = 0x15, + S8Z24_UNORM = 0x16, S8_UINT = 0x17, - D24C8_UNORM = 0x18, - D32_FLOAT_S8X24_UINT = 0x19, + V8Z24_UNORM = 0x18, + Z32_FLOAT_X24S8_UINT = 0x19, + /* + X8Z24_UNORM_X16V8S8_UINT = 0x1D, + Z32_FLOAT_X16V8X8_UINT = 0x1E, + Z32_FLOAT_X16V8S8_UINT = 0x1F, + */ }; namespace Engines { diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index cabe8dcbf1..8a8adbb42f 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -21,16 +21,16 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector& maxwell3d.regs.draw.topology.Assign( static_cast(parameters[0] & 0x3ffffff)); - maxwell3d.regs.vb_base_instance = parameters[5]; + maxwell3d.regs.global_base_instance_index = parameters[5]; maxwell3d.mme_draw.instance_count = instance_count; - maxwell3d.regs.vb_element_base = parameters[3]; - maxwell3d.regs.index_array.count = parameters[1]; - maxwell3d.regs.index_array.first = parameters[4]; + 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.regs.index_array.count = 0; + maxwell3d.regs.index_buffer.count = 0; maxwell3d.mme_draw.instance_count = 0; maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } @@ -40,7 +40,7 @@ void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector& maxwell3d.regs.vertex_buffer.first = parameters[3]; maxwell3d.regs.vertex_buffer.count = parameters[1]; - maxwell3d.regs.vb_base_instance = parameters[4]; + maxwell3d.regs.global_base_instance_index = parameters[4]; maxwell3d.regs.draw.topology.Assign( static_cast(parameters[0])); maxwell3d.mme_draw.instance_count = count; @@ -57,12 +57,12 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); const u32 element_base = parameters[4]; const u32 base_instance = parameters[5]; - maxwell3d.regs.index_array.first = parameters[3]; - maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? - maxwell3d.regs.index_array.count = parameters[1]; + maxwell3d.regs.index_buffer.first = parameters[3]; + maxwell3d.regs.vertex_id_base = element_base; + maxwell3d.regs.index_buffer.count = parameters[1]; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - maxwell3d.regs.vb_element_base = element_base; - maxwell3d.regs.vb_base_instance = base_instance; + 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); @@ -72,10 +72,10 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& if (maxwell3d.ShouldExecute()) { maxwell3d.Rasterizer().Draw(true, true); } - maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? - maxwell3d.regs.index_array.count = 0; - maxwell3d.regs.vb_element_base = 0x0; - maxwell3d.regs.vb_base_instance = 0x0; + 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); @@ -87,10 +87,10 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { SCOPE_EXIT({ // Clean everything. - maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? - maxwell3d.regs.index_array.count = 0; - maxwell3d.regs.vb_element_base = 0x0; - maxwell3d.regs.vb_base_instance = 0x0; + 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); @@ -122,11 +122,11 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector& const u32 first_index = parameters[base + 2]; const u32 base_vertex = parameters[base + 3]; const u32 base_instance = parameters[base + 4]; - maxwell3d.regs.index_array.first = first_index; - maxwell3d.regs.reg_array[0x446] = base_vertex; - maxwell3d.regs.index_array.count = num_vertices; - maxwell3d.regs.vb_element_base = base_vertex; - maxwell3d.regs.vb_base_instance = base_instance; + maxwell3d.regs.index_buffer.first = first_index; + maxwell3d.regs.vertex_id_base = base_vertex; + 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); diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index b0ebe71b74..00ce53e3e6 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -137,7 +137,7 @@ public: std::unique_lock lock{mutex}; if (maxwell3d) { const auto& regs = maxwell3d->regs; - Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); + Stream(VideoCore::QueryType::SamplesPassed).Update(regs.zpass_pixel_count_enable); } } diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 41493a7da2..1d20a79ec6 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -73,8 +73,8 @@ GLenum AssemblyStage(size_t stage_index) { /// @param location Hardware location /// @return Pair of ARB_transform_feedback3 token stream first and third arguments /// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt -std::pair TransformFeedbackEnum(u8 location) { - const u8 index = location / 4; +std::pair TransformFeedbackEnum(u32 location) { + const auto index = location / 4; if (index >= 8 && index <= 39) { return {GL_GENERIC_ATTRIB_NV, index - 8}; } @@ -286,7 +286,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.runtime.SetEnableStorageBuffers(use_storage_buffers); const auto& regs{maxwell3d->regs}; - const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const Shader::Info& info{stage_infos[stage]}; buffer_cache.UnbindGraphicsStorageBuffers(stage); @@ -557,10 +557,25 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { ++current_stream; const auto& locations = key.xfb_state.varyings[feedback]; - std::optional current_index; + std::optional current_index; for (u32 offset = 0; offset < layout.varying_count; ++offset) { - const u8 location = locations[offset]; - const u8 index = location / 4; + const auto get_attribute = [&locations](u32 index) -> u32 { + switch (index % 4) { + case 0: + return locations[index / 4].attribute0.Value(); + case 1: + return locations[index / 4].attribute1.Value(); + case 2: + return locations[index / 4].attribute2.Value(); + case 3: + return locations[index / 4].attribute3.Value(); + } + UNREACHABLE(); + return 0; + }; + + const auto attribute{get_attribute(offset)}; + const auto index = attribute / 4U; if (current_index == index) { // Increase number of components of the previous attachment @@ -569,7 +584,7 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { } current_index = index; - std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location); + std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(attribute); cursor[1] = 1; cursor += XFB_ENTRY_STRIDE; } diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index a0f0e63cba..ea53ddb460 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -37,8 +37,8 @@ struct GraphicsPipelineKey { BitField<0, 1, u32> xfb_enabled; BitField<1, 1, u32> early_z; BitField<2, 4, Maxwell::PrimitiveTopology> gs_input_topology; - BitField<6, 2, Maxwell::TessellationPrimitive> tessellation_primitive; - BitField<8, 2, Maxwell::TessellationSpacing> tessellation_spacing; + BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive; + BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing; BitField<10, 1, u32> tessellation_clockwise; }; std::array padding; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c2d80605d2..cce00cea8b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -87,7 +87,7 @@ void RasterizerOpenGL::SyncVertexFormats() { } flags[Dirty::VertexFormat0 + index] = false; - const auto attrib = maxwell3d->regs.vertex_attrib_format[index]; + const auto& attrib = maxwell3d->regs.vertex_attrib_format[index]; const auto gl_index = static_cast(index); // Disable constant attributes. @@ -97,8 +97,8 @@ void RasterizerOpenGL::SyncVertexFormats() { } glEnableVertexAttribArray(gl_index); - if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || - attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { + if (attrib.type == Maxwell::VertexAttribute::Type::SInt || + attrib.type == Maxwell::VertexAttribute::Type::UInt) { glVertexAttribIFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexFormat(attrib), attrib.offset); } else { @@ -125,8 +125,8 @@ void RasterizerOpenGL::SyncVertexInstances() { flags[Dirty::VertexInstance0 + index] = false; const auto gl_index = static_cast(index); - const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index); - const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0; + const bool instancing_enabled = regs.vertex_stream_instances.IsInstancingEnabled(gl_index); + const GLuint divisor = instancing_enabled ? regs.vertex_streams[index].frequency : 0; glVertexBindingDivisor(gl_index, divisor); } } @@ -147,27 +147,27 @@ void RasterizerOpenGL::Clear() { bool use_depth{}; bool use_stencil{}; - if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || - regs.clear_buffers.A) { + if (regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B || + regs.clear_surface.A) { use_color = true; - const GLuint index = regs.clear_buffers.RT; + const GLuint index = regs.clear_surface.RT; state_tracker.NotifyColorMask(index); - glColorMaski(index, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0, - regs.clear_buffers.B != 0, regs.clear_buffers.A != 0); + glColorMaski(index, regs.clear_surface.R != 0, regs.clear_surface.G != 0, + regs.clear_surface.B != 0, regs.clear_surface.A != 0); // TODO(Rodrigo): Determine if clamping is used on clears SyncFragmentColorClampState(); SyncFramebufferSRGB(); } - if (regs.clear_buffers.Z) { + if (regs.clear_surface.Z) { ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); use_depth = true; state_tracker.NotifyDepthMask(); glDepthMask(GL_TRUE); } - if (regs.clear_buffers.S) { + if (regs.clear_surface.S) { ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!"); use_stencil = true; } @@ -184,16 +184,16 @@ void RasterizerOpenGL::Clear() { texture_cache.UpdateRenderTargets(true); state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle()); SyncViewport(); - if (regs.clear_flags.scissor) { + if (regs.clear_control.use_scissor) { SyncScissorTest(); } else { state_tracker.NotifyScissor0(); glDisablei(GL_SCISSOR_TEST, 0); } - UNIMPLEMENTED_IF(regs.clear_flags.viewport); + UNIMPLEMENTED_IF(regs.clear_control.use_viewport_clip0); if (use_color) { - glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); + glClearBufferfv(GL_COLOR, regs.clear_surface.RT, regs.clear_color.data()); } if (use_depth && use_stencil) { glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil); @@ -227,14 +227,14 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); BeginTransformFeedback(pipeline, primitive_mode); - const GLuint base_instance = static_cast(maxwell3d->regs.vb_base_instance); + const GLuint base_instance = static_cast(maxwell3d->regs.global_base_instance_index); const GLsizei num_instances = static_cast(is_instanced ? maxwell3d->mme_draw.instance_count : 1); if (is_indexed) { - const GLint base_vertex = static_cast(maxwell3d->regs.vb_element_base); - const GLsizei num_vertices = static_cast(maxwell3d->regs.index_array.count); + const GLint base_vertex = static_cast(maxwell3d->regs.global_base_vertex_index); + const GLsizei num_vertices = static_cast(maxwell3d->regs.index_buffer.count); const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); - const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_array.format); + const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format); if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { glDrawElements(primitive_mode, num_vertices, format, offset); } else if (num_instances == 1 && base_instance == 0) { @@ -555,9 +555,9 @@ void RasterizerOpenGL::SyncViewport() { if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) { flags[Dirty::FrontFace] = false; - GLenum mode = MaxwellToGL::FrontFace(regs.front_face); + GLenum mode = MaxwellToGL::FrontFace(regs.gl_front_face); bool flip_faces = true; - if (regs.screen_y_control.triangle_rast_flip != 0) { + if (regs.window_origin.flip_y != 0) { flip_faces = !flip_faces; } if (regs.viewport_transform[0].scale_y < 0.0f) { @@ -582,14 +582,15 @@ void RasterizerOpenGL::SyncViewport() { if (regs.viewport_transform[0].scale_y < 0.0f) { flip_y = !flip_y; } - if (regs.screen_y_control.y_negate != 0) { + const bool lower_left{regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft}; + if (lower_left) { flip_y = !flip_y; } const bool is_zero_to_one = regs.depth_mode == Maxwell::DepthMode::ZeroToOne; const GLenum origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT; const GLenum depth = is_zero_to_one ? GL_ZERO_TO_ONE : GL_NEGATIVE_ONE_TO_ONE; state_tracker.ClipControl(origin, depth); - state_tracker.SetYNegate(regs.screen_y_control.y_negate != 0); + state_tracker.SetYNegate(lower_left); } const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; @@ -657,7 +658,8 @@ void RasterizerOpenGL::SyncDepthClamp() { } flags[Dirty::DepthClampEnabled] = false; - oglEnable(GL_DEPTH_CLAMP, maxwell3d->regs.view_volume_clip_control.depth_clamp_disabled == 0); + oglEnable(GL_DEPTH_CLAMP, maxwell3d->regs.viewport_clip_control.geometry_clip != + Maxwell::ViewportClipControl::GeometryClip::Passthrough); } void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { @@ -667,7 +669,7 @@ void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { } flags[Dirty::ClipDistances] = false; - clip_mask &= maxwell3d->regs.clip_distance_enabled; + clip_mask &= maxwell3d->regs.user_clip_enable.raw; if (clip_mask == last_clip_distance_mask) { return; } @@ -689,9 +691,9 @@ void RasterizerOpenGL::SyncCullMode() { if (flags[Dirty::CullTest]) { flags[Dirty::CullTest] = false; - if (regs.cull_test_enabled) { + if (regs.gl_cull_test_enabled) { glEnable(GL_CULL_FACE); - glCullFace(MaxwellToGL::CullFace(regs.cull_face)); + glCullFace(MaxwellToGL::CullFace(regs.gl_cull_face)); } else { glDisable(GL_CULL_FACE); } @@ -743,20 +745,20 @@ void RasterizerOpenGL::SyncStencilTestState() { const auto& regs = maxwell3d->regs; oglEnable(GL_STENCIL_TEST, regs.stencil_enable); - glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), - regs.stencil_front_func_ref, regs.stencil_front_func_mask); - glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), - MaxwellToGL::StencilOp(regs.stencil_front_op_zfail), - MaxwellToGL::StencilOp(regs.stencil_front_op_zpass)); - glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask); + glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_op.func), + regs.stencil_front_func.ref, regs.stencil_front_func.func_mask); + glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op.fail), + MaxwellToGL::StencilOp(regs.stencil_front_op.zfail), + MaxwellToGL::StencilOp(regs.stencil_front_op.zpass)); + glStencilMaskSeparate(GL_FRONT, regs.stencil_front_func.mask); if (regs.stencil_two_side_enable) { - glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func), - regs.stencil_back_func_ref, regs.stencil_back_func_mask); - glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail), - MaxwellToGL::StencilOp(regs.stencil_back_op_zfail), - MaxwellToGL::StencilOp(regs.stencil_back_op_zpass)); - glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask); + glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), + regs.stencil_back_func.ref, regs.stencil_back_func.mask); + glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail), + MaxwellToGL::StencilOp(regs.stencil_back_op.zfail), + MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); + glStencilMaskSeparate(GL_BACK, regs.stencil_back_func.mask); } else { glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF); glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP); @@ -782,7 +784,7 @@ void RasterizerOpenGL::SyncPolygonModes() { flags[Dirty::PolygonModes] = false; const auto& regs = maxwell3d->regs; - if (regs.fill_rectangle) { + if (regs.fill_via_triangle_mode != Maxwell::FillViaTriangleMode::Disabled) { if (!GLAD_GL_NV_fill_rectangle) { LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported"); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -855,8 +857,8 @@ void RasterizerOpenGL::SyncMultiSampleState() { flags[Dirty::MultisampleControl] = false; const auto& regs = maxwell3d->regs; - oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage); - oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one); + oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.anti_alias_alpha_control.alpha_to_coverage); + oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.anti_alias_alpha_control.alpha_to_one); } void RasterizerOpenGL::SyncFragmentColorClampState() { @@ -866,7 +868,8 @@ void RasterizerOpenGL::SyncFragmentColorClampState() { } flags[Dirty::FragmentClampColor] = false; - glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d->regs.frag_color_clamp ? GL_TRUE : GL_FALSE); + glClampColor(GL_CLAMP_FRAGMENT_COLOR, + maxwell3d->regs.frag_color_clamp.AnyEnabled() ? GL_TRUE : GL_FALSE); } void RasterizerOpenGL::SyncBlendState() { @@ -886,18 +889,18 @@ void RasterizerOpenGL::SyncBlendState() { } flags[Dirty::BlendStates] = false; - if (!regs.independent_blend_enable) { + if (!regs.blend_per_target_enabled) { if (!regs.blend.enable[0]) { glDisable(GL_BLEND); return; } glEnable(GL_BLEND); - glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb), - MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb), - MaxwellToGL::BlendFunc(regs.blend.factor_source_a), - MaxwellToGL::BlendFunc(regs.blend.factor_dest_a)); - glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb), - MaxwellToGL::BlendEquation(regs.blend.equation_a)); + glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source), + MaxwellToGL::BlendFunc(regs.blend.color_dest), + MaxwellToGL::BlendFunc(regs.blend.alpha_source), + MaxwellToGL::BlendFunc(regs.blend.alpha_dest)); + glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.color_op), + MaxwellToGL::BlendEquation(regs.blend.alpha_op)); return; } @@ -916,14 +919,13 @@ void RasterizerOpenGL::SyncBlendState() { } glEnablei(GL_BLEND, static_cast(i)); - const auto& src = regs.independent_blend[i]; - glBlendFuncSeparatei(static_cast(i), MaxwellToGL::BlendFunc(src.factor_source_rgb), - MaxwellToGL::BlendFunc(src.factor_dest_rgb), - MaxwellToGL::BlendFunc(src.factor_source_a), - MaxwellToGL::BlendFunc(src.factor_dest_a)); - glBlendEquationSeparatei(static_cast(i), - MaxwellToGL::BlendEquation(src.equation_rgb), - MaxwellToGL::BlendEquation(src.equation_a)); + const auto& src = regs.blend_per_target[i]; + glBlendFuncSeparatei(static_cast(i), MaxwellToGL::BlendFunc(src.color_source), + MaxwellToGL::BlendFunc(src.color_dest), + MaxwellToGL::BlendFunc(src.alpha_source), + MaxwellToGL::BlendFunc(src.alpha_dest)); + glBlendEquationSeparatei(static_cast(i), MaxwellToGL::BlendEquation(src.color_op), + MaxwellToGL::BlendEquation(src.alpha_op)); } } @@ -937,7 +939,7 @@ void RasterizerOpenGL::SyncLogicOpState() { const auto& regs = maxwell3d->regs; if (regs.logic_op.enable) { glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation)); + glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.op)); } else { glDisable(GL_COLOR_LOGIC_OP); } @@ -996,7 +998,7 @@ void RasterizerOpenGL::SyncPointState() { flags[Dirty::PointSize] = false; oglEnable(GL_POINT_SPRITE, maxwell3d->regs.point_sprite_enable); - oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.vp_point_size.enable); + oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.point_size_attribute.enabled); const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; glPointSize(std::max(1.0f, maxwell3d->regs.point_size * scale)); @@ -1010,8 +1012,8 @@ void RasterizerOpenGL::SyncLineState() { flags[Dirty::LineWidth] = false; const auto& regs = maxwell3d->regs; - oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable); - glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased); + oglEnable(GL_LINE_SMOOTH, regs.line_anti_alias_enable); + glLineWidth(regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased); } void RasterizerOpenGL::SyncPolygonOffset() { @@ -1029,8 +1031,8 @@ void RasterizerOpenGL::SyncPolygonOffset() { if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable || regs.polygon_offset_point_enable) { // Hardware divides polygon offset units by two - glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f, - regs.polygon_offset_clamp); + glPolygonOffsetClamp(regs.slope_scale_depth_bias, regs.depth_bias / 2.0f, + regs.depth_bias_clamp); } } @@ -1062,14 +1064,14 @@ void RasterizerOpenGL::SyncFramebufferSRGB() { void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) { const auto& regs = maxwell3d->regs; - if (regs.tfb_enabled == 0) { + if (regs.transform_feedback_enabled == 0) { return; } program->ConfigureTransformFeedback(); - UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); + UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); UNIMPLEMENTED_IF(primitive_mode != GL_POINTS); // We may have to call BeginTransformFeedbackNV here since they seem to call different @@ -1080,7 +1082,7 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum } void RasterizerOpenGL::EndTransformFeedback() { - if (maxwell3d->regs.tfb_enabled != 0) { + if (maxwell3d->regs.transform_feedback_enabled != 0) { glEndTransformFeedback(); } } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 5a29a41d21..6bdb0b6452 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -78,11 +78,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, info.tess_clockwise = key.tessellation_clockwise != 0; info.tess_primitive = [&key] { switch (key.tessellation_primitive) { - case Maxwell::TessellationPrimitive::Isolines: + case Maxwell::Tessellation::DomainType::Isolines: return Shader::TessPrimitive::Isolines; - case Maxwell::TessellationPrimitive::Triangles: + case Maxwell::Tessellation::DomainType::Triangles: return Shader::TessPrimitive::Triangles; - case Maxwell::TessellationPrimitive::Quads: + case Maxwell::Tessellation::DomainType::Quads: return Shader::TessPrimitive::Quads; } ASSERT(false); @@ -90,11 +90,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, }(); info.tess_spacing = [&] { switch (key.tessellation_spacing) { - case Maxwell::TessellationSpacing::Equal: + case Maxwell::Tessellation::Spacing::Integer: return Shader::TessSpacing::Equal; - case Maxwell::TessellationSpacing::FractionalOdd: + case Maxwell::Tessellation::Spacing::FractionalOdd: return Shader::TessSpacing::FractionalOdd; - case Maxwell::TessellationSpacing::FractionalEven: + case Maxwell::Tessellation::Spacing::FractionalEven: return Shader::TessSpacing::FractionalEven; } ASSERT(false); @@ -139,14 +139,15 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, } void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { - std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) { - return VideoCommon::TransformFeedbackState::Layout{ - .stream = layout.stream, - .varying_count = layout.varying_count, - .stride = layout.stride, - }; - }); - state.varyings = regs.tfb_varying_locs; + std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(), + [](const auto& layout) { + return VideoCommon::TransformFeedbackState::Layout{ + .stream = layout.stream, + .varying_count = layout.varying_count, + .stride = layout.stride, + }; + }); + state.varyings = regs.stream_out_layout; } } // Anonymous namespace @@ -309,14 +310,16 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { } const auto& regs{maxwell3d->regs}; graphics_key.raw = 0; - graphics_key.early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); + graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 ? regs.draw.topology.Value() : Maxwell::PrimitiveTopology{}); - graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value()); - graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value()); - graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); - graphics_key.xfb_enabled.Assign(regs.tfb_enabled != 0 ? 1 : 0); + graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); + graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); + graphics_key.tessellation_clockwise.Assign( + regs.tessellation.params.output_primitives.Value() != + Maxwell::Tessellation::OutputPrimitves::Triangles_CCW); + graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); if (graphics_key.xfb_enabled) { SetXfbState(graphics_key.xfb_state, regs); } @@ -354,7 +357,7 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async - if (maxwell3d->regs.index_array.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) { + if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) { return pipeline; } return nullptr; diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index a8f3a0f57b..e2c709aac4 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -38,12 +38,12 @@ void SetupDirtyColorMasks(Tables& tables) { void SetupDirtyVertexInstances(Tables& tables) { static constexpr std::size_t instance_base_offset = 3; for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) { - const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]); + const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]); const std::size_t instance_array_offset = array_offset + instance_base_offset; tables[0][instance_array_offset] = static_cast(VertexInstance0 + i); tables[1][instance_array_offset] = VertexInstances; - const std::size_t instance_offset = OFF(instanced_arrays) + i; + const std::size_t instance_offset = OFF(vertex_stream_instances) + i; tables[0][instance_offset] = static_cast(VertexInstance0 + i); tables[1][instance_offset] = VertexInstances; } @@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) { FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); - tables[0][OFF(viewport_transform_enabled)] = ViewportTransform; - tables[1][OFF(viewport_transform_enabled)] = Viewports; + tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform; + tables[1][OFF(viewport_scale_offset_enbled)] = Viewports; } void SetupDirtyScissors(Tables& tables) { @@ -88,7 +88,7 @@ void SetupDirtyPolygonModes(Tables& tables) { tables[1][OFF(polygon_mode_front)] = PolygonModes; tables[1][OFF(polygon_mode_back)] = PolygonModes; - tables[0][OFF(fill_rectangle)] = PolygonModes; + tables[0][OFF(fill_via_triangle_mode)] = PolygonModes; } void SetupDirtyDepthTest(Tables& tables) { @@ -100,12 +100,14 @@ void SetupDirtyDepthTest(Tables& tables) { void SetupDirtyStencilTest(Tables& tables) { static constexpr std::array offsets = { - OFF(stencil_enable), OFF(stencil_front_func_func), OFF(stencil_front_func_ref), - OFF(stencil_front_func_mask), OFF(stencil_front_op_fail), OFF(stencil_front_op_zfail), - OFF(stencil_front_op_zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable), - OFF(stencil_back_func_func), OFF(stencil_back_func_ref), OFF(stencil_back_func_mask), - OFF(stencil_back_op_fail), OFF(stencil_back_op_zfail), OFF(stencil_back_op_zpass), - OFF(stencil_back_mask)}; + OFF(stencil_enable), OFF(stencil_front_op.func), + OFF(stencil_front_func.ref), OFF(stencil_front_func.func_mask), + OFF(stencil_front_op.fail), OFF(stencil_front_op.zfail), + OFF(stencil_front_op.zpass), OFF(stencil_front_func.mask), + OFF(stencil_two_side_enable), OFF(stencil_back_op.func), + OFF(stencil_back_func.ref), OFF(stencil_back_func.func_mask), + OFF(stencil_back_op.fail), OFF(stencil_back_op.zfail), + OFF(stencil_back_op.zpass), OFF(stencil_back_func.mask)}; for (const auto offset : offsets) { tables[0][offset] = StencilTest; } @@ -121,15 +123,15 @@ void SetupDirtyAlphaTest(Tables& tables) { void SetupDirtyBlend(Tables& tables) { FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor); - tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled; + tables[0][OFF(blend_per_target_enabled)] = BlendIndependentEnabled; for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) { - const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]); - FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i); + const std::size_t offset = OFF(blend_per_target) + i * NUM(blend_per_target[0]); + FillBlock(tables[0], offset, NUM(blend_per_target[0]), BlendState0 + i); tables[0][OFF(blend.enable) + i] = static_cast(BlendState0 + i); } - FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates); + FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendStates); FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates); } @@ -142,13 +144,14 @@ void SetupDirtyPolygonOffset(Tables& tables) { table[OFF(polygon_offset_fill_enable)] = PolygonOffset; table[OFF(polygon_offset_line_enable)] = PolygonOffset; table[OFF(polygon_offset_point_enable)] = PolygonOffset; - table[OFF(polygon_offset_factor)] = PolygonOffset; - table[OFF(polygon_offset_units)] = PolygonOffset; - table[OFF(polygon_offset_clamp)] = PolygonOffset; + table[OFF(slope_scale_depth_bias)] = PolygonOffset; + table[OFF(depth_bias)] = PolygonOffset; + table[OFF(depth_bias_clamp)] = PolygonOffset; } void SetupDirtyMultisampleControl(Tables& tables) { - FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl); + FillBlock(tables[0], OFF(anti_alias_alpha_control), NUM(anti_alias_alpha_control), + MultisampleControl); } void SetupDirtyRasterizeEnable(Tables& tables) { @@ -168,7 +171,7 @@ void SetupDirtyFragmentClampColor(Tables& tables) { } void SetupDirtyPointSize(Tables& tables) { - tables[0][OFF(vp_point_size)] = PointSize; + tables[0][OFF(point_size_attribute)] = PointSize; tables[0][OFF(point_size)] = PointSize; tables[0][OFF(point_sprite_enable)] = PointSize; } @@ -176,28 +179,28 @@ void SetupDirtyPointSize(Tables& tables) { void SetupDirtyLineWidth(Tables& tables) { tables[0][OFF(line_width_smooth)] = LineWidth; tables[0][OFF(line_width_aliased)] = LineWidth; - tables[0][OFF(line_smooth_enable)] = LineWidth; + tables[0][OFF(line_anti_alias_enable)] = LineWidth; } void SetupDirtyClipControl(Tables& tables) { auto& table = tables[0]; - table[OFF(screen_y_control)] = ClipControl; + table[OFF(window_origin)] = ClipControl; table[OFF(depth_mode)] = ClipControl; } void SetupDirtyDepthClampEnabled(Tables& tables) { - tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled; + tables[0][OFF(viewport_clip_control)] = DepthClampEnabled; } void SetupDirtyMisc(Tables& tables) { auto& table = tables[0]; - table[OFF(clip_distance_enabled)] = ClipDistances; + table[OFF(user_clip_enable)] = ClipDistances; - table[OFF(front_face)] = FrontFace; + table[OFF(gl_front_face)] = FrontFace; - table[OFF(cull_test_enabled)] = CullTest; - table[OFF(cull_face)] = CullTest; + table[OFF(gl_cull_test_enabled)] = CullTest; + table[OFF(gl_cull_face)] = CullTest; } } // Anonymous namespace diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 0044212365..e14f9b2db2 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -126,51 +126,60 @@ inline const FormatTuple& GetFormatTuple(VideoCore::Surface::PixelFormat pixel_f inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { switch (attrib.type) { - case Maxwell::VertexAttribute::Type::UnsignedNorm: - case Maxwell::VertexAttribute::Type::UnsignedScaled: - case Maxwell::VertexAttribute::Type::UnsignedInt: + case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: + ASSERT_MSG(false, "Invalid vertex attribute type!"); + break; + case Maxwell::VertexAttribute::Type::UNorm: + case Maxwell::VertexAttribute::Type::UScaled: + case Maxwell::VertexAttribute::Type::UInt: switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_8: - case Maxwell::VertexAttribute::Size::Size_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return GL_UNSIGNED_BYTE; - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return GL_UNSIGNED_SHORT; - case Maxwell::VertexAttribute::Size::Size_32: - case Maxwell::VertexAttribute::Size::Size_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return GL_UNSIGNED_INT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return GL_UNSIGNED_INT_2_10_10_10_REV; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedNorm: - case Maxwell::VertexAttribute::Type::SignedScaled: - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SNorm: + case Maxwell::VertexAttribute::Type::SScaled: + case Maxwell::VertexAttribute::Type::SInt: switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_8: - case Maxwell::VertexAttribute::Size::Size_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return GL_BYTE; - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return GL_SHORT; - case Maxwell::VertexAttribute::Size::Size_32: - case Maxwell::VertexAttribute::Size::Size_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return GL_INT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return GL_INT_2_10_10_10_REV; default: break; @@ -178,17 +187,17 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { break; case Maxwell::VertexAttribute::Type::Float: switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return GL_HALF_FLOAT; - case Maxwell::VertexAttribute::Size::Size_32: - case Maxwell::VertexAttribute::Size::Size_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return GL_FLOAT; - case Maxwell::VertexAttribute::Size::Size_11_11_10: + case Maxwell::VertexAttribute::Size::Size_B10_G11_R11: return GL_UNSIGNED_INT_10F_11F_11F_REV; default: break; @@ -335,20 +344,20 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { - case Maxwell::Blend::Equation::Add: - case Maxwell::Blend::Equation::AddGL: + case Maxwell::Blend::Equation::Add_D3D: + case Maxwell::Blend::Equation::Add_GL: return GL_FUNC_ADD; - case Maxwell::Blend::Equation::Subtract: - case Maxwell::Blend::Equation::SubtractGL: + case Maxwell::Blend::Equation::Subtract_D3D: + case Maxwell::Blend::Equation::Subtract_GL: return GL_FUNC_SUBTRACT; - case Maxwell::Blend::Equation::ReverseSubtract: - case Maxwell::Blend::Equation::ReverseSubtractGL: + case Maxwell::Blend::Equation::ReverseSubtract_D3D: + case Maxwell::Blend::Equation::ReverseSubtract_GL: return GL_FUNC_REVERSE_SUBTRACT; - case Maxwell::Blend::Equation::Min: - case Maxwell::Blend::Equation::MinGL: + case Maxwell::Blend::Equation::Min_D3D: + case Maxwell::Blend::Equation::Min_GL: return GL_MIN; - case Maxwell::Blend::Equation::Max: - case Maxwell::Blend::Equation::MaxGL: + case Maxwell::Blend::Equation::Max_D3D: + case Maxwell::Blend::Equation::Max_GL: return GL_MAX; } UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation); @@ -357,62 +366,62 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { switch (factor) { - case Maxwell::Blend::Factor::Zero: - case Maxwell::Blend::Factor::ZeroGL: + case Maxwell::Blend::Factor::Zero_D3D: + case Maxwell::Blend::Factor::Zero_GL: return GL_ZERO; - case Maxwell::Blend::Factor::One: - case Maxwell::Blend::Factor::OneGL: + case Maxwell::Blend::Factor::One_D3D: + case Maxwell::Blend::Factor::One_GL: return GL_ONE; - case Maxwell::Blend::Factor::SourceColor: - case Maxwell::Blend::Factor::SourceColorGL: + case Maxwell::Blend::Factor::SourceColor_D3D: + case Maxwell::Blend::Factor::SourceColor_GL: return GL_SRC_COLOR; - case Maxwell::Blend::Factor::OneMinusSourceColor: - case Maxwell::Blend::Factor::OneMinusSourceColorGL: + case Maxwell::Blend::Factor::OneMinusSourceColor_D3D: + case Maxwell::Blend::Factor::OneMinusSourceColor_GL: return GL_ONE_MINUS_SRC_COLOR; - case Maxwell::Blend::Factor::SourceAlpha: - case Maxwell::Blend::Factor::SourceAlphaGL: + case Maxwell::Blend::Factor::SourceAlpha_D3D: + case Maxwell::Blend::Factor::SourceAlpha_GL: return GL_SRC_ALPHA; - case Maxwell::Blend::Factor::OneMinusSourceAlpha: - case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL: return GL_ONE_MINUS_SRC_ALPHA; - case Maxwell::Blend::Factor::DestAlpha: - case Maxwell::Blend::Factor::DestAlphaGL: + case Maxwell::Blend::Factor::DestAlpha_D3D: + case Maxwell::Blend::Factor::DestAlpha_GL: return GL_DST_ALPHA; - case Maxwell::Blend::Factor::OneMinusDestAlpha: - case Maxwell::Blend::Factor::OneMinusDestAlphaGL: + case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusDestAlpha_GL: return GL_ONE_MINUS_DST_ALPHA; - case Maxwell::Blend::Factor::DestColor: - case Maxwell::Blend::Factor::DestColorGL: + case Maxwell::Blend::Factor::DestColor_D3D: + case Maxwell::Blend::Factor::DestColor_GL: return GL_DST_COLOR; - case Maxwell::Blend::Factor::OneMinusDestColor: - case Maxwell::Blend::Factor::OneMinusDestColorGL: + case Maxwell::Blend::Factor::OneMinusDestColor_D3D: + case Maxwell::Blend::Factor::OneMinusDestColor_GL: return GL_ONE_MINUS_DST_COLOR; - case Maxwell::Blend::Factor::SourceAlphaSaturate: - case Maxwell::Blend::Factor::SourceAlphaSaturateGL: + case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D: + case Maxwell::Blend::Factor::SourceAlphaSaturate_GL: return GL_SRC_ALPHA_SATURATE; - case Maxwell::Blend::Factor::Source1Color: - case Maxwell::Blend::Factor::Source1ColorGL: + case Maxwell::Blend::Factor::Source1Color_D3D: + case Maxwell::Blend::Factor::Source1Color_GL: return GL_SRC1_COLOR; - case Maxwell::Blend::Factor::OneMinusSource1Color: - case Maxwell::Blend::Factor::OneMinusSource1ColorGL: + case Maxwell::Blend::Factor::OneMinusSource1Color_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Color_GL: return GL_ONE_MINUS_SRC1_COLOR; - case Maxwell::Blend::Factor::Source1Alpha: - case Maxwell::Blend::Factor::Source1AlphaGL: + case Maxwell::Blend::Factor::Source1Alpha_D3D: + case Maxwell::Blend::Factor::Source1Alpha_GL: return GL_SRC1_ALPHA; - case Maxwell::Blend::Factor::OneMinusSource1Alpha: - case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL: return GL_ONE_MINUS_SRC1_ALPHA; - case Maxwell::Blend::Factor::ConstantColor: - case Maxwell::Blend::Factor::ConstantColorGL: + case Maxwell::Blend::Factor::BlendFactor_D3D: + case Maxwell::Blend::Factor::ConstantColor_GL: return GL_CONSTANT_COLOR; - case Maxwell::Blend::Factor::OneMinusConstantColor: - case Maxwell::Blend::Factor::OneMinusConstantColorGL: + case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D: + case Maxwell::Blend::Factor::OneMinusConstantColor_GL: return GL_ONE_MINUS_CONSTANT_COLOR; - case Maxwell::Blend::Factor::ConstantAlpha: - case Maxwell::Blend::Factor::ConstantAlphaGL: + case Maxwell::Blend::Factor::BothSourceAlpha_D3D: + case Maxwell::Blend::Factor::ConstantAlpha_GL: return GL_CONSTANT_ALPHA; - case Maxwell::Blend::Factor::OneMinusConstantAlpha: - case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: + case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL: return GL_ONE_MINUS_CONSTANT_ALPHA; } UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor); @@ -421,60 +430,60 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { switch (comparison) { - case Maxwell::ComparisonOp::Never: - case Maxwell::ComparisonOp::NeverOld: + case Maxwell::ComparisonOp::Never_D3D: + case Maxwell::ComparisonOp::Never_GL: return GL_NEVER; - case Maxwell::ComparisonOp::Less: - case Maxwell::ComparisonOp::LessOld: + case Maxwell::ComparisonOp::Less_D3D: + case Maxwell::ComparisonOp::Less_GL: return GL_LESS; - case Maxwell::ComparisonOp::Equal: - case Maxwell::ComparisonOp::EqualOld: + case Maxwell::ComparisonOp::Equal_D3D: + case Maxwell::ComparisonOp::Equal_GL: return GL_EQUAL; - case Maxwell::ComparisonOp::LessEqual: - case Maxwell::ComparisonOp::LessEqualOld: + case Maxwell::ComparisonOp::LessEqual_D3D: + case Maxwell::ComparisonOp::LessEqual_GL: return GL_LEQUAL; - case Maxwell::ComparisonOp::Greater: - case Maxwell::ComparisonOp::GreaterOld: + case Maxwell::ComparisonOp::Greater_D3D: + case Maxwell::ComparisonOp::Greater_GL: return GL_GREATER; - case Maxwell::ComparisonOp::NotEqual: - case Maxwell::ComparisonOp::NotEqualOld: + case Maxwell::ComparisonOp::NotEqual_D3D: + case Maxwell::ComparisonOp::NotEqual_GL: return GL_NOTEQUAL; - case Maxwell::ComparisonOp::GreaterEqual: - case Maxwell::ComparisonOp::GreaterEqualOld: + case Maxwell::ComparisonOp::GreaterEqual_D3D: + case Maxwell::ComparisonOp::GreaterEqual_GL: return GL_GEQUAL; - case Maxwell::ComparisonOp::Always: - case Maxwell::ComparisonOp::AlwaysOld: + case Maxwell::ComparisonOp::Always_D3D: + case Maxwell::ComparisonOp::Always_GL: return GL_ALWAYS; } UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); return GL_ALWAYS; } -inline GLenum StencilOp(Maxwell::StencilOp stencil) { +inline GLenum StencilOp(Maxwell::StencilOp::Op stencil) { switch (stencil) { - case Maxwell::StencilOp::Keep: - case Maxwell::StencilOp::KeepOGL: + case Maxwell::StencilOp::Op::Keep_D3D: + case Maxwell::StencilOp::Op::Keep_GL: return GL_KEEP; - case Maxwell::StencilOp::Zero: - case Maxwell::StencilOp::ZeroOGL: + case Maxwell::StencilOp::Op::Zero_D3D: + case Maxwell::StencilOp::Op::Zero_GL: return GL_ZERO; - case Maxwell::StencilOp::Replace: - case Maxwell::StencilOp::ReplaceOGL: + case Maxwell::StencilOp::Op::Replace_D3D: + case Maxwell::StencilOp::Op::Replace_GL: return GL_REPLACE; - case Maxwell::StencilOp::Incr: - case Maxwell::StencilOp::IncrOGL: + case Maxwell::StencilOp::Op::IncrSaturate_D3D: + case Maxwell::StencilOp::Op::IncrSaturate_GL: return GL_INCR; - case Maxwell::StencilOp::Decr: - case Maxwell::StencilOp::DecrOGL: + case Maxwell::StencilOp::Op::DecrSaturate_D3D: + case Maxwell::StencilOp::Op::DecrSaturate_GL: return GL_DECR; - case Maxwell::StencilOp::Invert: - case Maxwell::StencilOp::InvertOGL: + case Maxwell::StencilOp::Op::Invert_D3D: + case Maxwell::StencilOp::Op::Invert_GL: return GL_INVERT; - case Maxwell::StencilOp::IncrWrap: - case Maxwell::StencilOp::IncrWrapOGL: + case Maxwell::StencilOp::Op::Incr_D3D: + case Maxwell::StencilOp::Op::Incr_GL: return GL_INCR_WRAP; - case Maxwell::StencilOp::DecrWrap: - case Maxwell::StencilOp::DecrWrapOGL: + case Maxwell::StencilOp::Op::Decr_D3D: + case Maxwell::StencilOp::Op::Decr_GL: return GL_DECR_WRAP; } UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil); @@ -505,39 +514,39 @@ inline GLenum CullFace(Maxwell::CullFace cull_face) { return GL_BACK; } -inline GLenum LogicOp(Maxwell::LogicOperation operation) { +inline GLenum LogicOp(Maxwell::LogicOp::Op operation) { switch (operation) { - case Maxwell::LogicOperation::Clear: + case Maxwell::LogicOp::Op::Clear: return GL_CLEAR; - case Maxwell::LogicOperation::And: + case Maxwell::LogicOp::Op::And: return GL_AND; - case Maxwell::LogicOperation::AndReverse: + case Maxwell::LogicOp::Op::AndReverse: return GL_AND_REVERSE; - case Maxwell::LogicOperation::Copy: + case Maxwell::LogicOp::Op::Copy: return GL_COPY; - case Maxwell::LogicOperation::AndInverted: + case Maxwell::LogicOp::Op::AndInverted: return GL_AND_INVERTED; - case Maxwell::LogicOperation::NoOp: + case Maxwell::LogicOp::Op::NoOp: return GL_NOOP; - case Maxwell::LogicOperation::Xor: + case Maxwell::LogicOp::Op::Xor: return GL_XOR; - case Maxwell::LogicOperation::Or: + case Maxwell::LogicOp::Op::Or: return GL_OR; - case Maxwell::LogicOperation::Nor: + case Maxwell::LogicOp::Op::Nor: return GL_NOR; - case Maxwell::LogicOperation::Equiv: + case Maxwell::LogicOp::Op::Equiv: return GL_EQUIV; - case Maxwell::LogicOperation::Invert: + case Maxwell::LogicOp::Op::Invert: return GL_INVERT; - case Maxwell::LogicOperation::OrReverse: + case Maxwell::LogicOp::Op::OrReverse: return GL_OR_REVERSE; - case Maxwell::LogicOperation::CopyInverted: + case Maxwell::LogicOp::Op::CopyInverted: return GL_COPY_INVERTED; - case Maxwell::LogicOperation::OrInverted: + case Maxwell::LogicOp::Op::OrInverted: return GL_OR_INVERTED; - case Maxwell::LogicOperation::Nand: + case Maxwell::LogicOp::Op::Nand: return GL_NAND; - case Maxwell::LogicOperation::Set: + case Maxwell::LogicOp::Op::Set: return GL_SET; } UNIMPLEMENTED_MSG("Unimplemented logic operation={}", operation); diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 733b454de7..eb7c22fd5a 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -34,14 +34,15 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { }; void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { - std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) { - return VideoCommon::TransformFeedbackState::Layout{ - .stream = layout.stream, - .varying_count = layout.varying_count, - .stride = layout.stride, - }; - }); - state.varyings = regs.tfb_varying_locs; + std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(), + [](const auto& layout) { + return VideoCommon::TransformFeedbackState::Layout{ + .stream = layout.stream, + .varying_count = layout.varying_count, + .stride = layout.stride, + }; + }); + state.varyings = regs.stream_out_layout; } } // Anonymous namespace @@ -58,33 +59,34 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, raw1 = 0; extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); - xfb_enabled.Assign(regs.tfb_enabled != 0); + xfb_enabled.Assign(regs.transform_feedback_enabled != 0); primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); - depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); + depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); patch_control_points_minus_one.Assign(regs.patch_vertices - 1); - tessellation_primitive.Assign(static_cast(regs.tess_mode.prim.Value())); - tessellation_spacing.Assign(static_cast(regs.tess_mode.spacing.Value())); - tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); + tessellation_primitive.Assign(static_cast(regs.tessellation.params.domain_type.Value())); + tessellation_spacing.Assign(static_cast(regs.tessellation.params.spacing.Value())); + tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() != + Maxwell::Tessellation::OutputPrimitves::Triangles_CCW); logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - logic_op.Assign(PackLogicOp(regs.logic_op.operation)); + logic_op.Assign(PackLogicOp(regs.logic_op.op)); topology.Assign(regs.draw.topology); - msaa_mode.Assign(regs.multisample_mode); + msaa_mode.Assign(regs.anti_alias_samples_mode); raw2 = 0; rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); const auto test_func = - regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always; + regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); - early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); + early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0); depth_format.Assign(static_cast(regs.zeta.format)); - y_negate.Assign(regs.screen_y_control.y_negate != 0 ? 1 : 0); - provoking_vertex_last.Assign(regs.provoking_vertex_last != 0 ? 1 : 0); - conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); - smooth_lines.Assign(regs.line_smooth_enable != 0 ? 1 : 0); + y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); + provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); + smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); for (size_t i = 0; i < regs.rt.size(); ++i) { color_formats[i] = static_cast(regs.rt[i].format); @@ -116,8 +118,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, maxwell3d.dirty.flags[Dirty::VertexInput] = false; enabled_divisors = 0; for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { - const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index); - binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0; + const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index); + binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0; enabled_divisors |= (is_enabled ? u64{1} : 0) << index; } for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { @@ -164,17 +166,17 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t // TODO: C++20 Use templated lambda to deduplicate code - if (!regs.independent_blend_enable) { - const auto& src = regs.blend; - if (!src.enable[index]) { + if (!regs.blend_per_target_enabled) { + if (!regs.blend.enable[index]) { return; } - equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); - equation_a.Assign(PackBlendEquation(src.equation_a)); - factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); - factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); - factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); - factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); + const auto& src = regs.blend; + equation_rgb.Assign(PackBlendEquation(src.color_op)); + equation_a.Assign(PackBlendEquation(src.alpha_op)); + factor_source_rgb.Assign(PackBlendFactor(src.color_source)); + factor_dest_rgb.Assign(PackBlendFactor(src.color_dest)); + factor_source_a.Assign(PackBlendFactor(src.alpha_source)); + factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); enable.Assign(1); return; } @@ -182,34 +184,34 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t if (!regs.blend.enable[index]) { return; } - const auto& src = regs.independent_blend[index]; - equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); - equation_a.Assign(PackBlendEquation(src.equation_a)); - factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); - factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); - factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); - factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); + const auto& src = regs.blend_per_target[index]; + equation_rgb.Assign(PackBlendEquation(src.color_op)); + equation_a.Assign(PackBlendEquation(src.alpha_op)); + factor_source_rgb.Assign(PackBlendFactor(src.color_source)); + factor_dest_rgb.Assign(PackBlendFactor(src.color_dest)); + factor_source_a.Assign(PackBlendFactor(src.alpha_source)); + factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); enable.Assign(1); } void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { - u32 packed_front_face = PackFrontFace(regs.front_face); - if (regs.screen_y_control.triangle_rast_flip != 0) { + u32 packed_front_face = PackFrontFace(regs.gl_front_face); + if (regs.window_origin.flip_y != 0) { // Flip front face packed_front_face = 1 - packed_front_face; } raw1 = 0; raw2 = 0; - front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); - front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); - front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); - front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); + front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); + front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); + front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); + front.test_func.Assign(PackComparisonOp(regs.stencil_front_op.func)); if (regs.stencil_two_side_enable) { - back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); - back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); - back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); - back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); + back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op.fail)); + back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op.zfail)); + back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op.zpass)); + back.test_func.Assign(PackComparisonOp(regs.stencil_back_op.func)); } else { back.action_stencil_fail.Assign(front.action_stencil_fail); back.action_depth_fail.Assign(front.action_depth_fail); @@ -222,9 +224,9 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { depth_test_enable.Assign(regs.depth_test_enable); front_face.Assign(packed_front_face); depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); - cull_face.Assign(PackCullFace(regs.cull_face)); - cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); - std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) { + cull_face.Assign(PackCullFace(regs.gl_cull_face)); + cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); + std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { return static_cast(array.stride.Value()); }); } @@ -251,41 +253,42 @@ Maxwell::ComparisonOp FixedPipelineState::UnpackComparisonOp(u32 packed) noexcep return static_cast(packed + 1); } -u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp op) noexcept { +u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp::Op op) noexcept { switch (op) { - case Maxwell::StencilOp::Keep: - case Maxwell::StencilOp::KeepOGL: + case Maxwell::StencilOp::Op::Keep_D3D: + case Maxwell::StencilOp::Op::Keep_GL: return 0; - case Maxwell::StencilOp::Zero: - case Maxwell::StencilOp::ZeroOGL: + case Maxwell::StencilOp::Op::Zero_D3D: + case Maxwell::StencilOp::Op::Zero_GL: return 1; - case Maxwell::StencilOp::Replace: - case Maxwell::StencilOp::ReplaceOGL: + case Maxwell::StencilOp::Op::Replace_D3D: + case Maxwell::StencilOp::Op::Replace_GL: return 2; - case Maxwell::StencilOp::Incr: - case Maxwell::StencilOp::IncrOGL: + case Maxwell::StencilOp::Op::IncrSaturate_D3D: + case Maxwell::StencilOp::Op::IncrSaturate_GL: return 3; - case Maxwell::StencilOp::Decr: - case Maxwell::StencilOp::DecrOGL: + case Maxwell::StencilOp::Op::DecrSaturate_D3D: + case Maxwell::StencilOp::Op::DecrSaturate_GL: return 4; - case Maxwell::StencilOp::Invert: - case Maxwell::StencilOp::InvertOGL: + case Maxwell::StencilOp::Op::Invert_D3D: + case Maxwell::StencilOp::Op::Invert_GL: return 5; - case Maxwell::StencilOp::IncrWrap: - case Maxwell::StencilOp::IncrWrapOGL: + case Maxwell::StencilOp::Op::Incr_D3D: + case Maxwell::StencilOp::Op::Incr_GL: return 6; - case Maxwell::StencilOp::DecrWrap: - case Maxwell::StencilOp::DecrWrapOGL: + case Maxwell::StencilOp::Op::Decr_D3D: + case Maxwell::StencilOp::Op::Decr_GL: return 7; } return 0; } -Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { - static constexpr std::array LUT = {Maxwell::StencilOp::Keep, Maxwell::StencilOp::Zero, - Maxwell::StencilOp::Replace, Maxwell::StencilOp::Incr, - Maxwell::StencilOp::Decr, Maxwell::StencilOp::Invert, - Maxwell::StencilOp::IncrWrap, Maxwell::StencilOp::DecrWrap}; +Maxwell::StencilOp::Op FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { + static constexpr std::array LUT = { + Maxwell::StencilOp::Op::Keep_D3D, Maxwell::StencilOp::Op::Zero_D3D, + Maxwell::StencilOp::Op::Replace_D3D, Maxwell::StencilOp::Op::IncrSaturate_D3D, + Maxwell::StencilOp::Op::DecrSaturate_D3D, Maxwell::StencilOp::Op::Invert_D3D, + Maxwell::StencilOp::Op::Incr_D3D, Maxwell::StencilOp::Op::Decr_D3D}; return LUT[packed]; } @@ -318,30 +321,30 @@ Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept return static_cast(packed + 0x1B00); } -u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept { +u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOp::Op op) noexcept { return static_cast(op) - 0x1500; } -Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { - return static_cast(packed + 0x1500); +Maxwell::LogicOp::Op FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { + return static_cast(packed + 0x1500); } u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept { switch (equation) { - case Maxwell::Blend::Equation::Add: - case Maxwell::Blend::Equation::AddGL: + case Maxwell::Blend::Equation::Add_D3D: + case Maxwell::Blend::Equation::Add_GL: return 0; - case Maxwell::Blend::Equation::Subtract: - case Maxwell::Blend::Equation::SubtractGL: + case Maxwell::Blend::Equation::Subtract_D3D: + case Maxwell::Blend::Equation::Subtract_GL: return 1; - case Maxwell::Blend::Equation::ReverseSubtract: - case Maxwell::Blend::Equation::ReverseSubtractGL: + case Maxwell::Blend::Equation::ReverseSubtract_D3D: + case Maxwell::Blend::Equation::ReverseSubtract_GL: return 2; - case Maxwell::Blend::Equation::Min: - case Maxwell::Blend::Equation::MinGL: + case Maxwell::Blend::Equation::Min_D3D: + case Maxwell::Blend::Equation::Min_GL: return 3; - case Maxwell::Blend::Equation::Max: - case Maxwell::Blend::Equation::MaxGL: + case Maxwell::Blend::Equation::Max_D3D: + case Maxwell::Blend::Equation::Max_GL: return 4; } return 0; @@ -349,97 +352,99 @@ u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noe Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept { static constexpr std::array LUT = { - Maxwell::Blend::Equation::Add, Maxwell::Blend::Equation::Subtract, - Maxwell::Blend::Equation::ReverseSubtract, Maxwell::Blend::Equation::Min, - Maxwell::Blend::Equation::Max}; + Maxwell::Blend::Equation::Add_D3D, Maxwell::Blend::Equation::Subtract_D3D, + Maxwell::Blend::Equation::ReverseSubtract_D3D, Maxwell::Blend::Equation::Min_D3D, + Maxwell::Blend::Equation::Max_D3D}; return LUT[packed]; } u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept { switch (factor) { - case Maxwell::Blend::Factor::Zero: - case Maxwell::Blend::Factor::ZeroGL: + case Maxwell::Blend::Factor::Zero_D3D: + case Maxwell::Blend::Factor::Zero_GL: return 0; - case Maxwell::Blend::Factor::One: - case Maxwell::Blend::Factor::OneGL: + case Maxwell::Blend::Factor::One_D3D: + case Maxwell::Blend::Factor::One_GL: return 1; - case Maxwell::Blend::Factor::SourceColor: - case Maxwell::Blend::Factor::SourceColorGL: + case Maxwell::Blend::Factor::SourceColor_D3D: + case Maxwell::Blend::Factor::SourceColor_GL: return 2; - case Maxwell::Blend::Factor::OneMinusSourceColor: - case Maxwell::Blend::Factor::OneMinusSourceColorGL: + case Maxwell::Blend::Factor::OneMinusSourceColor_D3D: + case Maxwell::Blend::Factor::OneMinusSourceColor_GL: return 3; - case Maxwell::Blend::Factor::SourceAlpha: - case Maxwell::Blend::Factor::SourceAlphaGL: + case Maxwell::Blend::Factor::SourceAlpha_D3D: + case Maxwell::Blend::Factor::SourceAlpha_GL: return 4; - case Maxwell::Blend::Factor::OneMinusSourceAlpha: - case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL: return 5; - case Maxwell::Blend::Factor::DestAlpha: - case Maxwell::Blend::Factor::DestAlphaGL: + case Maxwell::Blend::Factor::DestAlpha_D3D: + case Maxwell::Blend::Factor::DestAlpha_GL: return 6; - case Maxwell::Blend::Factor::OneMinusDestAlpha: - case Maxwell::Blend::Factor::OneMinusDestAlphaGL: + case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusDestAlpha_GL: return 7; - case Maxwell::Blend::Factor::DestColor: - case Maxwell::Blend::Factor::DestColorGL: + case Maxwell::Blend::Factor::DestColor_D3D: + case Maxwell::Blend::Factor::DestColor_GL: return 8; - case Maxwell::Blend::Factor::OneMinusDestColor: - case Maxwell::Blend::Factor::OneMinusDestColorGL: + case Maxwell::Blend::Factor::OneMinusDestColor_D3D: + case Maxwell::Blend::Factor::OneMinusDestColor_GL: return 9; - case Maxwell::Blend::Factor::SourceAlphaSaturate: - case Maxwell::Blend::Factor::SourceAlphaSaturateGL: + case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D: + case Maxwell::Blend::Factor::SourceAlphaSaturate_GL: return 10; - case Maxwell::Blend::Factor::Source1Color: - case Maxwell::Blend::Factor::Source1ColorGL: + case Maxwell::Blend::Factor::Source1Color_D3D: + case Maxwell::Blend::Factor::Source1Color_GL: return 11; - case Maxwell::Blend::Factor::OneMinusSource1Color: - case Maxwell::Blend::Factor::OneMinusSource1ColorGL: + case Maxwell::Blend::Factor::OneMinusSource1Color_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Color_GL: return 12; - case Maxwell::Blend::Factor::Source1Alpha: - case Maxwell::Blend::Factor::Source1AlphaGL: + case Maxwell::Blend::Factor::Source1Alpha_D3D: + case Maxwell::Blend::Factor::Source1Alpha_GL: return 13; - case Maxwell::Blend::Factor::OneMinusSource1Alpha: - case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL: return 14; - case Maxwell::Blend::Factor::ConstantColor: - case Maxwell::Blend::Factor::ConstantColorGL: + case Maxwell::Blend::Factor::BlendFactor_D3D: + case Maxwell::Blend::Factor::ConstantColor_GL: return 15; - case Maxwell::Blend::Factor::OneMinusConstantColor: - case Maxwell::Blend::Factor::OneMinusConstantColorGL: + case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D: + case Maxwell::Blend::Factor::OneMinusConstantColor_GL: return 16; - case Maxwell::Blend::Factor::ConstantAlpha: - case Maxwell::Blend::Factor::ConstantAlphaGL: + case Maxwell::Blend::Factor::BothSourceAlpha_D3D: + case Maxwell::Blend::Factor::ConstantAlpha_GL: return 17; - case Maxwell::Blend::Factor::OneMinusConstantAlpha: - case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: + case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL: return 18; } + UNIMPLEMENTED_MSG("Unknown blend factor {}", static_cast(factor)); return 0; } Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept { static constexpr std::array LUT = { - Maxwell::Blend::Factor::Zero, - Maxwell::Blend::Factor::One, - Maxwell::Blend::Factor::SourceColor, - Maxwell::Blend::Factor::OneMinusSourceColor, - Maxwell::Blend::Factor::SourceAlpha, - Maxwell::Blend::Factor::OneMinusSourceAlpha, - Maxwell::Blend::Factor::DestAlpha, - Maxwell::Blend::Factor::OneMinusDestAlpha, - Maxwell::Blend::Factor::DestColor, - Maxwell::Blend::Factor::OneMinusDestColor, - Maxwell::Blend::Factor::SourceAlphaSaturate, - Maxwell::Blend::Factor::Source1Color, - Maxwell::Blend::Factor::OneMinusSource1Color, - Maxwell::Blend::Factor::Source1Alpha, - Maxwell::Blend::Factor::OneMinusSource1Alpha, - Maxwell::Blend::Factor::ConstantColor, - Maxwell::Blend::Factor::OneMinusConstantColor, - Maxwell::Blend::Factor::ConstantAlpha, - Maxwell::Blend::Factor::OneMinusConstantAlpha, + Maxwell::Blend::Factor::Zero_D3D, + Maxwell::Blend::Factor::One_D3D, + Maxwell::Blend::Factor::SourceColor_D3D, + Maxwell::Blend::Factor::OneMinusSourceColor_D3D, + Maxwell::Blend::Factor::SourceAlpha_D3D, + Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D, + Maxwell::Blend::Factor::DestAlpha_D3D, + Maxwell::Blend::Factor::OneMinusDestAlpha_D3D, + Maxwell::Blend::Factor::DestColor_D3D, + Maxwell::Blend::Factor::OneMinusDestColor_D3D, + Maxwell::Blend::Factor::SourceAlphaSaturate_D3D, + Maxwell::Blend::Factor::Source1Color_D3D, + Maxwell::Blend::Factor::OneMinusSource1Color_D3D, + Maxwell::Blend::Factor::Source1Alpha_D3D, + Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D, + Maxwell::Blend::Factor::BlendFactor_D3D, + Maxwell::Blend::Factor::OneMinusBlendFactor_D3D, + Maxwell::Blend::Factor::BothSourceAlpha_D3D, + Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D, }; + ASSERT(packed < LUT.size()); return LUT[packed]; } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 9d60756e55..43441209c2 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,8 +21,8 @@ struct FixedPipelineState { static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; - static u32 PackStencilOp(Maxwell::StencilOp op) noexcept; - static Maxwell::StencilOp UnpackStencilOp(u32 packed) noexcept; + static u32 PackStencilOp(Maxwell::StencilOp::Op op) noexcept; + static Maxwell::StencilOp::Op UnpackStencilOp(u32 packed) noexcept; static u32 PackCullFace(Maxwell::CullFace cull) noexcept; static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept; @@ -33,8 +33,8 @@ struct FixedPipelineState { static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept; static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept; - static u32 PackLogicOp(Maxwell::LogicOperation op) noexcept; - static Maxwell::LogicOperation UnpackLogicOp(u32 packed) noexcept; + static u32 PackLogicOp(Maxwell::LogicOp::Op op) noexcept; + static Maxwell::LogicOp::Op UnpackLogicOp(u32 packed) noexcept; static u32 PackBlendEquation(Maxwell::Blend::Equation equation) noexcept; static Maxwell::Blend::Equation UnpackBlendEquation(u32 packed) noexcept; @@ -113,15 +113,15 @@ struct FixedPipelineState { BitField action_depth_pass; BitField test_func; - Maxwell::StencilOp ActionStencilFail() const noexcept { + Maxwell::StencilOp::Op ActionStencilFail() const noexcept { return UnpackStencilOp(action_stencil_fail); } - Maxwell::StencilOp ActionDepthFail() const noexcept { + Maxwell::StencilOp::Op ActionDepthFail() const noexcept { return UnpackStencilOp(action_depth_fail); } - Maxwell::StencilOp ActionDepthPass() const noexcept { + Maxwell::StencilOp::Op ActionDepthPass() const noexcept { return UnpackStencilOp(action_depth_pass); } @@ -193,7 +193,6 @@ struct FixedPipelineState { BitField<6, 5, u32> depth_format; BitField<11, 1, u32> y_negate; BitField<12, 1, u32> provoking_vertex_last; - BitField<13, 1, u32> conservative_raster_enable; BitField<14, 1, u32> smooth_lines; }; std::array color_formats; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index e7104d377c..5c156087b8 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -323,161 +323,182 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { const VkFormat format{([&]() { switch (type) { - case Maxwell::VertexAttribute::Type::UnsignedNorm: + case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: + ASSERT_MSG(false, "Invalid vertex attribute type!"); + break; + case Maxwell::VertexAttribute::Type::UNorm: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_UNORM; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_UNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_UNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_UNORM; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_UNORM; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_UNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_UNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_UNORM; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_UNORM_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedNorm: + case Maxwell::VertexAttribute::Type::SNorm: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_SNORM; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SNORM; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_SNORM_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::UnsignedScaled: + case Maxwell::VertexAttribute::Type::UScaled: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_USCALED; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_USCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_USCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_USCALED; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_USCALED; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_USCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_USCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_USCALED; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_USCALED_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedScaled: + case Maxwell::VertexAttribute::Type::SScaled: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::UnsignedInt: + case Maxwell::VertexAttribute::Type::UInt: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_UINT; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_UINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_UINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_UINT; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_UINT; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_UINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_UINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_UINT; - case Maxwell::VertexAttribute::Size::Size_32: + case Maxwell::VertexAttribute::Size::Size_R32: return VK_FORMAT_R32_UINT; - case Maxwell::VertexAttribute::Size::Size_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: return VK_FORMAT_R32G32_UINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: return VK_FORMAT_R32G32B32_UINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return VK_FORMAT_R32G32B32A32_UINT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_UINT_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SInt: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_SINT; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_SINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_SINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_SINT; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SINT; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SINT; - case Maxwell::VertexAttribute::Size::Size_32: + case Maxwell::VertexAttribute::Size::Size_R32: return VK_FORMAT_R32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: return VK_FORMAT_R32G32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: return VK_FORMAT_R32G32B32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return VK_FORMAT_R32G32B32A32_SINT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_SINT_PACK32; default: break; @@ -485,23 +506,23 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, break; case Maxwell::VertexAttribute::Type::Float: switch (size) { - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32: + case Maxwell::VertexAttribute::Size::Size_R32: return VK_FORMAT_R32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: return VK_FORMAT_R32G32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: return VK_FORMAT_R32G32B32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return VK_FORMAT_R32G32B32A32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_11_11_10: + case Maxwell::VertexAttribute::Size::Size_B10_G11_R11: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; default: break; @@ -521,29 +542,29 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { switch (comparison) { - case Maxwell::ComparisonOp::Never: - case Maxwell::ComparisonOp::NeverOld: + case Maxwell::ComparisonOp::Never_D3D: + case Maxwell::ComparisonOp::Never_GL: return VK_COMPARE_OP_NEVER; - case Maxwell::ComparisonOp::Less: - case Maxwell::ComparisonOp::LessOld: + case Maxwell::ComparisonOp::Less_D3D: + case Maxwell::ComparisonOp::Less_GL: return VK_COMPARE_OP_LESS; - case Maxwell::ComparisonOp::Equal: - case Maxwell::ComparisonOp::EqualOld: + case Maxwell::ComparisonOp::Equal_D3D: + case Maxwell::ComparisonOp::Equal_GL: return VK_COMPARE_OP_EQUAL; - case Maxwell::ComparisonOp::LessEqual: - case Maxwell::ComparisonOp::LessEqualOld: + case Maxwell::ComparisonOp::LessEqual_D3D: + case Maxwell::ComparisonOp::LessEqual_GL: return VK_COMPARE_OP_LESS_OR_EQUAL; - case Maxwell::ComparisonOp::Greater: - case Maxwell::ComparisonOp::GreaterOld: + case Maxwell::ComparisonOp::Greater_D3D: + case Maxwell::ComparisonOp::Greater_GL: return VK_COMPARE_OP_GREATER; - case Maxwell::ComparisonOp::NotEqual: - case Maxwell::ComparisonOp::NotEqualOld: + case Maxwell::ComparisonOp::NotEqual_D3D: + case Maxwell::ComparisonOp::NotEqual_GL: return VK_COMPARE_OP_NOT_EQUAL; - case Maxwell::ComparisonOp::GreaterEqual: - case Maxwell::ComparisonOp::GreaterEqualOld: + case Maxwell::ComparisonOp::GreaterEqual_D3D: + case Maxwell::ComparisonOp::GreaterEqual_GL: return VK_COMPARE_OP_GREATER_OR_EQUAL; - case Maxwell::ComparisonOp::Always: - case Maxwell::ComparisonOp::AlwaysOld: + case Maxwell::ComparisonOp::Always_D3D: + case Maxwell::ComparisonOp::Always_GL: return VK_COMPARE_OP_ALWAYS; } UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); @@ -563,31 +584,31 @@ VkIndexType IndexFormat(Maxwell::IndexFormat index_format) { return {}; } -VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) { +VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op) { switch (stencil_op) { - case Maxwell::StencilOp::Keep: - case Maxwell::StencilOp::KeepOGL: + case Maxwell::StencilOp::Op::Keep_D3D: + case Maxwell::StencilOp::Op::Keep_GL: return VK_STENCIL_OP_KEEP; - case Maxwell::StencilOp::Zero: - case Maxwell::StencilOp::ZeroOGL: + case Maxwell::StencilOp::Op::Zero_D3D: + case Maxwell::StencilOp::Op::Zero_GL: return VK_STENCIL_OP_ZERO; - case Maxwell::StencilOp::Replace: - case Maxwell::StencilOp::ReplaceOGL: + case Maxwell::StencilOp::Op::Replace_D3D: + case Maxwell::StencilOp::Op::Replace_GL: return VK_STENCIL_OP_REPLACE; - case Maxwell::StencilOp::Incr: - case Maxwell::StencilOp::IncrOGL: + case Maxwell::StencilOp::Op::IncrSaturate_D3D: + case Maxwell::StencilOp::Op::IncrSaturate_GL: return VK_STENCIL_OP_INCREMENT_AND_CLAMP; - case Maxwell::StencilOp::Decr: - case Maxwell::StencilOp::DecrOGL: + case Maxwell::StencilOp::Op::DecrSaturate_D3D: + case Maxwell::StencilOp::Op::DecrSaturate_GL: return VK_STENCIL_OP_DECREMENT_AND_CLAMP; - case Maxwell::StencilOp::Invert: - case Maxwell::StencilOp::InvertOGL: + case Maxwell::StencilOp::Op::Invert_D3D: + case Maxwell::StencilOp::Op::Invert_GL: return VK_STENCIL_OP_INVERT; - case Maxwell::StencilOp::IncrWrap: - case Maxwell::StencilOp::IncrWrapOGL: + case Maxwell::StencilOp::Op::Incr_D3D: + case Maxwell::StencilOp::Op::Incr_GL: return VK_STENCIL_OP_INCREMENT_AND_WRAP; - case Maxwell::StencilOp::DecrWrap: - case Maxwell::StencilOp::DecrWrapOGL: + case Maxwell::StencilOp::Op::Decr_D3D: + case Maxwell::StencilOp::Op::Decr_GL: return VK_STENCIL_OP_DECREMENT_AND_WRAP; } UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil_op); @@ -596,20 +617,20 @@ VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) { VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { - case Maxwell::Blend::Equation::Add: - case Maxwell::Blend::Equation::AddGL: + case Maxwell::Blend::Equation::Add_D3D: + case Maxwell::Blend::Equation::Add_GL: return VK_BLEND_OP_ADD; - case Maxwell::Blend::Equation::Subtract: - case Maxwell::Blend::Equation::SubtractGL: + case Maxwell::Blend::Equation::Subtract_D3D: + case Maxwell::Blend::Equation::Subtract_GL: return VK_BLEND_OP_SUBTRACT; - case Maxwell::Blend::Equation::ReverseSubtract: - case Maxwell::Blend::Equation::ReverseSubtractGL: + case Maxwell::Blend::Equation::ReverseSubtract_D3D: + case Maxwell::Blend::Equation::ReverseSubtract_GL: return VK_BLEND_OP_REVERSE_SUBTRACT; - case Maxwell::Blend::Equation::Min: - case Maxwell::Blend::Equation::MinGL: + case Maxwell::Blend::Equation::Min_D3D: + case Maxwell::Blend::Equation::Min_GL: return VK_BLEND_OP_MIN; - case Maxwell::Blend::Equation::Max: - case Maxwell::Blend::Equation::MaxGL: + case Maxwell::Blend::Equation::Max_D3D: + case Maxwell::Blend::Equation::Max_GL: return VK_BLEND_OP_MAX; } UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation); @@ -618,62 +639,62 @@ VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) { VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor) { switch (factor) { - case Maxwell::Blend::Factor::Zero: - case Maxwell::Blend::Factor::ZeroGL: + case Maxwell::Blend::Factor::Zero_D3D: + case Maxwell::Blend::Factor::Zero_GL: return VK_BLEND_FACTOR_ZERO; - case Maxwell::Blend::Factor::One: - case Maxwell::Blend::Factor::OneGL: + case Maxwell::Blend::Factor::One_D3D: + case Maxwell::Blend::Factor::One_GL: return VK_BLEND_FACTOR_ONE; - case Maxwell::Blend::Factor::SourceColor: - case Maxwell::Blend::Factor::SourceColorGL: + case Maxwell::Blend::Factor::SourceColor_D3D: + case Maxwell::Blend::Factor::SourceColor_GL: return VK_BLEND_FACTOR_SRC_COLOR; - case Maxwell::Blend::Factor::OneMinusSourceColor: - case Maxwell::Blend::Factor::OneMinusSourceColorGL: + case Maxwell::Blend::Factor::OneMinusSourceColor_D3D: + case Maxwell::Blend::Factor::OneMinusSourceColor_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - case Maxwell::Blend::Factor::SourceAlpha: - case Maxwell::Blend::Factor::SourceAlphaGL: + case Maxwell::Blend::Factor::SourceAlpha_D3D: + case Maxwell::Blend::Factor::SourceAlpha_GL: return VK_BLEND_FACTOR_SRC_ALPHA; - case Maxwell::Blend::Factor::OneMinusSourceAlpha: - case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - case Maxwell::Blend::Factor::DestAlpha: - case Maxwell::Blend::Factor::DestAlphaGL: + case Maxwell::Blend::Factor::DestAlpha_D3D: + case Maxwell::Blend::Factor::DestAlpha_GL: return VK_BLEND_FACTOR_DST_ALPHA; - case Maxwell::Blend::Factor::OneMinusDestAlpha: - case Maxwell::Blend::Factor::OneMinusDestAlphaGL: + case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusDestAlpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; - case Maxwell::Blend::Factor::DestColor: - case Maxwell::Blend::Factor::DestColorGL: + case Maxwell::Blend::Factor::DestColor_D3D: + case Maxwell::Blend::Factor::DestColor_GL: return VK_BLEND_FACTOR_DST_COLOR; - case Maxwell::Blend::Factor::OneMinusDestColor: - case Maxwell::Blend::Factor::OneMinusDestColorGL: + case Maxwell::Blend::Factor::OneMinusDestColor_D3D: + case Maxwell::Blend::Factor::OneMinusDestColor_GL: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; - case Maxwell::Blend::Factor::SourceAlphaSaturate: - case Maxwell::Blend::Factor::SourceAlphaSaturateGL: + case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D: + case Maxwell::Blend::Factor::SourceAlphaSaturate_GL: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; - case Maxwell::Blend::Factor::Source1Color: - case Maxwell::Blend::Factor::Source1ColorGL: + case Maxwell::Blend::Factor::Source1Color_D3D: + case Maxwell::Blend::Factor::Source1Color_GL: return VK_BLEND_FACTOR_SRC1_COLOR; - case Maxwell::Blend::Factor::OneMinusSource1Color: - case Maxwell::Blend::Factor::OneMinusSource1ColorGL: + case Maxwell::Blend::Factor::OneMinusSource1Color_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Color_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; - case Maxwell::Blend::Factor::Source1Alpha: - case Maxwell::Blend::Factor::Source1AlphaGL: + case Maxwell::Blend::Factor::Source1Alpha_D3D: + case Maxwell::Blend::Factor::Source1Alpha_GL: return VK_BLEND_FACTOR_SRC1_ALPHA; - case Maxwell::Blend::Factor::OneMinusSource1Alpha: - case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; - case Maxwell::Blend::Factor::ConstantColor: - case Maxwell::Blend::Factor::ConstantColorGL: + case Maxwell::Blend::Factor::BlendFactor_D3D: + case Maxwell::Blend::Factor::ConstantColor_GL: return VK_BLEND_FACTOR_CONSTANT_COLOR; - case Maxwell::Blend::Factor::OneMinusConstantColor: - case Maxwell::Blend::Factor::OneMinusConstantColorGL: + case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D: + case Maxwell::Blend::Factor::OneMinusConstantColor_GL: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; - case Maxwell::Blend::Factor::ConstantAlpha: - case Maxwell::Blend::Factor::ConstantAlphaGL: + case Maxwell::Blend::Factor::BothSourceAlpha_D3D: + case Maxwell::Blend::Factor::ConstantAlpha_GL: return VK_BLEND_FACTOR_CONSTANT_ALPHA; - case Maxwell::Blend::Factor::OneMinusConstantAlpha: - case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: + case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; } UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor); diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 356d46292b..6f65502d61 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h @@ -55,7 +55,7 @@ VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); VkIndexType IndexFormat(Maxwell::IndexFormat index_format); -VkStencilOp StencilOp(Maxwell::StencilOp stencil_op); +VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op); VkBlendOp BlendEquation(Maxwell::Blend::Equation equation); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index f47786f48f..c3f66c8a38 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -288,7 +288,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes); const auto& regs{maxwell3d->regs}; - const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const Shader::Info& info{stage_infos[stage]}; buffer_cache.UnbindGraphicsStorageBuffers(stage); @@ -664,15 +664,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .lineStippleFactor = 0, .lineStipplePattern = 0, }; - VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT, - .pNext = nullptr, - .flags = 0, - .conservativeRasterizationMode = key.state.conservative_raster_enable != 0 - ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT - : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, - .extraPrimitiveOverestimationSize = 0.0f, - }; VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, .pNext = nullptr, @@ -683,9 +674,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) { line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state); } - if (device.IsExtConservativeRasterizationSupported()) { - conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster); - } if (device.IsExtProvokingVertexSupported()) { provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 732e7b6f24..20f1d65846 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -62,29 +62,29 @@ auto MakeSpan(Container& container) { Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { switch (comparison) { - case Maxwell::ComparisonOp::Never: - case Maxwell::ComparisonOp::NeverOld: + case Maxwell::ComparisonOp::Never_D3D: + case Maxwell::ComparisonOp::Never_GL: return Shader::CompareFunction::Never; - case Maxwell::ComparisonOp::Less: - case Maxwell::ComparisonOp::LessOld: + case Maxwell::ComparisonOp::Less_D3D: + case Maxwell::ComparisonOp::Less_GL: return Shader::CompareFunction::Less; - case Maxwell::ComparisonOp::Equal: - case Maxwell::ComparisonOp::EqualOld: + case Maxwell::ComparisonOp::Equal_D3D: + case Maxwell::ComparisonOp::Equal_GL: return Shader::CompareFunction::Equal; - case Maxwell::ComparisonOp::LessEqual: - case Maxwell::ComparisonOp::LessEqualOld: + case Maxwell::ComparisonOp::LessEqual_D3D: + case Maxwell::ComparisonOp::LessEqual_GL: return Shader::CompareFunction::LessThanEqual; - case Maxwell::ComparisonOp::Greater: - case Maxwell::ComparisonOp::GreaterOld: + case Maxwell::ComparisonOp::Greater_D3D: + case Maxwell::ComparisonOp::Greater_GL: return Shader::CompareFunction::Greater; - case Maxwell::ComparisonOp::NotEqual: - case Maxwell::ComparisonOp::NotEqualOld: + case Maxwell::ComparisonOp::NotEqual_D3D: + case Maxwell::ComparisonOp::NotEqual_GL: return Shader::CompareFunction::NotEqual; - case Maxwell::ComparisonOp::GreaterEqual: - case Maxwell::ComparisonOp::GreaterEqualOld: + case Maxwell::ComparisonOp::GreaterEqual_D3D: + case Maxwell::ComparisonOp::GreaterEqual_GL: return Shader::CompareFunction::GreaterThanEqual; - case Maxwell::ComparisonOp::Always: - case Maxwell::ComparisonOp::AlwaysOld: + case Maxwell::ComparisonOp::Always_D3D: + case Maxwell::ComparisonOp::Always_GL: return Shader::CompareFunction::Always; } UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); @@ -96,15 +96,18 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut return Shader::AttributeType::Disabled; } switch (attr.Type()) { - case Maxwell::VertexAttribute::Type::SignedNorm: - case Maxwell::VertexAttribute::Type::UnsignedNorm: - case Maxwell::VertexAttribute::Type::UnsignedScaled: - case Maxwell::VertexAttribute::Type::SignedScaled: + case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: + ASSERT_MSG(false, "Invalid vertex attribute type!"); + return Shader::AttributeType::Disabled; + case Maxwell::VertexAttribute::Type::SNorm: + case Maxwell::VertexAttribute::Type::UNorm: + case Maxwell::VertexAttribute::Type::UScaled: + case Maxwell::VertexAttribute::Type::SScaled: case Maxwell::VertexAttribute::Type::Float: return Shader::AttributeType::Float; - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SInt: return Shader::AttributeType::SignedInt; - case Maxwell::VertexAttribute::Type::UnsignedInt: + case Maxwell::VertexAttribute::Type::UInt: return Shader::AttributeType::UnsignedInt; } return Shader::AttributeType::Float; @@ -162,16 +165,14 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program } break; case Shader::Stage::TessellationEval: - // We have to flip tessellation clockwise for some reason... - info.tess_clockwise = key.state.tessellation_clockwise == 0; info.tess_primitive = [&key] { const u32 raw{key.state.tessellation_primitive.Value()}; - switch (static_cast(raw)) { - case Maxwell::TessellationPrimitive::Isolines: + switch (static_cast(raw)) { + case Maxwell::Tessellation::DomainType::Isolines: return Shader::TessPrimitive::Isolines; - case Maxwell::TessellationPrimitive::Triangles: + case Maxwell::Tessellation::DomainType::Triangles: return Shader::TessPrimitive::Triangles; - case Maxwell::TessellationPrimitive::Quads: + case Maxwell::Tessellation::DomainType::Quads: return Shader::TessPrimitive::Quads; } ASSERT(false); @@ -179,12 +180,12 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program }(); info.tess_spacing = [&] { const u32 raw{key.state.tessellation_spacing}; - switch (static_cast(raw)) { - case Maxwell::TessellationSpacing::Equal: + switch (static_cast(raw)) { + case Maxwell::Tessellation::Spacing::Integer: return Shader::TessSpacing::Equal; - case Maxwell::TessellationSpacing::FractionalOdd: + case Maxwell::Tessellation::Spacing::FractionalOdd: return Shader::TessSpacing::FractionalOdd; - case Maxwell::TessellationSpacing::FractionalEven: + case Maxwell::Tessellation::Spacing::FractionalEven: return Shader::TessSpacing::FractionalEven; } ASSERT(false); @@ -490,7 +491,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async - if (maxwell3d->regs.index_array.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) { + if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) { return pipeline; } return nullptr; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index acfd5da7d6..892cd94a30 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -70,7 +70,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in const float width = conv(src.scale_x * 2.0f); float y = conv(src.translate_y - src.scale_y); float height = conv(src.scale_y * 2.0f); - bool y_negate = regs.screen_y_control.y_negate; + bool y_negate = regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft; if (!device.IsNvViewportSwizzleSupported()) { y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY); @@ -130,11 +130,11 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3 DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced, bool is_indexed) { DrawParams params{ - .base_instance = regs.vb_base_instance, + .base_instance = regs.global_base_instance_index, .num_instances = is_instanced ? num_instances : 1, - .base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first, - .num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count, - .first_index = is_indexed ? regs.index_array.first : 0, + .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, .is_indexed = is_indexed, }; if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { @@ -225,10 +225,10 @@ void RasterizerVulkan::Clear() { query_cache.UpdateCounters(); auto& regs = maxwell3d->regs; - const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || - regs.clear_buffers.A; - const bool use_depth = regs.clear_buffers.Z; - const bool use_stencil = regs.clear_buffers.S; + const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B || + regs.clear_surface.A; + const bool use_depth = regs.clear_surface.Z; + const bool use_stencil = regs.clear_surface.S; if (!use_color && !use_depth && !use_stencil) { return; } @@ -254,9 +254,9 @@ void RasterizerVulkan::Clear() { default_scissor.extent.height = std::numeric_limits::max(); VkClearRect clear_rect{ - .rect = regs.clear_flags.scissor ? GetScissorState(regs, 0, up_scale, down_shift) - : default_scissor, - .baseArrayLayer = regs.clear_buffers.layer, + .rect = regs.clear_control.use_scissor ? GetScissorState(regs, 0, up_scale, down_shift) + : default_scissor, + .baseArrayLayer = regs.clear_surface.layer, .layerCount = 1, }; if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) { @@ -267,7 +267,7 @@ void RasterizerVulkan::Clear() { .height = std::min(clear_rect.rect.extent.height, render_area.height), }; - const u32 color_attachment = regs.clear_buffers.RT; + const u32 color_attachment = regs.clear_surface.RT; if (use_color && framebuffer->HasAspectColorBit(color_attachment)) { VkClearValue clear_value; bool is_integer = false; @@ -289,7 +289,8 @@ void RasterizerVulkan::Clear() { break; } if (!is_integer) { - std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color)); + std::memcpy(clear_value.color.float32, regs.clear_color.data(), + regs.clear_color.size() * sizeof(f32)); } else if (!is_signed) { for (size_t i = 0; i < 4; i++) { clear_value.color.uint32[i] = static_cast( @@ -648,23 +649,23 @@ void RasterizerVulkan::UpdateDynamicStates() { void RasterizerVulkan::BeginTransformFeedback() { const auto& regs = maxwell3d->regs; - if (regs.tfb_enabled == 0) { + if (regs.transform_feedback_enabled == 0) { return; } if (!device.IsExtTransformFeedbackSupported()) { LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); return; } - UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); + UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); scheduler.Record( [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); } void RasterizerVulkan::EndTransformFeedback() { const auto& regs = maxwell3d->regs; - if (regs.tfb_enabled == 0) { + if (regs.transform_feedback_enabled == 0) { return; } if (!device.IsExtTransformFeedbackSupported()) { @@ -728,11 +729,11 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchDepthBias()) { return; } - float units = regs.polygon_offset_units / 2.0f; - const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::S8_UINT_Z24_UNORM || - regs.zeta.format == Tegra::DepthFormat::D24X8_UNORM || - regs.zeta.format == Tegra::DepthFormat::D24S8_UNORM || - regs.zeta.format == Tegra::DepthFormat::D24C8_UNORM; + float units = regs.depth_bias / 2.0f; + const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::Z24_UNORM_S8_UINT || + regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM || + regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM || + regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM; if (is_d24 && !device.SupportsD24DepthBuffer()) { // the base formulas can be obtained from here: // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias @@ -740,8 +741,8 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { static_cast(1ULL << (32 - 24)) / (static_cast(0x1.ep+127)); units = static_cast(static_cast(units) * rescale_factor); } - scheduler.Record([constant = units, clamp = regs.polygon_offset_clamp, - factor = regs.polygon_offset_factor](vk::CommandBuffer cmdbuf) { + scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, + factor = regs.slope_scale_depth_bias](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBias(constant, clamp, factor); }); } @@ -771,10 +772,11 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) if (regs.stencil_two_side_enable) { // Separate values per face scheduler.Record( - [front_ref = regs.stencil_front_func_ref, front_write_mask = regs.stencil_front_mask, - front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_func_ref, - back_write_mask = regs.stencil_back_mask, - back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { + [front_ref = regs.stencil_front_func.ref, + front_write_mask = regs.stencil_front_func.mask, + front_test_mask = regs.stencil_front_func.func_mask, + back_ref = regs.stencil_back_func.ref, back_write_mask = regs.stencil_back_func.mask, + back_test_mask = regs.stencil_back_func.func_mask](vk::CommandBuffer cmdbuf) { // Front face cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT, front_ref); cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front_write_mask); @@ -787,8 +789,9 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) }); } else { // Front face defines both faces - scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask, - test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { + scheduler.Record([ref = regs.stencil_front_func.ref, + write_mask = regs.stencil_front_func.mask, + test_mask = regs.stencil_front_func.func_mask](vk::CommandBuffer cmdbuf) { cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask); @@ -800,7 +803,8 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchLineWidth()) { return; } - const float width = regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased; + const float width = + regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased; scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); } @@ -808,10 +812,10 @@ void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchCullMode()) { return; } - scheduler.Record( - [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) { - cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); - }); + scheduler.Record([enabled = regs.gl_cull_test_enabled, + cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) { + cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); + }); } void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { @@ -860,8 +864,8 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { return; } - VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face); - if (regs.screen_y_control.triangle_rast_flip != 0) { + VkFrontFace front_face = MaxwellToVK::FrontFace(regs.gl_front_face); + if (regs.window_origin.flip_y != 0) { front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE; } @@ -873,16 +877,16 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchStencilOp()) { return; } - const Maxwell::StencilOp fail = regs.stencil_front_op_fail; - const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail; - const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; - const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; + const Maxwell::StencilOp::Op fail = regs.stencil_front_op.fail; + const Maxwell::StencilOp::Op zfail = regs.stencil_front_op.zfail; + const Maxwell::StencilOp::Op zpass = regs.stencil_front_op.zpass; + const Maxwell::ComparisonOp compare = regs.stencil_front_op.func; if (regs.stencil_two_side_enable) { // Separate stencil op per face - const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; - const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; - const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; - const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func; + const Maxwell::StencilOp::Op back_fail = regs.stencil_back_op.fail; + const Maxwell::StencilOp::Op back_zfail = regs.stencil_back_op.zfail; + const Maxwell::StencilOp::Op back_zpass = regs.stencil_back_op.zpass; + const Maxwell::ComparisonOp back_compare = regs.stencil_back_op.func; scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, back_compare](vk::CommandBuffer cmdbuf) { cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail), @@ -954,15 +958,15 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) dirty[Dirty::VertexBinding0 + index] = false; const u32 binding{static_cast(index)}; - const auto& input_binding{regs.vertex_array[binding]}; - const bool is_instanced{regs.instanced_arrays.IsInstancingEnabled(binding)}; + const auto& input_binding{regs.vertex_streams[binding]}; + const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; bindings.push_back({ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, .pNext = nullptr, .binding = binding, .stride = input_binding.stride, .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, - .divisor = is_instanced ? input_binding.divisor : 1, + .divisor = is_instanced ? input_binding.frequency : 1, }); } scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index f234e1a310..ed98c83701 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -51,8 +51,8 @@ Flags MakeInvalidationFlags() { void SetupDirtyViewports(Tables& tables) { FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); - tables[0][OFF(viewport_transform_enabled)] = Viewports; - tables[1][OFF(screen_y_control)] = Viewports; + tables[0][OFF(viewport_scale_offset_enbled)] = Viewports; + tables[1][OFF(window_origin)] = Viewports; } void SetupDirtyScissors(Tables& tables) { @@ -61,9 +61,9 @@ void SetupDirtyScissors(Tables& tables) { void SetupDirtyDepthBias(Tables& tables) { auto& table = tables[0]; - table[OFF(polygon_offset_units)] = DepthBias; - table[OFF(polygon_offset_clamp)] = DepthBias; - table[OFF(polygon_offset_factor)] = DepthBias; + table[OFF(depth_bias)] = DepthBias; + table[OFF(depth_bias_clamp)] = DepthBias; + table[OFF(slope_scale_depth_bias)] = DepthBias; } void SetupDirtyBlendConstants(Tables& tables) { @@ -77,12 +77,12 @@ void SetupDirtyDepthBounds(Tables& tables) { void SetupDirtyStencilProperties(Tables& tables) { auto& table = tables[0]; table[OFF(stencil_two_side_enable)] = StencilProperties; - table[OFF(stencil_front_func_ref)] = StencilProperties; - table[OFF(stencil_front_mask)] = StencilProperties; - table[OFF(stencil_front_func_mask)] = StencilProperties; - table[OFF(stencil_back_func_ref)] = StencilProperties; - table[OFF(stencil_back_mask)] = StencilProperties; - table[OFF(stencil_back_func_mask)] = StencilProperties; + table[OFF(stencil_front_func.ref)] = StencilProperties; + table[OFF(stencil_front_func.mask)] = StencilProperties; + table[OFF(stencil_front_func.func_mask)] = StencilProperties; + table[OFF(stencil_back_func.ref)] = StencilProperties; + table[OFF(stencil_back_func.mask)] = StencilProperties; + table[OFF(stencil_back_func.func_mask)] = StencilProperties; } void SetupDirtyLineWidth(Tables& tables) { @@ -92,8 +92,8 @@ void SetupDirtyLineWidth(Tables& tables) { void SetupDirtyCullMode(Tables& tables) { auto& table = tables[0]; - table[OFF(cull_face)] = CullMode; - table[OFF(cull_test_enabled)] = CullMode; + table[OFF(gl_cull_face)] = CullMode; + table[OFF(gl_cull_test_enabled)] = CullMode; } void SetupDirtyDepthBoundsEnable(Tables& tables) { @@ -114,20 +114,20 @@ void SetupDirtyDepthCompareOp(Tables& tables) { void SetupDirtyFrontFace(Tables& tables) { auto& table = tables[0]; - table[OFF(front_face)] = FrontFace; - table[OFF(screen_y_control)] = FrontFace; + table[OFF(gl_front_face)] = FrontFace; + table[OFF(window_origin)] = FrontFace; } void SetupDirtyStencilOp(Tables& tables) { auto& table = tables[0]; - table[OFF(stencil_front_op_fail)] = StencilOp; - table[OFF(stencil_front_op_zfail)] = StencilOp; - table[OFF(stencil_front_op_zpass)] = StencilOp; - table[OFF(stencil_front_func_func)] = StencilOp; - table[OFF(stencil_back_op_fail)] = StencilOp; - table[OFF(stencil_back_op_zfail)] = StencilOp; - table[OFF(stencil_back_op_zpass)] = StencilOp; - table[OFF(stencil_back_func_func)] = StencilOp; + table[OFF(stencil_front_op.fail)] = StencilOp; + table[OFF(stencil_front_op.zfail)] = StencilOp; + table[OFF(stencil_front_op.zpass)] = StencilOp; + table[OFF(stencil_front_op.func)] = StencilOp; + table[OFF(stencil_back_op.fail)] = StencilOp; + table[OFF(stencil_back_op.zfail)] = StencilOp; + table[OFF(stencil_back_op.zpass)] = StencilOp; + table[OFF(stencil_back_op.func)] = StencilOp; // Table 0 is used by StencilProperties tables[1][OFF(stencil_two_side_enable)] = StencilOp; @@ -139,10 +139,10 @@ void SetupDirtyStencilTestEnable(Tables& tables) { void SetupDirtyBlending(Tables& tables) { tables[0][OFF(color_mask_common)] = Blending; - tables[0][OFF(independent_blend_enable)] = Blending; + tables[0][OFF(blend_per_target_enabled)] = Blending; FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); FillBlock(tables[0], OFF(blend), NUM(blend), Blending); - FillBlock(tables[0], OFF(independent_blend), NUM(independent_blend), Blending); + FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending); } void SetupDirtyViewportSwizzles(Tables& tables) { @@ -166,10 +166,10 @@ void SetupDirtyVertexBindings(Tables& tables) { static constexpr size_t divisor_offset = 3; for (size_t i = 0; i < Regs::NumVertexArrays; ++i) { const u8 flag = static_cast(VertexBinding0 + i); - tables[0][OFF(instanced_arrays) + i] = VertexInput; - tables[1][OFF(instanced_arrays) + i] = flag; - tables[0][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = VertexInput; - tables[1][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = flag; + tables[0][OFF(vertex_stream_instances) + i] = VertexInput; + tables[1][OFF(vertex_stream_instances) + i] = flag; + tables[0][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = VertexInput; + tables[1][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = flag; } } } // Anonymous namespace diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index f530665794..d9482371bc 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -43,14 +43,14 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { } dirty[VideoCommon::Dirty::Shaders] = false; - const GPUVAddr base_addr{maxwell3d->regs.code_address.CodeAddress()}; + const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()}; for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) { if (!maxwell3d->regs.IsShaderConfigEnabled(index)) { unique_hashes[index] = 0; continue; } - const auto& shader_config{maxwell3d->regs.shader_config[index]}; - const auto program{static_cast(index)}; + const auto& shader_config{maxwell3d->regs.pipelines[index]}; + const auto program{static_cast(index)}; const GPUVAddr shader_addr{base_addr + shader_config.offset}; const std::optional cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)}; if (!cpu_shader_addr) { @@ -90,14 +90,14 @@ const ShaderInfo* ShaderCache::ComputeShader() { void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result, const std::array& unique_hashes) { size_t env_index{}; - const GPUVAddr base_addr{maxwell3d->regs.code_address.CodeAddress()}; + const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()}; for (size_t index = 0; index < NUM_PROGRAMS; ++index) { if (unique_hashes[index] == 0) { continue; } - const auto program{static_cast(index)}; + const auto program{static_cast(index)}; auto& env{result.envs[index]}; - const u32 start_address{maxwell3d->regs.shader_config[index].offset}; + const u32 start_address{maxwell3d->regs.pipelines[index].offset}; env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, base_addr, start_address}; env.SetCachedSize(shader_infos[index]->size_bytes); result.env_ptrs[env_index++] = &env; diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 5f76259470..fbabb32194 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -250,34 +250,34 @@ Shader::TextureType GenericEnvironment::ReadTextureTypeImpl(GPUVAddr tic_addr, u GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - Maxwell::ShaderProgram program, GPUVAddr program_base_, + Maxwell::ShaderType program, GPUVAddr program_base_, u32 start_address_) : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} { gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph)); initial_offset = sizeof(sph); - gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask; + gp_passthrough_mask = maxwell3d->regs.post_vtg_shader_attrib_skip_mask; switch (program) { - case Maxwell::ShaderProgram::VertexA: + case Maxwell::ShaderType::VertexA: stage = Shader::Stage::VertexA; stage_index = 0; break; - case Maxwell::ShaderProgram::VertexB: + case Maxwell::ShaderType::VertexB: stage = Shader::Stage::VertexB; stage_index = 0; break; - case Maxwell::ShaderProgram::TesselationControl: + case Maxwell::ShaderType::TessellationInit: stage = Shader::Stage::TessellationControl; stage_index = 1; break; - case Maxwell::ShaderProgram::TesselationEval: + case Maxwell::ShaderType::Tessellation: stage = Shader::Stage::TessellationEval; stage_index = 2; break; - case Maxwell::ShaderProgram::Geometry: + case Maxwell::ShaderType::Geometry: stage = Shader::Stage::Geometry; stage_index = 3; break; - case Maxwell::ShaderProgram::Fragment: + case Maxwell::ShaderType::Pixel: stage = Shader::Stage::Fragment; stage_index = 4; break; @@ -288,7 +288,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, const u64 local_size{sph.LocalMemorySize()}; ASSERT(local_size <= std::numeric_limits::max()); local_memory_size = static_cast(local_size) + sph.common3.shader_local_memory_crs_size; - texture_bound = maxwell3d->regs.tex_cb_index; + texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; } u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { @@ -304,8 +304,9 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { const auto& regs{maxwell3d->regs}; - const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; - return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, via_header_index, handle); + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; + return ReadTextureTypeImpl(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, + handle); } ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 5a145f33a6..8b3b8e9f54 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -93,7 +93,7 @@ public: explicit GraphicsEnvironment() = default; explicit GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::Maxwell3D::Regs::ShaderProgram program, + Tegra::Engines::Maxwell3D::Regs::ShaderType program, GPUVAddr program_base_, u32 start_address_); ~GraphicsEnvironment() override = default; diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index a2bf082941..6bd133d103 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -73,17 +73,17 @@ bool SurfaceTargetIsArray(SurfaceTarget target) { PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { switch (format) { - case Tegra::DepthFormat::S8_UINT_Z24_UNORM: + case Tegra::DepthFormat::Z24_UNORM_S8_UINT: return PixelFormat::S8_UINT_D24_UNORM; - case Tegra::DepthFormat::D24S8_UNORM: + case Tegra::DepthFormat::S8Z24_UNORM: return PixelFormat::D24_UNORM_S8_UINT; - case Tegra::DepthFormat::D32_FLOAT: + case Tegra::DepthFormat::Z32_FLOAT: return PixelFormat::D32_FLOAT; - case Tegra::DepthFormat::D16_UNORM: + case Tegra::DepthFormat::Z16_UNORM: return PixelFormat::D16_UNORM; case Tegra::DepthFormat::S8_UINT: return PixelFormat::S8_UINT; - case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT: + case Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT: return PixelFormat::D32_FLOAT_S8_UINT; default: UNIMPLEMENTED_MSG("Unimplemented format={}", format); @@ -117,9 +117,9 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) return PixelFormat::R32G32_UINT; case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT: return PixelFormat::R16G16B16X16_FLOAT; - case Tegra::RenderTargetFormat::B8G8R8A8_UNORM: + case Tegra::RenderTargetFormat::A8R8G8B8_UNORM: return PixelFormat::B8G8R8A8_UNORM; - case Tegra::RenderTargetFormat::B8G8R8A8_SRGB: + case Tegra::RenderTargetFormat::A8R8G8B8_SRGB: return PixelFormat::B8G8R8A8_SRGB; case Tegra::RenderTargetFormat::A2B10G10R10_UNORM: return PixelFormat::A2B10G10R10_UNORM; diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 6c073ee57b..852ec25194 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" #include "video_core/surface.h" #include "video_core/texture_cache/format_lookup_table.h" @@ -12,6 +14,7 @@ namespace VideoCommon { +using Tegra::Engines::Maxwell3D; using Tegra::Texture::TextureType; using Tegra::Texture::TICEntry; using VideoCore::Surface::PixelFormat; @@ -107,12 +110,13 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { } } -ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept { +ImageInfo::ImageInfo(const Maxwell3D::Regs& regs, size_t index) noexcept { const auto& rt = regs.rt[index]; format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format); rescaleable = false; if (rt.tile_mode.is_pitch_linear) { - ASSERT(rt.tile_mode.is_3d == 0); + ASSERT(rt.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray); type = ImageType::Linear; pitch = rt.width; size = Extent3D{ @@ -124,15 +128,16 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) } size.width = rt.width; size.height = rt.height; - layer_stride = rt.layer_stride * 4; + layer_stride = rt.array_pitch * 4; maybe_unaligned_layer_stride = layer_stride; - num_samples = NumSamples(regs.multisample_mode); + num_samples = NumSamples(regs.anti_alias_samples_mode); block = Extent3D{ .width = rt.tile_mode.block_width, .height = rt.tile_mode.block_height, .depth = rt.tile_mode.block_depth, }; - if (rt.tile_mode.is_3d) { + if (rt.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) { type = ImageType::e3D; size.depth = rt.depth; } else { @@ -146,31 +151,37 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept { format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format); - size.width = regs.zeta_width; - size.height = regs.zeta_height; + size.width = regs.zeta_size.width; + size.height = regs.zeta_size.height; rescaleable = false; resources.levels = 1; - layer_stride = regs.zeta.layer_stride * 4; + layer_stride = regs.zeta.array_pitch * 4; maybe_unaligned_layer_stride = layer_stride; - num_samples = NumSamples(regs.multisample_mode); + num_samples = NumSamples(regs.anti_alias_samples_mode); block = Extent3D{ .width = regs.zeta.tile_mode.block_width, .height = regs.zeta.tile_mode.block_height, .depth = regs.zeta.tile_mode.block_depth, }; if (regs.zeta.tile_mode.is_pitch_linear) { - ASSERT(regs.zeta.tile_mode.is_3d == 0); + ASSERT(regs.zeta.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray); type = ImageType::Linear; pitch = size.width * BytesPerBlock(format); - } else if (regs.zeta.tile_mode.is_3d) { + } else if (regs.zeta.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) { ASSERT(regs.zeta.tile_mode.is_pitch_linear == 0); + ASSERT(regs.zeta_size.dim_control == + Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeOne); type = ImageType::e3D; - size.depth = regs.zeta_depth; + size.depth = regs.zeta_size.depth; } else { + ASSERT(regs.zeta_size.dim_control == + Maxwell3D::Regs::ZetaSize::DimensionControl::DepthDefinesArray); rescaleable = block.depth == 0; downscaleable = size.height > 512; type = ImageType::e2D; - resources.layers = regs.zeta_depth; + resources.layers = regs.zeta_size.depth; } } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index eaf4a1c959..413baf730d 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -189,15 +189,16 @@ typename P::Sampler* TextureCache

::GetComputeSampler(u32 index) { template void TextureCache

::SynchronizeGraphicsDescriptors() { - using SamplerIndex = Tegra::Engines::Maxwell3D::Regs::SamplerIndex; - const bool linked_tsc = maxwell3d->regs.sampler_index == SamplerIndex::ViaHeaderIndex; - const u32 tic_limit = maxwell3d->regs.tic.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tsc.limit; - if (channel_state->graphics_sampler_table.Synchornize(maxwell3d->regs.tsc.Address(), + using SamplerBinding = Tegra::Engines::Maxwell3D::Regs::SamplerBinding; + const bool linked_tsc = maxwell3d->regs.sampler_binding == SamplerBinding::ViaHeaderBinding; + const u32 tic_limit = maxwell3d->regs.tex_header.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit; + if (channel_state->graphics_sampler_table.Synchornize(maxwell3d->regs.tex_sampler.Address(), tsc_limit)) { channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (channel_state->graphics_image_table.Synchornize(maxwell3d->regs.tic.Address(), tic_limit)) { + if (channel_state->graphics_image_table.Synchornize(maxwell3d->regs.tex_header.Address(), + tic_limit)) { channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } @@ -352,8 +353,8 @@ void TextureCache

::UpdateRenderTargets(bool is_clear) { down_shift = Settings::values.resolution_info.down_shift; } render_targets.size = Extent2D{ - (maxwell3d->regs.render_area.width * up_scale) >> down_shift, - (maxwell3d->regs.render_area.height * up_scale) >> down_shift, + (maxwell3d->regs.surface_clip.width * up_scale) >> down_shift, + (maxwell3d->regs.surface_clip.height * up_scale) >> down_shift, }; render_targets.is_rescaled = is_rescaling; @@ -1980,7 +1981,7 @@ bool TextureCache

::IsFullClear(ImageViewId id) { // Images with multiple resources can't be cleared in a single call return false; } - if (regs.clear_flags.scissor == 0) { + if (regs.clear_control.use_scissor == 0) { // If scissor testing is disabled, the clear is always full return true; } diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index 7e605981c3..45071185ad 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -15,51 +15,51 @@ namespace VideoCommon { std::vector MakeTransformFeedbackVaryings( const TransformFeedbackState& state) { static constexpr std::array VECTORS{ - 28, // gl_Position - 32, // Generic 0 - 36, // Generic 1 - 40, // Generic 2 - 44, // Generic 3 - 48, // Generic 4 - 52, // Generic 5 - 56, // Generic 6 - 60, // Generic 7 - 64, // Generic 8 - 68, // Generic 9 - 72, // Generic 10 - 76, // Generic 11 - 80, // Generic 12 - 84, // Generic 13 - 88, // Generic 14 - 92, // Generic 15 - 96, // Generic 16 - 100, // Generic 17 - 104, // Generic 18 - 108, // Generic 19 - 112, // Generic 20 - 116, // Generic 21 - 120, // Generic 22 - 124, // Generic 23 - 128, // Generic 24 - 132, // Generic 25 - 136, // Generic 26 - 140, // Generic 27 - 144, // Generic 28 - 148, // Generic 29 - 152, // Generic 30 - 156, // Generic 31 - 160, // gl_FrontColor - 164, // gl_FrontSecondaryColor - 160, // gl_BackColor - 164, // gl_BackSecondaryColor - 192, // gl_TexCoord[0] - 196, // gl_TexCoord[1] - 200, // gl_TexCoord[2] - 204, // gl_TexCoord[3] - 208, // gl_TexCoord[4] - 212, // gl_TexCoord[5] - 216, // gl_TexCoord[6] - 220, // gl_TexCoord[7] + 28U, // gl_Position + 32U, // Generic 0 + 36U, // Generic 1 + 40U, // Generic 2 + 44U, // Generic 3 + 48U, // Generic 4 + 52U, // Generic 5 + 56U, // Generic 6 + 60U, // Generic 7 + 64U, // Generic 8 + 68U, // Generic 9 + 72U, // Generic 10 + 76U, // Generic 11 + 80U, // Generic 12 + 84U, // Generic 13 + 88U, // Generic 14 + 92U, // Generic 15 + 96U, // Generic 16 + 100U, // Generic 17 + 104U, // Generic 18 + 108U, // Generic 19 + 112U, // Generic 20 + 116U, // Generic 21 + 120U, // Generic 22 + 124U, // Generic 23 + 128U, // Generic 24 + 132U, // Generic 25 + 136U, // Generic 26 + 140U, // Generic 27 + 144U, // Generic 28 + 148U, // Generic 29 + 152U, // Generic 30 + 156U, // Generic 31 + 160U, // gl_FrontColor + 164U, // gl_FrontSecondaryColor + 160U, // gl_BackColor + 164U, // gl_BackSecondaryColor + 192U, // gl_TexCoord[0] + 196U, // gl_TexCoord[1] + 200U, // gl_TexCoord[2] + 204U, // gl_TexCoord[3] + 208U, // gl_TexCoord[4] + 212U, // gl_TexCoord[5] + 216U, // gl_TexCoord[6] + 220U, // gl_TexCoord[7] }; std::vector xfb(256); for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) { @@ -68,8 +68,20 @@ std::vector MakeTransformFeedbackVaryings( const u32 varying_count = layout.varying_count; u32 highest = 0; for (u32 offset = 0; offset < varying_count; ++offset) { - const u32 base_offset = offset; - const u8 location = locations[offset]; + const auto get_attribute = [&locations](u32 index) -> u32 { + switch (index % 4) { + case 0: + return locations[index / 4].attribute0.Value(); + case 1: + return locations[index / 4].attribute1.Value(); + case 2: + return locations[index / 4].attribute2.Value(); + case 3: + return locations[index / 4].attribute3.Value(); + } + UNREACHABLE(); + return 0; + }; UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream); Shader::TransformFeedbackVarying varying{ @@ -78,16 +90,18 @@ std::vector MakeTransformFeedbackVaryings( .offset = offset * 4, .components = 1, }; - if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { - UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); + const u32 base_offset = offset; + const auto attribute{get_attribute(offset)}; + if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) { + UNIMPLEMENTED_IF_MSG(attribute % 4 != 0, "Unaligned TFB {}", attribute); - const u8 base_index = location / 4; - while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { + const auto base_index = attribute / 4; + while (offset + 1 < varying_count && base_index == get_attribute(offset + 1) / 4) { ++offset; ++varying.components; } } - xfb[location] = varying; + xfb[attribute] = varying; highest = std::max(highest, (base_offset + varying.components) * 4); } UNIMPLEMENTED_IF(highest != layout.stride); diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h index a519adb59e..d13eb16c39 100644 --- a/src/video_core/transform_feedback.h +++ b/src/video_core/transform_feedback.h @@ -19,7 +19,8 @@ struct TransformFeedbackState { u32 stride; }; std::array layouts; - std::array, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> + std::array, + Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> varyings; }; From 1e35ade1ecfb37cd8e4f7d5211d5f7a6f0c4cf9c Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:10:06 -0400 Subject: [PATCH 112/245] configure_graphics: Fix graphics API selection when a game is running The graphics API setting should not be changed when a game is running. --- src/yuzu/configuration/configure_graphics.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 87e5d0f48a..bd69d04a65 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -57,9 +57,10 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren UpdateBackgroundColorButton(new_bg_color); }); - ui->api->setEnabled(!UISettings::values.has_broken_vulkan); - ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan || - Settings::IsConfiguringGlobal()); + ui->api->setEnabled(!UISettings::values.has_broken_vulkan && ui->api->isEnabled()); + ui->api_widget->setEnabled( + (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) && + ui->api_widget->isEnabled()); ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); } From a5476541f2adf5a61820033e5f2d77ab5ab4d5fc Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 6 Oct 2022 09:31:12 -0400 Subject: [PATCH 113/245] video_core: don't block rendering on screenshots --- src/video_core/renderer_base.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 45791aa75e..e8761a747b 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/logging/log.h" #include "core/frontend/emu_window.h" #include "video_core/renderer_base.h" @@ -35,8 +37,12 @@ void RendererBase::RequestScreenshot(void* data, std::function callb LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); return; } + auto async_callback{[callback = std::move(callback)](bool invert_y) { + std::thread t{callback, invert_y}; + t.detach(); + }}; renderer_settings.screenshot_bits = data; - renderer_settings.screenshot_complete_callback = std::move(callback); + renderer_settings.screenshot_complete_callback = async_callback; renderer_settings.screenshot_framebuffer_layout = layout; renderer_settings.screenshot_requested = true; } From ddf64e56af2307bd505f2ad375c31d76072183ff Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 7 Oct 2022 22:19:41 -0400 Subject: [PATCH 114/245] IFriendService: stub CheckFriendListAvailability --- src/core/hle/service/friend/friend.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index e0db787fc6..fad532115e 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -26,7 +26,7 @@ public: {10101, &IFriendService::GetFriendList, "GetFriendList"}, {10102, nullptr, "UpdateFriendInfo"}, {10110, nullptr, "GetFriendProfileImage"}, - {10120, nullptr, "IsFriendListCacheAvailable"}, + {10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"}, {10121, nullptr, "EnsureFriendListAvailable"}, {10200, nullptr, "SendFriendRequestForApplication"}, {10211, nullptr, "AddFacedFriendRequestForApplication"}, @@ -194,6 +194,17 @@ private: // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" } + void CheckFriendListAvailability(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw()}; + + LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(true); + } + KernelHelpers::ServiceContext service_context; Kernel::KEvent* completion_event; From 5b7c0f13d3a47877912b0c4fe917859f82d92df7 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 7 Oct 2022 22:20:29 -0400 Subject: [PATCH 115/245] fsp_srv: stub GetCacheStorageSize --- src/core/hle/service/filesystem/fsp_srv.cpp | 14 +++++++++++++- src/core/hle/service/filesystem/fsp_srv.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index e23eae36a7..c08274ef9f 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -707,7 +707,7 @@ FSP_SRV::FSP_SRV(Core::System& system_) {31, nullptr, "OpenGameCardFileSystem"}, {32, nullptr, "ExtendSaveDataFileSystem"}, {33, nullptr, "DeleteCacheStorage"}, - {34, nullptr, "GetCacheStorageSize"}, + {34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"}, {35, nullptr, "CreateSaveDataFileSystemByHashSalt"}, {36, nullptr, "OpenHostFileSystemWithOption"}, {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"}, @@ -1107,6 +1107,18 @@ void FSP_SRV::GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx) { rb.Push(access_log_program_index); } +void FSP_SRV::GetCacheStorageSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index{rp.Pop()}; + + LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(s64{0}); + rb.Push(s64{0}); +} + class IMultiCommitManager final : public ServiceFramework { public: explicit IMultiCommitManager(Core::System& system_) diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 36f552e05c..3d88b97f96 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -54,6 +54,7 @@ private: void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); void GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx); void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); + void GetCacheStorageSize(Kernel::HLERequestContext& ctx); FileSystemController& fsc; const FileSys::ContentProvider& content_provider; From 47a2efee73b1ac8670cf47d5c0ab3f7e4a0b526b Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 4 Oct 2022 20:05:08 -0400 Subject: [PATCH 116/245] kernel: add expanded result macros --- src/core/hle/result.h | 120 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 47a1b829b2..e20e0bfeed 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/expected.h" @@ -130,6 +131,10 @@ union Result { [[nodiscard]] constexpr bool IsError() const { return !IsSuccess(); } + + [[nodiscard]] constexpr bool IsFailure() const { + return !IsSuccess(); + } }; static_assert(std::is_trivial_v); @@ -349,10 +354,110 @@ private: } \ } while (false) -#define R_SUCCEEDED(res) (res.IsSuccess()) +#define R_SUCCEEDED(res) (static_cast(res).IsSuccess()) +#define R_FAILED(res) (static_cast(res).IsFailure()) -/// Evaluates a boolean expression, and succeeds if that expression is true. -#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) +namespace ResultImpl { +template +class ScopedResultGuard { + YUZU_NON_COPYABLE(ScopedResultGuard); + YUZU_NON_MOVEABLE(ScopedResultGuard); + +private: + Result& m_ref; + F m_f; + +public: + constexpr ScopedResultGuard(Result& ref, F f) : m_ref(ref), m_f(std::move(f)) {} + constexpr ~ScopedResultGuard() { + if (EvaluateResult(m_ref)) { + m_f(); + } + } +}; + +template +class ResultReferenceForScopedResultGuard { +private: + Result& m_ref; + +public: + constexpr ResultReferenceForScopedResultGuard(Result& r) : m_ref(r) {} + constexpr operator Result&() const { + return m_ref; + } +}; + +template +constexpr ScopedResultGuard operator+( + ResultReferenceForScopedResultGuard ref, F&& f) { + return ScopedResultGuard(static_cast(ref), std::forward(f)); +} + +constexpr bool EvaluateResultSuccess(const Result& r) { + return R_SUCCEEDED(r); +} +constexpr bool EvaluateResultFailure(const Result& r) { + return R_FAILED(r); +} + +template +constexpr void UpdateCurrentResultReference(T result_reference, Result result) { + ASSERT(false); +} + +template <> +constexpr void UpdateCurrentResultReference(Result& result_reference, Result result) { + result_reference = result; +} + +template <> +constexpr void UpdateCurrentResultReference(Result result_reference, Result result) {} +} // namespace ResultImpl + +#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \ + [[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \ + std::same_as; \ + [[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \ + [[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \ + Result& __TmpCurrentResultReference = \ + HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE + +#define ON_RESULT_RETURN_IMPL(...) \ + static_assert(std::same_as); \ + auto RESULT_GUARD_STATE_##__COUNTER__ = \ + ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \ + __TmpCurrentResultReference) + \ + [&]() + +#define ON_RESULT_FAILURE_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultFailure) + +#define ON_RESULT_FAILURE \ + DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ + ON_RESULT_FAILURE_2 + +#define ON_RESULT_SUCCESS_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultSuccess) + +#define ON_RESULT_SUCCESS \ + DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ + ON_RESULT_SUCCESS_2 + +constexpr inline Result __TmpCurrentResultReference = ResultSuccess; + +/// Returns a result. +#define R_RETURN(res_expr) \ + { \ + const Result _tmp_r_throw_rc = (res_expr); \ + ResultImpl::UpdateCurrentResultReference( \ + __TmpCurrentResultReference, _tmp_r_throw_rc); \ + return _tmp_r_throw_rc; \ + } + +/// Returns ResultSuccess() +#define R_SUCCEED() R_RETURN(ResultSuccess) + +/// Throws a result. +#define R_THROW(res_expr) R_RETURN(res_expr) /// Evaluates a boolean expression, and returns a result unless that expression is true. #define R_UNLESS(expr, res) \ @@ -361,7 +466,7 @@ private: if (res.IsError()) { \ LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ } \ - return res; \ + R_THROW(res); \ } \ } @@ -369,7 +474,10 @@ private: #define R_TRY(res_expr) \ { \ const auto _tmp_r_try_rc = (res_expr); \ - if (_tmp_r_try_rc.IsError()) { \ - return _tmp_r_try_rc; \ + if (R_FAILED(_tmp_r_try_rc)) { \ + R_THROW(_tmp_r_try_rc); \ } \ } + +/// Evaluates a boolean expression, and succeeds if that expression is true. +#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) From 96324342438a5ce16ab9fbec6d8c17756ef166ff Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 8 Oct 2022 18:27:40 -0400 Subject: [PATCH 117/245] core_timing: use high-precision sleeps on non-Windows targets --- src/core/core_timing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 6c0fcb7b54..2678ce5328 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -270,6 +270,7 @@ void CoreTiming::ThreadLoop() { // There are more events left in the queue, wait until the next event. const auto wait_time = *next_time - GetGlobalTimeNs().count(); if (wait_time > 0) { +#ifdef _WIN32 // Assume a timer resolution of 1ms. static constexpr s64 TimerResolutionNS = 1000000; @@ -287,6 +288,9 @@ void CoreTiming::ThreadLoop() { if (event.IsSet()) { event.Reset(); } +#else + event.WaitFor(std::chrono::nanoseconds(wait_time)); +#endif } } else { // Queue is empty, wait until another event is scheduled and signals us to continue. From 8c9e238a7baef2c3f8a9a608ec8bebeeccfa6676 Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Sat, 8 Oct 2022 19:27:54 +0100 Subject: [PATCH 118/245] Choose the SDL audio backend when Cubeb reports too high of a latency --- src/audio_core/sink/cubeb_sink.cpp | 31 +++++++++- src/audio_core/sink/cubeb_sink.h | 7 +++ src/audio_core/sink/sdl2_sink.cpp | 10 ++-- src/audio_core/sink/sdl2_sink.h | 7 +++ src/audio_core/sink/sink_details.cpp | 66 ++++++++++++++-------- src/audio_core/sink/sink_details.h | 2 +- src/yuzu/configuration/configure_audio.cpp | 4 +- 7 files changed, 95 insertions(+), 32 deletions(-) diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 36b115ad69..32c1b1cb36 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -66,10 +66,10 @@ public: const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency); if (latency_error != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); - minimum_latency = 256U; + minimum_latency = TargetSampleCount * 2; } - minimum_latency = std::max(minimum_latency, 256u); + minimum_latency = std::max(minimum_latency, TargetSampleCount * 2); LOG_INFO(Service_Audio, "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " @@ -326,4 +326,31 @@ std::vector ListCubebSinkDevices(bool capture) { return device_list; } +u32 GetCubebLatency() { + cubeb* ctx; + + if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); + // Return a large latency so we choose SDL instead. + return 10000u; + } + + cubeb_stream_params params{}; + params.rate = TargetSampleRate; + params.channels = 2; + params.format = CUBEB_SAMPLE_S16LE; + params.prefs = CUBEB_STREAM_PREF_NONE; + params.layout = CUBEB_LAYOUT_STEREO; + + u32 latency{0}; + const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency); + if (latency_error != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); + latency = TargetSampleCount * 2; + } + latency = std::max(latency, TargetSampleCount * 2); + cubeb_destroy(ctx); + return latency; +} + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h index 4b0cb160d4..3302cb98d0 100644 --- a/src/audio_core/sink/cubeb_sink.h +++ b/src/audio_core/sink/cubeb_sink.h @@ -96,4 +96,11 @@ private: */ std::vector ListCubebSinkDevices(bool capture); +/** + * Get the reported latency for this sink. + * + * @return Minimum latency for this sink. + */ +u32 GetCubebLatency(); + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 1bd001b941..f12ebf7fe1 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -47,11 +47,7 @@ public: spec.freq = TargetSampleRate; spec.channels = static_cast(device_channels); spec.format = AUDIO_S16SYS; - if (type == StreamType::Render) { - spec.samples = TargetSampleCount; - } else { - spec.samples = 1024; - } + spec.samples = TargetSampleCount * 2; spec.callback = &SDLSinkStream::DataCallback; spec.userdata = this; @@ -240,4 +236,8 @@ std::vector ListSDLSinkDevices(bool capture) { return device_list; } +u32 GetSDLLatency() { + return TargetSampleCount * 2; +} + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index f01eddc1b4..27ed1ab942 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h @@ -87,4 +87,11 @@ private: */ std::vector ListSDLSinkDevices(bool capture); +/** + * Get the reported latency for this sink. + * + * @return Minimum latency for this sink. + */ +u32 GetSDLLatency(); + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 67bdab7799..b878bf23f8 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp @@ -21,58 +21,80 @@ namespace { struct SinkDetails { using FactoryFn = std::unique_ptr (*)(std::string_view); using ListDevicesFn = std::vector (*)(bool); + using LatencyFn = u32 (*)(); /// Name for this sink. - const char* id; + std::string_view id; /// A method to call to construct an instance of this type of sink. FactoryFn factory; /// A method to call to list available devices. ListDevicesFn list_devices; + /// Method to get the latency of this backend. + LatencyFn latency; }; // sink_details is ordered in terms of desirability, with the best choice at the top. constexpr SinkDetails sink_details[] = { #ifdef HAVE_CUBEB - SinkDetails{"cubeb", - [](std::string_view device_id) -> std::unique_ptr { - return std::make_unique(device_id); - }, - &ListCubebSinkDevices}, + SinkDetails{ + "cubeb", + [](std::string_view device_id) -> std::unique_ptr { + return std::make_unique(device_id); + }, + &ListCubebSinkDevices, + &GetCubebLatency, + }, #endif #ifdef HAVE_SDL2 - SinkDetails{"sdl2", - [](std::string_view device_id) -> std::unique_ptr { - return std::make_unique(device_id); - }, - &ListSDLSinkDevices}, + SinkDetails{ + "sdl", + [](std::string_view device_id) -> std::unique_ptr { + return std::make_unique(device_id); + }, + &ListSDLSinkDevices, + &GetSDLLatency, + }, #endif SinkDetails{"null", [](std::string_view device_id) -> std::unique_ptr { return std::make_unique(device_id); }, - [](bool capture) { return std::vector{"null"}; }}, + [](bool capture) { return std::vector{"null"}; }, []() { return 0u; }}, }; const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { - auto iter = - std::find_if(std::begin(sink_details), std::end(sink_details), - [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); + const auto find_backend{[](std::string_view id) { + return std::find_if(std::begin(sink_details), std::end(sink_details), + [&id](const auto& sink_detail) { return sink_detail.id == id; }); + }}; - if (sink_id == "auto" || iter == std::end(sink_details)) { - if (sink_id != "auto") { - LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); + auto iter = find_backend(sink_id); + + if (sink_id == "auto") { + // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which + // causes audio issues, in that case go with SDL. +#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) + iter = find_backend("cubeb"); + if (iter->latency() > TargetSampleCount * 3) { + iter = find_backend("sdl"); } - // Auto-select. - // sink_details is ordered in terms of desirability, with the best choice at the front. +#else iter = std::begin(sink_details); +#endif + LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); + } + + if (iter == std::end(sink_details)) { + LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); + iter = find_backend("null"); } return *iter; } } // Anonymous namespace -std::vector GetSinkIDs() { - std::vector sink_ids(std::size(sink_details)); +std::vector GetSinkIDs() { + std::vector sink_ids(std::size(sink_details)); std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), [](const auto& sink) { return sink.id; }); diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h index 3ebdb1e305..e75932898c 100644 --- a/src/audio_core/sink/sink_details.h +++ b/src/audio_core/sink/sink_details.h @@ -19,7 +19,7 @@ class Sink; * * @return Vector of available sink names. */ -std::vector GetSinkIDs(); +std::vector GetSinkIDs(); /** * Gets the list of devices for a particular sink identified by the given ID. diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 19b8b15ef5..70cc6f84bd 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -161,8 +161,8 @@ void ConfigureAudio::InitializeAudioSinkComboBox() { ui->sink_combo_box->clear(); ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); - for (const char* id : AudioCore::Sink::GetSinkIDs()) { - ui->sink_combo_box->addItem(QString::fromUtf8(id)); + for (const auto& id : AudioCore::Sink::GetSinkIDs()) { + ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast(id.length()))); } } From 224a19758efff450b78dbba43c80f73e55599074 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 9 Oct 2022 12:49:07 -0500 Subject: [PATCH 119/245] input_common: have an unique vector in callback status --- src/common/input.h | 5 +++-- src/core/hid/input_converter.cpp | 10 ++++++++-- src/input_common/input_poller.cpp | 10 ++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/common/input.h b/src/common/input.h index bfa0639f5e..b533f3844c 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -277,8 +277,9 @@ struct CallbackStatus { BodyColorStatus color_status{}; BatteryStatus battery_status{}; VibrationStatus vibration_status{}; - CameraStatus camera_status{}; - NfcStatus nfc_status{}; + CameraFormat camera_status{CameraFormat::None}; + NfcState nfc_status{NfcState::Unknown}; + std::vector raw_data{}; }; // Triggered once every input change diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index fe9915abe6..5d8b75b508 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -277,7 +277,10 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu Common::Input::CameraStatus camera{}; switch (callback.type) { case Common::Input::InputType::IrSensor: - camera = callback.camera_status; + camera = { + .format = callback.camera_status, + .data = callback.raw_data, + }; break; default: LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); @@ -291,7 +294,10 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal Common::Input::NfcStatus nfc{}; switch (callback.type) { case Common::Input::InputType::Nfc: - return callback.nfc_status; + nfc = { + .state = callback.nfc_status, + .data = callback.raw_data, + }; break; default: LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 75705b67e7..ca33fb4eb2 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -691,9 +691,12 @@ public: } void OnChange() { + const auto camera_status = GetStatus(); + const Common::Input::CallbackStatus status{ .type = Common::Input::InputType::IrSensor, - .camera_status = GetStatus(), + .camera_status = camera_status.format, + .raw_data = camera_status.data, }; TriggerOnChange(status); @@ -732,9 +735,12 @@ public: } void OnChange() { + const auto nfc_status = GetStatus(); + const Common::Input::CallbackStatus status{ .type = Common::Input::InputType::Nfc, - .nfc_status = GetStatus(), + .nfc_status = nfc_status.state, + .raw_data = nfc_status.data, }; TriggerOnChange(status); From 682c50715ce3240144507a3b0d965e45c5616c3b Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Sun, 9 Oct 2022 13:56:31 -0400 Subject: [PATCH 120/245] ci/windows: Revert to using GCC for MinGW builds Using MinGW in the future may not be ideal as it does not work very well with crash dumps (#8682). Switch back to GCC on MinGW. This also gives CI a way to check GCC 12 (as of writing, or whatever version of mingw-gcc Arch happens to be shipping on a given week). --- .ci/scripts/windows/docker.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index 6f522feed0..0be3613aab 100755 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh @@ -10,13 +10,9 @@ set -e ccache -sv mkdir -p build && cd build -export LDFLAGS="-fuse-ld=lld" -# -femulated-tls required due to an incompatibility between GCC and Clang -# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate -export CXXFLAGS="-femulated-tls" cmake .. \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWClangCross.cmake" \ + -DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \ -DDISPLAY_VERSION="$1" \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DENABLE_QT_TRANSLATION=ON \ From eb74ef474b6ff98db2b39b5fee43d51f404dbca4 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Mon, 10 Oct 2022 13:32:09 -0500 Subject: [PATCH 121/245] yuzu: Add 16:10 aspect ratio --- src/core/frontend/framebuffer_layout.cpp | 2 ++ src/core/frontend/framebuffer_layout.h | 1 + src/yuzu/configuration/configure_graphics.ui | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 90dd68ff15..b4081fc394 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -67,6 +67,8 @@ float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) { return 3.0f / 4.0f; case AspectRatio::R21_9: return 9.0f / 21.0f; + case AspectRatio::R16_10: + return 10.0f / 16.0f; case AspectRatio::StretchToWindow: return window_aspect_ratio; default: diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 1561d994ef..94683b30f2 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -27,6 +27,7 @@ enum class AspectRatio { Default, R4_3, R21_9, + R16_10, StretchToWindow, }; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 1e4f74704b..fdbb333725 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -299,6 +299,11 @@ Force 21:9 + + + Force 16:10 + + Stretch to Window From 4496030ea9bd873d101628a8eb943b976ec7b07a Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Mon, 10 Oct 2022 20:32:38 +0100 Subject: [PATCH 122/245] Fix stencil func registers, make clip control equivalent to how it was before, but surely wrong. --- src/video_core/dirty_flags.cpp | 2 +- src/video_core/engines/maxwell_3d.cpp | 8 +++---- src/video_core/engines/maxwell_3d.h | 22 ++++++++++--------- .../renderer_opengl/gl_rasterizer.cpp | 17 +++++++++----- .../renderer_opengl/gl_state_tracker.cpp | 14 +++++------- .../renderer_vulkan/fixed_pipeline_state.cpp | 6 ++++- .../renderer_vulkan/vk_rasterizer.cpp | 14 +++++------- .../renderer_vulkan/vk_state_tracker.cpp | 12 +++++----- 8 files changed, 51 insertions(+), 44 deletions(-) diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp index 1039e036fe..c2ecc12f56 100644 --- a/src/video_core/dirty_flags.cpp +++ b/src/video_core/dirty_flags.cpp @@ -61,7 +61,7 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { } void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(pipelines), NUM(pipelines) * Maxwell3D::Regs::MaxShaderProgram, + FillBlock(tables[0], OFF(pipelines), NUM(pipelines[0]) * Maxwell3D::Regs::MaxShaderProgram, Shaders); } } // Anonymous namespace diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 84c1abf3d0..89a9d1f5ac 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -74,15 +74,15 @@ void Maxwell3D::InitializeRegisterDefaults() { regs.stencil_front_op.zfail = Regs::StencilOp::Op::Keep_D3D; regs.stencil_front_op.zpass = Regs::StencilOp::Op::Keep_D3D; regs.stencil_front_op.func = Regs::ComparisonOp::Always_GL; - regs.stencil_front_func.func_mask = 0xFFFFFFFF; - regs.stencil_front_func.mask = 0xFFFFFFFF; + regs.stencil_front_func_mask = 0xFFFFFFFF; + regs.stencil_front_mask = 0xFFFFFFFF; regs.stencil_two_side_enable = 1; regs.stencil_back_op.fail = Regs::StencilOp::Op::Keep_D3D; regs.stencil_back_op.zfail = Regs::StencilOp::Op::Keep_D3D; regs.stencil_back_op.zpass = Regs::StencilOp::Op::Keep_D3D; regs.stencil_back_op.func = Regs::ComparisonOp::Always_GL; - regs.stencil_back_func.func_mask = 0xFFFFFFFF; - regs.stencil_back_func.mask = 0xFFFFFFFF; + regs.stencil_back_func_mask = 0xFFFFFFFF; + regs.stencil_back_mask = 0xFFFFFFFF; regs.depth_test_func = Regs::ComparisonOp::Always_GL; regs.gl_front_face = Regs::FrontFace::CounterClockWise; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index efe1073b06..12dbd9cc40 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1795,12 +1795,6 @@ public: ComparisonOp func; }; - struct StencilFunc { - s32 ref; - u32 func_mask; - u32 mask; - }; - struct PsSaturate { // Opposite of DepthMode enum class Depth : u32 { @@ -2737,7 +2731,9 @@ public: u32 post_z_pixel_imask; ///< 0x0F1C INSERT_PADDING_BYTES_NOINIT(0x20); ConstantColorRendering const_color_rendering; ///< 0x0F40 - StencilFunc stencil_back_func; ///< 0x0F54 + s32 stencil_back_ref; ///< 0x0F54 + u32 stencil_back_mask; ///< 0x0F58 + u32 stencil_back_func_mask; ///< 0x0F5C INSERT_PADDING_BYTES_NOINIT(0x24); VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84 u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C @@ -2855,7 +2851,9 @@ public: Blend blend; ///< 0x133C u32 stencil_enable; ///< 0x1380 StencilOp stencil_front_op; ///< 0x1384 - StencilFunc stencil_front_func; ///< 0x1394 + s32 stencil_front_ref; ///< 0x1394 + s32 stencil_front_func_mask; ///< 0x1398 + s32 stencil_front_mask; ///< 0x139C INSERT_PADDING_BYTES_NOINIT(0x4); u32 draw_auto_start_byte_count; ///< 0x13A4 PsSaturate frag_color_clamp; ///< 0x13A8 @@ -3311,7 +3309,9 @@ ASSERT_REG_POSITION(vpc_perf, 0x0F14); ASSERT_REG_POSITION(pm_local_trigger, 0x0F18); ASSERT_REG_POSITION(post_z_pixel_imask, 0x0F1C); ASSERT_REG_POSITION(const_color_rendering, 0x0F40); -ASSERT_REG_POSITION(stencil_back_func, 0x0F54); +ASSERT_REG_POSITION(stencil_back_ref, 0x0F54); +ASSERT_REG_POSITION(stencil_back_mask, 0x0F58); +ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C); ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84); ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C); ASSERT_REG_POSITION(color_mask_common, 0x0F90); @@ -3416,7 +3416,9 @@ ASSERT_REG_POSITION(invalidate_texture_data_cache_lines, 0x1338); ASSERT_REG_POSITION(blend, 0x133C); ASSERT_REG_POSITION(stencil_enable, 0x1380); ASSERT_REG_POSITION(stencil_front_op, 0x1384); -ASSERT_REG_POSITION(stencil_front_func, 0x1394); +ASSERT_REG_POSITION(stencil_front_ref, 0x1394); +ASSERT_REG_POSITION(stencil_front_func_mask, 0x1398); +ASSERT_REG_POSITION(stencil_front_mask, 0x139C); ASSERT_REG_POSITION(draw_auto_start_byte_count, 0x13A4); ASSERT_REG_POSITION(frag_color_clamp, 0x13A8); ASSERT_REG_POSITION(window_origin, 0x13AC); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index cce00cea8b..e5c09a969b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -658,8 +658,13 @@ void RasterizerOpenGL::SyncDepthClamp() { } flags[Dirty::DepthClampEnabled] = false; - oglEnable(GL_DEPTH_CLAMP, maxwell3d->regs.viewport_clip_control.geometry_clip != - Maxwell::ViewportClipControl::GeometryClip::Passthrough); + bool depth_clamp_disabled{maxwell3d->regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + maxwell3d->regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + maxwell3d->regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ}; + oglEnable(GL_DEPTH_CLAMP, !depth_clamp_disabled); } void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { @@ -746,19 +751,19 @@ void RasterizerOpenGL::SyncStencilTestState() { oglEnable(GL_STENCIL_TEST, regs.stencil_enable); glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_op.func), - regs.stencil_front_func.ref, regs.stencil_front_func.func_mask); + regs.stencil_front_ref, regs.stencil_front_func_mask); glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op.fail), MaxwellToGL::StencilOp(regs.stencil_front_op.zfail), MaxwellToGL::StencilOp(regs.stencil_front_op.zpass)); - glStencilMaskSeparate(GL_FRONT, regs.stencil_front_func.mask); + glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask); if (regs.stencil_two_side_enable) { glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), - regs.stencil_back_func.ref, regs.stencil_back_func.mask); + regs.stencil_back_ref, regs.stencil_back_mask); glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail), MaxwellToGL::StencilOp(regs.stencil_back_op.zfail), MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); - glStencilMaskSeparate(GL_BACK, regs.stencil_back_func.mask); + glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask); } else { glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF); glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP); diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index e2c709aac4..a359f96f1a 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -100,14 +100,12 @@ void SetupDirtyDepthTest(Tables& tables) { void SetupDirtyStencilTest(Tables& tables) { static constexpr std::array offsets = { - OFF(stencil_enable), OFF(stencil_front_op.func), - OFF(stencil_front_func.ref), OFF(stencil_front_func.func_mask), - OFF(stencil_front_op.fail), OFF(stencil_front_op.zfail), - OFF(stencil_front_op.zpass), OFF(stencil_front_func.mask), - OFF(stencil_two_side_enable), OFF(stencil_back_op.func), - OFF(stencil_back_func.ref), OFF(stencil_back_func.func_mask), - OFF(stencil_back_op.fail), OFF(stencil_back_op.zfail), - OFF(stencil_back_op.zpass), OFF(stencil_back_func.mask)}; + OFF(stencil_enable), OFF(stencil_front_op.func), OFF(stencil_front_ref), + OFF(stencil_front_func_mask), OFF(stencil_front_op.fail), OFF(stencil_front_op.zfail), + OFF(stencil_front_op.zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable), + OFF(stencil_back_op.func), OFF(stencil_back_ref), OFF(stencil_back_func_mask), + OFF(stencil_back_op.fail), OFF(stencil_back_op.zfail), OFF(stencil_back_op.zpass), + OFF(stencil_back_mask)}; for (const auto offset : offsets) { tables[0][offset] = StencilTest; } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index eb7c22fd5a..eab1b8f936 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -63,7 +63,11 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::Passthrough); + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); patch_control_points_minus_one.Assign(regs.patch_vertices - 1); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 892cd94a30..47dfb45a1d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -772,11 +772,10 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) if (regs.stencil_two_side_enable) { // Separate values per face scheduler.Record( - [front_ref = regs.stencil_front_func.ref, - front_write_mask = regs.stencil_front_func.mask, - front_test_mask = regs.stencil_front_func.func_mask, - back_ref = regs.stencil_back_func.ref, back_write_mask = regs.stencil_back_func.mask, - back_test_mask = regs.stencil_back_func.func_mask](vk::CommandBuffer cmdbuf) { + [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask, + front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref, + back_write_mask = regs.stencil_back_mask, + back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { // Front face cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT, front_ref); cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front_write_mask); @@ -789,9 +788,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) }); } else { // Front face defines both faces - scheduler.Record([ref = regs.stencil_front_func.ref, - write_mask = regs.stencil_front_func.mask, - test_mask = regs.stencil_front_func.func_mask](vk::CommandBuffer cmdbuf) { + scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask, + test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index ed98c83701..b87c3be669 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -77,12 +77,12 @@ void SetupDirtyDepthBounds(Tables& tables) { void SetupDirtyStencilProperties(Tables& tables) { auto& table = tables[0]; table[OFF(stencil_two_side_enable)] = StencilProperties; - table[OFF(stencil_front_func.ref)] = StencilProperties; - table[OFF(stencil_front_func.mask)] = StencilProperties; - table[OFF(stencil_front_func.func_mask)] = StencilProperties; - table[OFF(stencil_back_func.ref)] = StencilProperties; - table[OFF(stencil_back_func.mask)] = StencilProperties; - table[OFF(stencil_back_func.func_mask)] = StencilProperties; + table[OFF(stencil_front_ref)] = StencilProperties; + table[OFF(stencil_front_mask)] = StencilProperties; + table[OFF(stencil_front_func_mask)] = StencilProperties; + table[OFF(stencil_back_ref)] = StencilProperties; + table[OFF(stencil_back_mask)] = StencilProperties; + table[OFF(stencil_back_func_mask)] = StencilProperties; } void SetupDirtyLineWidth(Tables& tables) { From b1cd6cec19de46540db497137e2b93fee5c9ff17 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 10 Oct 2022 19:22:26 -0400 Subject: [PATCH 123/245] syncpoint_manager: ensure handle is removable before removing --- src/video_core/host1x/syncpoint_manager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp index 326e8355ad..a44fc83d3d 100644 --- a/src/video_core/host1x/syncpoint_manager.cpp +++ b/src/video_core/host1x/syncpoint_manager.cpp @@ -36,7 +36,17 @@ SyncpointManager::ActionHandle SyncpointManager::RegisterAction( void SyncpointManager::DeregisterAction(std::list& action_storage, ActionHandle& handle) { std::unique_lock lk(guard); - action_storage.erase(handle); + + // We want to ensure the iterator still exists prior to erasing it + // Otherwise, if an invalid iterator was passed in then it could lead to UB + // It is important to avoid UB in that case since the deregister isn't called from a locked + // context + for (auto it = action_storage.begin(); it != action_storage.end(); it++) { + if (it == handle) { + action_storage.erase(it); + return; + } + } } void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) { From 6bcd676b610773727f446a8c81bcca1c64a95b10 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 11 Oct 2022 18:15:30 -0400 Subject: [PATCH 124/245] general: preliminary support for hbl --- src/core/hle/service/ns/ns.cpp | 30 ++++++++++- src/core/hle/service/ns/ns.h | 3 ++ src/core/hle/service/ptm/ts.cpp | 15 ++++-- src/core/hle/service/ptm/ts.h | 1 + src/core/hle/service/set/set_sys.cpp | 79 +++++++++++++++++++++++++++- src/core/hle/service/set/set_sys.h | 2 + 6 files changed, 124 insertions(+), 6 deletions(-) diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index f7318c3cb8..f59a1a63d0 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -8,6 +8,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/ns/errors.h" #include "core/hle/service/ns/iplatform_service_manager.h" #include "core/hle/service/ns/language.h" @@ -581,7 +582,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "GetApplicationControlData"}, + {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"}, {1, nullptr, "GetApplicationDesiredLanguage"}, {2, nullptr, "ConvertApplicationLanguageToLanguageCode"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, @@ -594,6 +595,33 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; +void IReadOnlyApplicationControlDataInterface::GetApplicationControlData( + Kernel::HLERequestContext& ctx) { + enum class ApplicationControlSource : u8 { + CacheOnly, + Storage, + StorageOnly, + }; + + struct RequestParameters { + ApplicationControlSource source; + u64 application_id; + }; + static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size."); + + IPC::RequestParser rp{ctx}; + const auto parameters{rp.PopRaw()}; + const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)}; + const auto result = nacp_data ? ResultSuccess : ResultUnknown; + + if (nacp_data) { + ctx.WriteBuffer(nacp_data->data(), nacp_data->size()); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { // clang-format off static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 4dc1915180..9c18e935c5 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -78,6 +78,9 @@ class IReadOnlyApplicationControlDataInterface final public: explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); ~IReadOnlyApplicationControlDataInterface() override; + +private: + void GetApplicationControlData(Kernel::HLERequestContext& ctx); }; class NS final : public ServiceFramework { diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp index 65c3f135f4..b1a0a5544a 100644 --- a/src/core/hle/service/ptm/ts.cpp +++ b/src/core/hle/service/ptm/ts.cpp @@ -15,7 +15,7 @@ TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} { {0, nullptr, "GetTemperatureRange"}, {1, &TS::GetTemperature, "GetTemperature"}, {2, nullptr, "SetMeasurementMode"}, - {3, nullptr, "GetTemperatureMilliC"}, + {3, &TS::GetTemperatureMilliC, "GetTemperatureMilliC"}, {4, nullptr, "OpenSession"}, }; // clang-format on @@ -29,8 +29,6 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto location{rp.PopEnum()}; - LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location); - const s32 temperature = location == Location::Internal ? 35 : 20; IPC::ResponseBuilder rb{ctx, 3}; @@ -38,4 +36,15 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) { rb.Push(temperature); } +void TS::GetTemperatureMilliC(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto location{rp.PopEnum()}; + + const s32 temperature = location == Location::Internal ? 35000 : 20000; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(temperature); +} + } // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h index 39a734ef7a..39d51847ee 100644 --- a/src/core/hle/service/ptm/ts.h +++ b/src/core/hle/service/ptm/ts.h @@ -20,6 +20,7 @@ private: }; void GetTemperature(Kernel::HLERequestContext& ctx); + void GetTemperatureMilliC(Kernel::HLERequestContext& ctx); }; } // namespace Service::PTM diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 2a0b812c1e..d7cea6aac8 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -101,6 +101,81 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } +// FIXME: implement support for the real system_settings.ini + +template +static std::vector ToBytes(const T& value) { + static_assert(std::is_trivially_copyable_v); + + const auto* begin = reinterpret_cast(&value); + const auto* end = begin + sizeof(T); + + return std::vector(begin, end); +} + +using Settings = + std::map, std::less<>>, std::less<>>; + +static Settings GetSettings() { + Settings ret; + + ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); + ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); + + return ret; +} + +void SET_SYS::GetSettingsItemValueSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + // The category of the setting. This corresponds to the top-level keys of + // system_settings.ini. + const auto setting_category_buf{ctx.ReadBuffer(0)}; + const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + + // The name of the setting. This corresponds to the second-level keys of + // system_settings.ini. + const auto setting_name_buf{ctx.ReadBuffer(1)}; + const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + + auto settings{GetSettings()}; + u64 response_size{0}; + + if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { + response_size = settings[setting_category][setting_name].size(); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess); + rb.Push(response_size); +} + +void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + // The category of the setting. This corresponds to the top-level keys of + // system_settings.ini. + const auto setting_category_buf{ctx.ReadBuffer(0)}; + const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + + // The name of the setting. This corresponds to the second-level keys of + // system_settings.ini. + const auto setting_name_buf{ctx.ReadBuffer(1)}; + const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + + auto settings{GetSettings()}; + Result response{ResultUnknown}; + + if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { + auto setting_value = settings[setting_category][setting_name]; + ctx.WriteBuffer(setting_value.data(), setting_value.size()); + response = ResultSuccess; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(response); +} + SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { // clang-format off static const FunctionInfo functions[] = { @@ -138,8 +213,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {32, nullptr, "SetAccountNotificationSettings"}, {35, nullptr, "GetVibrationMasterVolume"}, {36, nullptr, "SetVibrationMasterVolume"}, - {37, nullptr, "GetSettingsItemValueSize"}, - {38, nullptr, "GetSettingsItemValue"}, + {37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, + {38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"}, {39, nullptr, "GetTvSettings"}, {40, nullptr, "SetTvSettings"}, {41, nullptr, "GetEdid"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index ac97772b78..258ef8c578 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -23,6 +23,8 @@ private: BasicBlack = 1, }; + void GetSettingsItemValueSize(Kernel::HLERequestContext& ctx); + void GetSettingsItemValue(Kernel::HLERequestContext& ctx); void GetFirmwareVersion(Kernel::HLERequestContext& ctx); void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); void GetColorSetId(Kernel::HLERequestContext& ctx); From 9b34afa588c4e4bf312e2812ffe6879e09dafc75 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 3 Oct 2022 22:52:52 -0400 Subject: [PATCH 125/245] Add implementation of svcCreateSession --- src/core/hle/kernel/svc.cpp | 90 +++++++++++++++++++++++++++++++++- src/core/hle/kernel/svc_wrap.h | 14 ++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 27e5a805db..3a89511aa7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -29,6 +29,7 @@ #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread.h" @@ -256,6 +257,93 @@ static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u3 return UnmapMemory(system, dst_addr, src_addr, size); } +template +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { + auto& process = *system.CurrentProcess(); + auto& handle_table = process.GetHandleTable(); + + // Declare the session we're going to allocate. + T* session; + + // Reserve a new session from the process resource limit. + // FIXME: LimitableResource_SessionCountMax + KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions); + if (session_reservation.Succeeded()) { + session = T::Create(system.Kernel()); + } else { + return ResultLimitReached; + + // // We couldn't reserve a session. Check that we support dynamically expanding the + // // resource limit. + // R_UNLESS(process.GetResourceLimit() == + // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); + // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); + + // // Try to allocate a session from unused slab memory. + // session = T::CreateFromUnusedSlabMemory(); + // R_UNLESS(session != nullptr, ResultLimitReached); + // ON_RESULT_FAILURE { session->Close(); }; + + // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to + // // prevent request exhaustion. + // // NOTE: Nintendo checks if session->DynamicCast() != nullptr, but there's + // // no reason to not do this statically. + // if constexpr (std::same_as) { + // for (size_t i = 0; i < 2; i++) { + // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); + // R_UNLESS(request != nullptr, ResultLimitReached); + // request->Close(); + // } + // } + + // We successfully allocated a session, so add the object we allocated to the resource + // limit. + // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1); + } + + // Check that we successfully created a session. + R_UNLESS(session != nullptr, ResultOutOfResource); + + // Initialize the session. + session->Initialize(nullptr, fmt::format("{}", name)); + + // Commit the session reservation. + session_reservation.Commit(); + + // Ensure that we clean up the session (and its only references are handle table) on function + // end. + SCOPE_EXIT({ + session->GetClientSession().Close(); + session->GetServerSession().Close(); + }); + + // Register the session. + T::Register(system.Kernel(), session); + + // Add the server session to the handle table. + R_TRY(handle_table.Add(out_server, &session->GetServerSession())); + + // Add the client session to the handle table. */ + const auto result = handle_table.Add(out_client, &session->GetClientSession()); + + if (!R_SUCCEEDED(result)) { + // Ensure that we maintaing a clean handle state on exit. + handle_table.Remove(*out_server); + } + + return result; +} + +static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, + u32 is_light, u64 name) { + if (is_light) { + // return CreateSession(system, out_server, out_client, name); + return ResultUnknown; + } else { + return CreateSession(system, out_server, out_client, name); + } +} + /// Connect to an OS service given the port name, returns the handle to the port to out static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { auto& memory = system.Memory(); @@ -2860,7 +2948,7 @@ static const FunctionDef SVC_Table_64[] = { {0x3D, SvcWrap64, "ChangeKernelTraceState"}, {0x3E, nullptr, "Unknown3e"}, {0x3F, nullptr, "Unknown3f"}, - {0x40, nullptr, "CreateSession"}, + {0x40, SvcWrap64, "CreateSession"}, {0x41, nullptr, "AcceptSession"}, {0x42, nullptr, "ReplyAndReceiveLight"}, {0x43, nullptr, "ReplyAndReceive"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 4bc49087e2..16bf65802a 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -346,6 +346,20 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } +// Used by CreateSession +template +void SvcWrap64(Core::System& system) { + Handle param_1 = 0; + Handle param_2 = 0; + const u32 retval = func(system, ¶m_1, ¶m_2, static_cast(Param(system, 2)), + static_cast(Param(system, 3))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + FuncReturn(system, retval); +} + // Used by WaitForAddress template void SvcWrap64(Core::System& system) { From 61a8696510b3bca120f1f0289a3829e3db14834e Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 11 Oct 2022 18:16:56 -0400 Subject: [PATCH 126/245] k_server_session: preliminary support for userspace server sessions --- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 1 + src/core/hle/ipc_helpers.h | 3 +- src/core/hle/kernel/k_client_session.cpp | 5 +- src/core/hle/kernel/k_client_session.h | 3 +- src/core/hle/kernel/k_server_session.cpp | 243 ++++++++++++++++++++-- src/core/hle/kernel/k_server_session.h | 37 ++-- src/core/hle/kernel/svc.cpp | 82 ++++++-- src/core/hle/kernel/svc_wrap.h | 18 ++ src/core/hle/service/sm/sm.cpp | 3 +- 9 files changed, 346 insertions(+), 49 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1d46f6d401..22b5d56568 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -111,6 +111,7 @@ public: LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, num_instructions, memory.Read32(pc)); + ReturnException(pc, ARM_Interface::no_execute); } void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index d631c03576..0cc26a2118 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -152,7 +152,8 @@ public: Kernel::LimitableResource::Sessions, 1); auto* session = Kernel::KSession::Create(kernel); - session->Initialize(nullptr, iface->GetServiceName()); + session->Initialize(nullptr, iface->GetServiceName(), + std::make_shared(kernel)); context->AddMoveObject(&session->GetClientSession()); iface->ClientConnected(&session->GetServerSession()); diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index b2a887b14f..8892c5b7cb 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -21,10 +21,9 @@ void KClientSession::Destroy() { void KClientSession::OnServerClosed() {} -Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { +Result KClientSession::SendSyncRequest() { // Signal the server session that new data is available - return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing); + return parent->GetServerSession().OnRequest(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index 0c750d7567..b4a19c5460 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -46,8 +46,7 @@ public: return parent; } - Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); + Result SendSyncRequest(); void OnServerClosed(); diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 802c646a6d..4252c9adbd 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -7,6 +7,8 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/scope_exit.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -18,13 +20,19 @@ #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/service_thread.h" #include "core/memory.h" namespace Kernel { -KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} +using ThreadQueueImplForKServerSessionRequest = KThreadQueue; + +static constexpr u32 MessageBufferSize = 0x100; + +KServerSession::KServerSession(KernelCore& kernel_) + : KSynchronizationObject{kernel_}, m_lock{kernel_} {} KServerSession::~KServerSession() = default; @@ -33,17 +41,14 @@ void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, // Set member variables. parent = parent_session_; name = std::move(name_); - - if (manager_) { - manager = manager_; - } else { - manager = std::make_shared(kernel); - } + manager = manager_; } void KServerSession::Destroy() { parent->OnServerClosed(); + this->CleanupRequests(); + parent->Close(); // Release host emulation members. @@ -54,13 +59,13 @@ void KServerSession::Destroy() { } void KServerSession::OnClientClosed() { - if (manager->HasSessionHandler()) { + if (manager && manager->HasSessionHandler()) { manager->SessionHandler().ClientDisconnected(this); } } bool KServerSession::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // If the client is closed, we're always signaled. if (parent->IsClientClosed()) { @@ -68,7 +73,7 @@ bool KServerSession::IsSignaled() const { } // Otherwise, we're signaled if we have a request and aren't handling one. - return false; + return !m_thread_request_list.empty() && m_current_thread_request == nullptr; } void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { @@ -173,9 +178,221 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { return result; } -Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { - return QueueSyncRequest(thread, memory); +Result KServerSession::OnRequest() { + // Create the wait queue. + ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; + + { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Ensure that we can handle new requests. + R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed); + + // Check that we're not terminating. + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); + + if (manager) { + // HLE request. + auto& memory{kernel.System().Memory()}; + this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); + } else { + // Non-HLE request. + auto* thread{GetCurrentThreadPointer(kernel)}; + + // Get whether we're empty. + const bool was_empty = m_thread_request_list.empty(); + + // Add the thread to the list. + thread->Open(); + m_thread_request_list.push_back(thread); + + // If we were empty, signal. + if (was_empty) { + this->NotifyAvailable(); + } + } + + // This is a synchronous request, so we should wait for our request to complete. + GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); + GetCurrentThread(kernel).BeginWait(&wait_queue); + } + + return GetCurrentThread(kernel).GetWaitResult(); +} + +Result KServerSession::SendReply() { + // Lock the session. + KScopedLightLock lk(m_lock); + + // Get the request. + KThread* client_thread; + { + KScopedSchedulerLock sl{kernel}; + + // Get the current request. + client_thread = m_current_thread_request; + R_UNLESS(client_thread != nullptr, ResultInvalidState); + + // Clear the current request, since we're processing it. + m_current_thread_request = nullptr; + if (!m_thread_request_list.empty()) { + this->NotifyAvailable(); + } + } + + // Close reference to the request once we're done processing it. + SCOPE_EXIT({ client_thread->Close(); }); + + // Extract relevant information from the request. + // const uintptr_t client_message = request->GetAddress(); + // const size_t client_buffer_size = request->GetSize(); + // KThread *client_thread = request->GetThread(); + // KEvent *event = request->GetEvent(); + + // Check whether we're closed. + const bool closed = (client_thread == nullptr || parent->IsClientClosed()); + + Result result = ResultSuccess; + if (!closed) { + // If we're not closed, send the reply. + Core::Memory::Memory& memory{kernel.System().Memory()}; + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + + auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); + std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); + } else { + result = ResultSessionClosed; + } + + // Select a result for the client. + Result client_result = result; + if (closed && R_SUCCEEDED(result)) { + result = ResultSessionClosed; + client_result = ResultSessionClosed; + } else { + result = ResultSuccess; + } + + // If there's a client thread, update it. + if (client_thread != nullptr) { + // End the client thread's wait. + KScopedSchedulerLock sl{kernel}; + + if (!client_thread->IsTerminationRequested()) { + client_thread->EndWait(client_result); + } + } + + return result; +} + +Result KServerSession::ReceiveRequest() { + // Lock the session. + KScopedLightLock lk(m_lock); + + // Get the request and client thread. + // KSessionRequest *request; + KThread* client_thread; + + { + KScopedSchedulerLock sl{kernel}; + + // Ensure that we can service the request. + R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed); + + // Ensure we aren't already servicing a request. + R_UNLESS(m_current_thread_request == nullptr, ResultNotFound); + + // Ensure we have a request to service. + R_UNLESS(!m_thread_request_list.empty(), ResultNotFound); + + // Pop the first request from the list. + client_thread = m_thread_request_list.front(); + m_thread_request_list.pop_front(); + + // Get the thread for the request. + R_UNLESS(client_thread != nullptr, ResultSessionClosed); + + // Open the client thread. + client_thread->Open(); + } + + // SCOPE_EXIT({ client_thread->Close(); }); + + // Set the request as our current. + m_current_thread_request = client_thread; + + // Receive the message. + Core::Memory::Memory& memory{kernel.System().Memory()}; + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + + auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); + auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); + + // We succeeded. + return ResultSuccess; +} + +void KServerSession::CleanupRequests() { + KScopedLightLock lk(m_lock); + + // Clean up any pending requests. + while (true) { + // Get the next request. + // KSessionRequest *request = nullptr; + KThread* client_thread = nullptr; + { + KScopedSchedulerLock sl{kernel}; + + if (m_current_thread_request) { + // Choose the current request if we have one. + client_thread = m_current_thread_request; + m_current_thread_request = nullptr; + } else if (!m_thread_request_list.empty()) { + // Pop the request from the front of the list. + client_thread = m_thread_request_list.front(); + m_thread_request_list.pop_front(); + } + } + + // If there's no request, we're done. + if (client_thread == nullptr) { + break; + } + + // Close a reference to the request once it's cleaned up. + SCOPE_EXIT({ client_thread->Close(); }); + + // Extract relevant information from the request. + // const uintptr_t client_message = request->GetAddress(); + // const size_t client_buffer_size = request->GetSize(); + // KThread *client_thread = request->GetThread(); + // KEvent *event = request->GetEvent(); + + // KProcess *server_process = request->GetServerProcess(); + // KProcess *client_process = (client_thread != nullptr) ? + // client_thread->GetOwnerProcess() : nullptr; + // KProcessPageTable *client_page_table = (client_process != nullptr) ? + // &client_process->GetPageTable() : nullptr; + + // Cleanup the mappings. + // Result result = CleanupMap(request, server_process, client_page_table); + + // If there's a client thread, update it. + if (client_thread != nullptr) { + // End the client thread's wait. + KScopedSchedulerLock sl{kernel}; + + if (!client_thread->IsTerminationRequested()) { + client_thread->EndWait(ResultSessionClosed); + } + } + } } } // namespace Kernel diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 6d08219453..748d52826c 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,6 +11,7 @@ #include #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" @@ -59,25 +61,15 @@ public: void OnClientClosed(); void ClientConnected(SessionRequestHandlerPtr handler) { - manager->SetSessionHandler(std::move(handler)); + if (manager) { + manager->SetSessionHandler(std::move(handler)); + } } void ClientDisconnected() { manager = nullptr; } - /** - * Handle a sync request from the emulated application. - * - * @param thread Thread that initiated the request. - * @param memory Memory context to handle the sync request under. - * @param core_timing Core timing context to schedule the request event under. - * - * @returns Result from the operation. - */ - Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); - /// Adds a new domain request handler to the collection of request handlers within /// this ServerSession instance. void AppendDomainHandler(SessionRequestHandlerPtr handler); @@ -88,7 +80,7 @@ public: /// Returns true if the session has been converted to a domain, otherwise False bool IsDomain() const { - return manager->IsDomain(); + return manager && manager->IsDomain(); } /// Converts the session to a domain at the end of the current command @@ -101,7 +93,15 @@ public: return manager; } + /// TODO: flesh these out to match the real kernel + Result OnRequest(); + Result SendReply(); + Result ReceiveRequest(); + private: + /// Frees up waiting client sessions when this server session is about to die + void CleanupRequests(); + /// Queues a sync request from the emulated application. Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); @@ -112,7 +112,7 @@ private: /// object handle. Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); - /// This session's HLE request handlers + /// This session's HLE request handlers; if nullptr, this is not an HLE server std::shared_ptr manager; /// When set to True, converts the session to a domain at the end of the command @@ -120,6 +120,13 @@ private: /// KSession that owns this KServerSession KSession* parent{}; + + /// List of threads which are pending a reply. + /// FIXME: KSessionRequest + std::list m_thread_request_list; + KThread* m_current_thread_request{}; + + KLightLock m_lock; }; } // namespace Kernel diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 3a89511aa7..510a9b3e3a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -323,7 +323,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // Add the server session to the handle table. R_TRY(handle_table.Add(out_server, &session->GetServerSession())); - // Add the client session to the handle table. */ + // Add the client session to the handle table. const auto result = handle_table.Add(out_client, &session->GetClientSession()); if (!R_SUCCEEDED(result)) { @@ -383,7 +383,8 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; - R_TRY(port->CreateSession(std::addressof(session))); + R_TRY(port->CreateSession(std::addressof(session), + std::make_shared(kernel))); port->Close(); // Register the session in the table, close the extra reference. @@ -401,7 +402,7 @@ static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, return ConnectToNamedPort(system, out_handle, port_name_address); } -/// Makes a blocking IPC call to an OS service. +/// Makes a blocking IPC call to a service. static Result SendSyncRequest(Core::System& system, Handle handle) { auto& kernel = system.Kernel(); @@ -415,22 +416,75 @@ static Result SendSyncRequest(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - { - KScopedSchedulerLock lock(kernel); - - // This is a synchronous request, so we should wait for our request to complete. - GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue)); - GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); - session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming()); - } - - return GetCurrentThread(kernel).GetWaitResult(); + return session->SendSyncRequest(); } static Result SendSyncRequest32(Core::System& system, Handle handle) { return SendSyncRequest(system, handle); } +static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, + s32 num_handles, Handle reply_target, s64 timeout_ns) { + auto& kernel = system.Kernel(); + auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); + + // Convert handle list to object table. + std::vector objs(num_handles); + R_UNLESS( + handle_table.GetMultipleObjects(objs.data(), handles, num_handles), + ResultInvalidHandle); + + // Ensure handles are closed when we're done. + SCOPE_EXIT({ + for (auto i = 0; i < num_handles; ++i) { + objs[i]->Close(); + } + }); + + // Reply to the target, if one is specified. + if (reply_target != InvalidHandle) { + KScopedAutoObject session = handle_table.GetObject(reply_target); + R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + + // If we fail to reply, we want to set the output index to -1. + // ON_RESULT_FAILURE { *out_index = -1; }; + + // Send the reply. + // R_TRY(session->SendReply()); + + Result rc = session->SendReply(); + if (!R_SUCCEEDED(rc)) { + *out_index = -1; + return rc; + } + } + + // Wait for a message. + while (true) { + // Wait for an object. + s32 index; + Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast(objs.size()), timeout_ns); + if (result == ResultTimedOut) { + return result; + } + + // Receive the request. + if (R_SUCCEEDED(result)) { + KServerSession* session = objs[index]->DynamicCast(); + if (session != nullptr) { + result = session->ReceiveRequest(); + if (result == ResultNotFound) { + continue; + } + } + } + + *out_index = index; + return result; + } +} + /// Get the ID for the specified thread. static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { // Get the thread from its handle. @@ -2951,7 +3005,7 @@ static const FunctionDef SVC_Table_64[] = { {0x40, SvcWrap64, "CreateSession"}, {0x41, nullptr, "AcceptSession"}, {0x42, nullptr, "ReplyAndReceiveLight"}, - {0x43, nullptr, "ReplyAndReceive"}, + {0x43, SvcWrap64, "ReplyAndReceive"}, {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, {0x45, SvcWrap64, "CreateEvent"}, {0x46, nullptr, "MapIoRegion"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 16bf65802a..272c54cf75 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -8,6 +8,7 @@ #include "core/core.h" #include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" +#include "core/memory.h" namespace Kernel { @@ -360,6 +361,23 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } +// Used by ReplyAndReceive +template +void SvcWrap64(Core::System& system) { + s32 param_1 = 0; + s32 num_handles = static_cast(Param(system, 2)); + + std::vector handles(num_handles); + system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle)); + + const u32 retval = func(system, ¶m_1, handles.data(), num_handles, + static_cast(Param(system, 3)), static_cast(Param(system, 4))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); +} + // Used by WaitForAddress template void SvcWrap64(Core::System& system) { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 246c946236..48e70f93c7 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -156,7 +156,8 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port->GetClientPort().CreateSession(std::addressof(session)); + if (const auto result = port->GetClientPort().CreateSession( + std::addressof(session), std::make_shared(kernel)); result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; From a9ace6856de57f1124daaa77aacd6f36a64c68f7 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 12 Oct 2022 20:26:04 -0400 Subject: [PATCH 127/245] kernel: remove KWritableEvent --- src/audio_core/in/audio_in_system.cpp | 6 +-- src/audio_core/out/audio_out_system.cpp | 6 +-- src/audio_core/renderer/system.cpp | 4 +- src/core/CMakeLists.txt | 2 - src/core/hle/kernel/hle_ipc.h | 2 +- src/core/hle/kernel/k_class_token.cpp | 12 ++--- src/core/hle/kernel/k_class_token.h | 1 - src/core/hle/kernel/k_event.cpp | 44 +++++++++++-------- src/core/hle/kernel/k_event.h | 31 ++++++------- src/core/hle/kernel/k_readable_event.cpp | 35 ++++++++++----- src/core/hle/kernel/k_readable_event.h | 17 +++---- src/core/hle/kernel/k_writable_event.cpp | 35 --------------- src/core/hle/kernel/k_writable_event.h | 39 ---------------- src/core/hle/kernel/kernel.h | 4 -- src/core/hle/kernel/svc.cpp | 25 +++++------ src/core/hle/service/acc/async_context.cpp | 2 +- src/core/hle/service/am/am.cpp | 12 ++--- src/core/hle/service/am/applets/applets.cpp | 10 ++--- src/core/hle/service/audio/audren_u.cpp | 4 +- src/core/hle/service/bcat/backend/backend.cpp | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 5 +-- .../hle/service/hid/controllers/palma.cpp | 16 +++---- src/core/hle/service/hid/hidbus/ringcon.cpp | 8 ++-- src/core/hle/service/kernel_helpers.cpp | 5 +-- src/core/hle/service/ldn/ldn.cpp | 2 +- src/core/hle/service/nfp/nfp_device.cpp | 6 +-- src/core/hle/service/nim/nim.cpp | 4 +- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 5 +-- src/core/hle/service/nvdrv/nvdrv.cpp | 1 - src/core/hle/service/nvdrv/nvdrv_interface.h | 4 -- .../nvflinger/buffer_queue_producer.cpp | 9 ++-- .../service/nvflinger/buffer_queue_producer.h | 1 - src/core/hle/service/nvflinger/nvflinger.h | 1 - src/core/hle/service/ptm/psm.cpp | 6 +-- .../system_clock_context_update_callback.cpp | 10 ++--- .../system_clock_context_update_callback.h | 6 +-- .../hle/service/vi/display/vi_display.cpp | 3 +- 37 files changed, 152 insertions(+), 233 deletions(-) delete mode 100644 src/core/hle/kernel/k_writable_event.cpp delete mode 100644 src/core/hle/kernel/k_writable_event.h diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index e7f918a47b..6b7e6715cf 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -23,7 +23,7 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } void System::StartSession() { @@ -142,7 +142,7 @@ void System::ReleaseBuffers() { if (signal) { // Signal if any buffer was released, or if none are registered, we need more. - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } } @@ -159,7 +159,7 @@ bool System::FlushAudioInBuffers() { buffers.FlushBuffers(buffers_released); if (buffers_released > 0) { - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } return true; } diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 8b907590a9..48a8019232 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -24,7 +24,7 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } std::string_view System::GetDefaultOutputDeviceName() const { @@ -141,7 +141,7 @@ void System::ReleaseBuffers() { bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } } @@ -158,7 +158,7 @@ bool System::FlushAudioOutBuffers() { buffers.FlushBuffers(buffers_released); if (buffers_released > 0) { - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } return true; } diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 7a217969e9..bde794cd1c 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -534,7 +534,7 @@ Result System::Update(std::span input, std::span performance, std: return result; } - adsp_rendered_event->GetWritableEvent().Clear(); + adsp_rendered_event->Clear(); num_times_updated++; const auto end_time{core.CoreTiming().GetClockTicks()}; @@ -625,7 +625,7 @@ void System::SendCommandToDsp() { reset_command_buffers = false; command_buffer_size = command_size; if (remaining_command_count == 0) { - adsp_rendered_event->GetWritableEvent().Signal(); + adsp_rendered_event->Signal(); } } else { adsp.ClearRemainCount(session_id); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 95302c419a..abeb5859b5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -261,8 +261,6 @@ add_library(core STATIC hle/kernel/k_worker_task.h hle/kernel/k_worker_task_manager.cpp hle/kernel/k_worker_task_manager.h - hle/kernel/k_writable_event.cpp - hle/kernel/k_writable_event.h hle/kernel/kernel.cpp hle/kernel/kernel.h hle/kernel/memory_types.h diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 99265ce90a..e258e2cdfe 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -43,13 +43,13 @@ class Domain; class HLERequestContext; class KAutoObject; class KernelCore; +class KEvent; class KHandleTable; class KProcess; class KServerSession; class KThread; class KReadableEvent; class KSession; -class KWritableEvent; class ServiceThread; enum class ThreadWakeupReason; diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index cc2a0f7cae..10265c23ce 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp @@ -18,7 +18,6 @@ #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/k_writable_event.h" namespace Kernel { @@ -42,13 +41,12 @@ static_assert(ClassToken == 0b10000101'00000000); static_assert(ClassToken == 0b00011001'00000000); static_assert(ClassToken == 0b00101001'00000000); static_assert(ClassToken == 0b01001001'00000000); -static_assert(ClassToken == 0b10001001'00000000); // static_assert(ClassToken == 0b00110001'00000000); // static_assert(ClassToken == 0b01010001'00000000); -static_assert(ClassToken == 0b10010001'00000000); +static_assert(ClassToken == 0b01010001'00000000); // static_assert(ClassToken == 0b01100001'00000000); // static_assert(ClassToken == 0b10100001'00000000); -static_assert(ClassToken == 0b11000001'00000000); +static_assert(ClassToken == 0b10100001'00000000); // Ensure that the token hierarchy is correct. @@ -73,13 +71,12 @@ static_assert(ClassToken == ((0b10000101 << 8) | ClassToken) static_assert(ClassToken == ((0b00011001 << 8) | ClassToken)); static_assert(ClassToken == ((0b00101001 << 8) | ClassToken)); static_assert(ClassToken == ((0b01001001 << 8) | ClassToken)); -static_assert(ClassToken == ((0b10001001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b00110001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b01010001 << 8) | ClassToken)); -static_assert(ClassToken == ((0b10010001 << 8) | ClassToken)); +static_assert(ClassToken == ((0b01010001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b01100001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b10100001 << 8) | ClassToken)); -static_assert(ClassToken == ((0b11000001 << 8) | ClassToken)); +static_assert(ClassToken == ((0b10100001 << 8) | ClassToken)); // Ensure that the token hierarchy reflects the class hierarchy. @@ -110,7 +107,6 @@ static_assert(std::is_final_v && std::is_base_of_v); static_assert(std::is_final_v && std::is_base_of_v); static_assert(std::is_final_v && std::is_base_of_v); static_assert(std::is_final_v && std::is_base_of_v); -static_assert(std::is_final_v && std::is_base_of_v); // static_assert(std::is_final_v && // std::is_base_of_v); // static_assert(std::is_final_v && diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h index c9001ae3d8..ab20e00ff8 100644 --- a/src/core/hle/kernel/k_class_token.h +++ b/src/core/hle/kernel/k_class_token.h @@ -101,7 +101,6 @@ public: KSession, KSharedMemory, KEvent, - KWritableEvent, KLightClientSession, KLightServerSession, KTransferMemory, diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index e52fafbc7c..78ca594635 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -8,39 +8,45 @@ namespace Kernel { KEvent::KEvent(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_}, readable_event{kernel_}, writable_event{ - kernel_} {} + : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_readable_event{kernel_} {} KEvent::~KEvent() = default; -void KEvent::Initialize(std::string&& name_, KProcess* owner_) { - // Increment reference count. - // Because reference count is one on creation, this will result - // in a reference count of two. Thus, when both readable and - // writable events are closed this object will be destroyed. - Open(); +void KEvent::Initialize(KProcess* owner) { + // Create our readable event. + KAutoObject::Create(std::addressof(m_readable_event)); - // Create our sub events. - KAutoObject::Create(std::addressof(readable_event)); - KAutoObject::Create(std::addressof(writable_event)); - - // Initialize our sub sessions. - readable_event.Initialize(this, name_ + ":Readable"); - writable_event.Initialize(this, name_ + ":Writable"); + // Initialize our readable event. + m_readable_event.Initialize(this); // Set our owner process. - owner = owner_; - owner->Open(); + m_owner = owner; + m_owner->Open(); // Mark initialized. - name = std::move(name_); - initialized = true; + m_initialized = true; } void KEvent::Finalize() { KAutoObjectWithSlabHeapAndContainer::Finalize(); } +Result KEvent::Signal() { + KScopedSchedulerLock sl{kernel}; + + R_SUCCEED_IF(m_readable_event_destroyed); + + return m_readable_event.Signal(); +} + +Result KEvent::Clear() { + KScopedSchedulerLock sl{kernel}; + + R_SUCCEED_IF(m_readable_event_destroyed); + + return m_readable_event.Clear(); +} + void KEvent::PostDestroy(uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast(arg); diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 2ff828feb3..48ce7d9a06 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -4,14 +4,12 @@ #pragma once #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/slab_helpers.h" namespace Kernel { class KernelCore; class KReadableEvent; -class KWritableEvent; class KProcess; class KEvent final : public KAutoObjectWithSlabHeapAndContainer { @@ -21,37 +19,40 @@ public: explicit KEvent(KernelCore& kernel_); ~KEvent() override; - void Initialize(std::string&& name, KProcess* owner_); + void Initialize(KProcess* owner); void Finalize() override; bool IsInitialized() const override { - return initialized; + return m_initialized; } uintptr_t GetPostDestroyArgument() const override { - return reinterpret_cast(owner); + return reinterpret_cast(m_owner); } KProcess* GetOwner() const override { - return owner; + return m_owner; } KReadableEvent& GetReadableEvent() { - return readable_event; - } - - KWritableEvent& GetWritableEvent() { - return writable_event; + return m_readable_event; } static void PostDestroy(uintptr_t arg); + Result Signal(); + Result Clear(); + + void OnReadableEventDestroyed() { + m_readable_event_destroyed = true; + } + private: - KReadableEvent readable_event; - KWritableEvent writable_event; - KProcess* owner{}; - bool initialized{}; + KReadableEvent m_readable_event; + KProcess* m_owner{}; + bool m_initialized{}; + bool m_readable_event_destroyed{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp index 94c5464fe8..5c942d47c8 100644 --- a/src/core/hle/kernel/k_readable_event.cpp +++ b/src/core/hle/kernel/k_readable_event.cpp @@ -15,31 +15,44 @@ KReadableEvent::KReadableEvent(KernelCore& kernel_) : KSynchronizationObject{ker KReadableEvent::~KReadableEvent() = default; -bool KReadableEvent::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); +void KReadableEvent::Initialize(KEvent* parent) { + m_is_signaled = false; + m_parent = parent; - return is_signaled; + if (m_parent != nullptr) { + m_parent->Open(); + } +} + +bool KReadableEvent::IsSignaled() const { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + + return m_is_signaled; } void KReadableEvent::Destroy() { - if (parent) { - parent->Close(); + if (m_parent) { + { + KScopedSchedulerLock sl{kernel}; + m_parent->OnReadableEventDestroyed(); + } + m_parent->Close(); } } Result KReadableEvent::Signal() { KScopedSchedulerLock lk{kernel}; - if (!is_signaled) { - is_signaled = true; - NotifyAvailable(); + if (!m_is_signaled) { + m_is_signaled = true; + this->NotifyAvailable(); } return ResultSuccess; } Result KReadableEvent::Clear() { - Reset(); + this->Reset(); return ResultSuccess; } @@ -47,11 +60,11 @@ Result KReadableEvent::Clear() { Result KReadableEvent::Reset() { KScopedSchedulerLock lk{kernel}; - if (!is_signaled) { + if (!m_is_signaled) { return ResultInvalidState; } - is_signaled = false; + m_is_signaled = false; return ResultSuccess; } diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index 18dcad289b..743f96bf59 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -20,26 +20,23 @@ public: explicit KReadableEvent(KernelCore& kernel_); ~KReadableEvent() override; - void Initialize(KEvent* parent_event_, std::string&& name_) { - is_signaled = false; - parent = parent_event_; - name = std::move(name_); - } + void Initialize(KEvent* parent); KEvent* GetParent() const { - return parent; + return m_parent; } + Result Signal(); + Result Clear(); + bool IsSignaled() const override; void Destroy() override; - Result Signal(); - Result Clear(); Result Reset(); private: - bool is_signaled{}; - KEvent* parent{}; + bool m_is_signaled{}; + KEvent* m_parent{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp deleted file mode 100644 index ff88c5acd3..0000000000 --- a/src/core/hle/kernel/k_writable_event.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" - -namespace Kernel { - -KWritableEvent::KWritableEvent(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_} {} - -KWritableEvent::~KWritableEvent() = default; - -void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) { - parent = parent_event_; - name = std::move(name_); - parent->GetReadableEvent().Open(); -} - -Result KWritableEvent::Signal() { - return parent->GetReadableEvent().Signal(); -} - -Result KWritableEvent::Clear() { - return parent->GetReadableEvent().Clear(); -} - -void KWritableEvent::Destroy() { - // Close our references. - parent->GetReadableEvent().Close(); - parent->Close(); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h deleted file mode 100644 index 3fd0c7d0ae..0000000000 --- a/src/core/hle/kernel/k_writable_event.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/kernel/k_auto_object.h" -#include "core/hle/kernel/slab_helpers.h" -#include "core/hle/result.h" - -namespace Kernel { - -class KernelCore; -class KEvent; - -class KWritableEvent final - : public KAutoObjectWithSlabHeapAndContainer { - KERNEL_AUTOOBJECT_TRAITS(KWritableEvent, KAutoObject); - -public: - explicit KWritableEvent(KernelCore& kernel_); - ~KWritableEvent() override; - - void Destroy() override; - - static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - - void Initialize(KEvent* parent_, std::string&& name_); - Result Signal(); - Result Clear(); - - KEvent* GetParent() const { - return parent; - } - -private: - KEvent* parent{}; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index bcf016a971..0847cbcbf7 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -52,7 +52,6 @@ class KThread; class KThreadLocalPage; class KTransferMemory; class KWorkerTaskManager; -class KWritableEvent; class KCodeMemory; class PhysicalCore; class ServiceThread; @@ -345,8 +344,6 @@ public: return slab_heap_container->thread; } else if constexpr (std::is_same_v) { return slab_heap_container->transfer_memory; - } else if constexpr (std::is_same_v) { - return slab_heap_container->writeable_event; } else if constexpr (std::is_same_v) { return slab_heap_container->code_memory; } else if constexpr (std::is_same_v) { @@ -412,7 +409,6 @@ private: KSlabHeap shared_memory_info; KSlabHeap thread; KSlabHeap transfer_memory; - KSlabHeap writeable_event; KSlabHeap code_memory; KSlabHeap page_buffer; KSlabHeap thread_local_page; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 27e5a805db..035607daf9 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -34,7 +34,6 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/svc.h" @@ -2303,11 +2302,11 @@ static Result SignalEvent(Core::System& system, Handle event_handle) { // Get the current handle table. const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - // Get the writable event. - KScopedAutoObject writable_event = handle_table.GetObject(event_handle); - R_UNLESS(writable_event.IsNotNull(), ResultInvalidHandle); + // Get the event. + KScopedAutoObject event = handle_table.GetObject(event_handle); + R_UNLESS(event.IsNotNull(), ResultInvalidHandle); - return writable_event->Signal(); + return event->Signal(); } static Result SignalEvent32(Core::System& system, Handle event_handle) { @@ -2322,9 +2321,9 @@ static Result ClearEvent(Core::System& system, Handle event_handle) { // Try to clear the writable event. { - KScopedAutoObject writable_event = handle_table.GetObject(event_handle); - if (writable_event.IsNotNull()) { - return writable_event->Clear(); + KScopedAutoObject event = handle_table.GetObject(event_handle); + if (event.IsNotNull()) { + return event->Clear(); } } @@ -2362,24 +2361,24 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r R_UNLESS(event != nullptr, ResultOutOfResource); // Initialize the event. - event->Initialize("CreateEvent", kernel.CurrentProcess()); + event->Initialize(kernel.CurrentProcess()); // Commit the thread reservation. event_reservation.Commit(); // Ensure that we clean up the event (and its only references are handle table) on function end. SCOPE_EXIT({ - event->GetWritableEvent().Close(); event->GetReadableEvent().Close(); + event->Close(); }); // Register the event. KEvent::Register(kernel, event); - // Add the writable event to the handle table. - R_TRY(handle_table.Add(out_write, std::addressof(event->GetWritableEvent()))); + // Add the event to the handle table. + R_TRY(handle_table.Add(out_write, event)); - // Add the writable event to the handle table. + // Ensure that we maintaing a clean handle state on exit. auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); // Add the readable event to the handle table. diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp index c85b2e43a1..713689d8f2 100644 --- a/src/core/hle/service/acc/async_context.cpp +++ b/src/core/hle/service/acc/async_context.cpp @@ -64,7 +64,7 @@ void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) { void IAsyncContext::MarkComplete() { is_complete.store(true); - completion_event->GetWritableEvent().Signal(); + completion_event->Signal(); } } // namespace Service::Account diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 6fb7e198ea..e55233054f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -316,7 +316,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv accumulated_suspended_tick_changed_event = service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent"); - accumulated_suspended_tick_changed_event->GetWritableEvent().Signal(); + accumulated_suspended_tick_changed_event->Signal(); } ISelfController::~ISelfController() { @@ -378,7 +378,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) { void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); - launchable_event->GetWritableEvent().Signal(); + launchable_event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -618,18 +618,18 @@ Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() { void AppletMessageQueue::PushMessage(AppletMessage msg) { messages.push(msg); - on_new_message->GetWritableEvent().Signal(); + on_new_message->Signal(); } AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { if (messages.empty()) { - on_new_message->GetWritableEvent().Clear(); + on_new_message->Clear(); return AppletMessage::None; } auto msg = messages.front(); messages.pop(); if (messages.empty()) { - on_new_message->GetWritableEvent().Clear(); + on_new_message->Clear(); } return msg; } @@ -653,7 +653,7 @@ void AppletMessageQueue::FocusStateChanged() { void AppletMessageQueue::OperationModeChanged() { PushMessage(AppletMessage::OperationModeChanged); PushMessage(AppletMessage::PerformanceModeChanged); - on_operation_mode_changed->GetWritableEvent().Signal(); + on_operation_mode_changed->Signal(); } ICommonStateGetter::ICommonStateGetter(Core::System& system_, diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index b5b8e4cad7..7062df21c6 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -65,7 +65,7 @@ std::shared_ptr AppletDataBroker::PopNormalDataToGame() { auto out = std::move(out_channel.front()); out_channel.pop_front(); - pop_out_data_event->GetWritableEvent().Clear(); + pop_out_data_event->Clear(); return out; } @@ -84,7 +84,7 @@ std::shared_ptr AppletDataBroker::PopInteractiveDataToGame() { auto out = std::move(out_interactive_channel.front()); out_interactive_channel.pop_front(); - pop_interactive_out_data_event->GetWritableEvent().Clear(); + pop_interactive_out_data_event->Clear(); return out; } @@ -103,7 +103,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr&& storag void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr&& storage) { out_channel.emplace_back(std::move(storage)); - pop_out_data_event->GetWritableEvent().Signal(); + pop_out_data_event->Signal(); } void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr&& storage) { @@ -112,11 +112,11 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr&& s void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr&& storage) { out_interactive_channel.emplace_back(std::move(storage)); - pop_interactive_out_data_event->GetWritableEvent().Signal(); + pop_interactive_out_data_event->Signal(); } void AppletDataBroker::SignalStateChanged() { - state_changed_event->GetWritableEvent().Signal(); + state_changed_event->Signal(); switch (applet_mode) { case LibraryAppletMode::AllForeground: diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6fb07c37dc..60c30cd5bc 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -239,7 +239,7 @@ public: }; RegisterHandlers(functions); - event->GetWritableEvent().Signal(); + event->Signal(); } ~IAudioDevice() override { @@ -325,7 +325,7 @@ private: void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "(STUBBED) called"); - event->GetWritableEvent().Signal(); + event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index cd0b405ffa..847f769874 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -82,7 +82,7 @@ void ProgressServiceBackend::FinishDownload(Result result) { } void ProgressServiceBackend::SignalUpdate() { - update_event->GetWritableEvent().Signal(); + update_event->Signal(); } Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index f8972ec7a5..98e4f2af7f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -16,7 +16,6 @@ #include "core/hid/hid_core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/errors.h" #include "core/hle/service/kernel_helpers.h" @@ -167,7 +166,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { const auto& battery_level = controller.device->GetBattery(); auto* shared_memory = controller.shared_memory; if (controller_type == Core::HID::NpadStyleIndex::None) { - controller.styleset_changed_event->GetWritableEvent().Signal(); + controller.styleset_changed_event->Signal(); return; } @@ -1033,7 +1032,7 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { const auto& controller = GetControllerFromNpadIdType(npad_id); - controller.styleset_changed_event->GetWritableEvent().Signal(); + controller.styleset_changed_event->Signal(); } void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp index 575d4e626b..4564ea1e24 100644 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -73,7 +73,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, operation.operation = PalmaOperationType::PlayActivity; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -93,7 +93,7 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { operation.operation = PalmaOperationType::ReadStep; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -122,7 +122,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle operation.operation = PalmaOperationType::ReadUniqueCode; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -133,7 +133,7 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& operation.operation = PalmaOperationType::SetUniqueCodeInvalid; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -147,7 +147,7 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -159,7 +159,7 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle operation.operation = PalmaOperationType::WriteWaveEntry; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -172,7 +172,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; operation.result = PalmaResultSuccess; operation.data[0] = {}; - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } @@ -185,7 +185,7 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( operation.result = PalmaResultSuccess; operation.data = {}; operation.data[0] = static_cast(database_id_version); - operation_complete_event->GetWritableEvent().Signal(); + operation_complete_event->Signal(); return ResultSuccess; } diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp index ad223d6496..57f1a2a26b 100644 --- a/src/core/hle/service/hid/hidbus/ringcon.cpp +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp @@ -131,12 +131,12 @@ bool RingController::SetCommand(const std::vector& data) { case RingConCommands::ReadRepCount: case RingConCommands::ReadTotalPushCount: ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return true; case RingConCommands::ResetRepCount: ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); total_rep_count = 0; - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return true; case RingConCommands::SaveCalData: { ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); @@ -144,14 +144,14 @@ bool RingController::SetCommand(const std::vector& data) { SaveCalData save_info{}; std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); user_calibration = save_info.calibration; - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return true; } default: LOG_ERROR(Service_HID, "Command not implemented {}", command); command = RingConCommands::Error; // Signal a reply to avoid softlocking the game - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return false; } } diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 3e317367b0..af133af932 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -9,7 +9,6 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" namespace Service::KernelHelpers { @@ -46,7 +45,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } // Initialize the event. - event->Initialize(std::move(name), process); + event->Initialize(process); // Commit the thread reservation. event_reservation.Commit(); @@ -59,7 +58,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { void ServiceContext::CloseEvent(Kernel::KEvent* event) { event->GetReadableEvent().Close(); - event->GetWritableEvent().Close(); + event->Close(); } } // namespace Service::KernelHelpers diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index ea3e7e55af..6df5631369 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -165,7 +165,7 @@ public: } void OnEventFired() { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } void GetState(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index ec895ac014..76f8a267a1 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -58,7 +58,7 @@ NfpDevice::~NfpDevice() { void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { if (type == Core::HID::ControllerTriggerType::Connected || type == Core::HID::ControllerTriggerType::Disconnected) { - availability_change_event->GetWritableEvent().Signal(); + availability_change_event->Signal(); return; } @@ -100,7 +100,7 @@ bool NfpDevice::LoadAmiibo(std::span data) { device_state = DeviceState::TagFound; deactivate_event->GetReadableEvent().Clear(); - activate_event->GetWritableEvent().Signal(); + activate_event->Signal(); return true; } @@ -115,7 +115,7 @@ void NfpDevice::CloseAmiibo() { encrypted_tag_data = {}; tag_data = {}; activate_event->GetReadableEvent().Clear(); - deactivate_event->GetWritableEvent().Signal(); + deactivate_event->Signal(); } Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index b2bb7426d8..5a8a91e0bd 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -328,7 +328,7 @@ private: void StartTask(Kernel::HLERequestContext& ctx) { // No need to connect to the internet, just finish the task straight away. LOG_DEBUG(Service_NIM, "called"); - finished_event->GetWritableEvent().Signal(); + finished_event->Signal(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -350,7 +350,7 @@ private: void Cancel(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIM, "called"); - finished_event->GetWritableEvent().Clear(); + finished_event->Clear(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 5bee4a3d3f..eee11fab88 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -12,7 +12,6 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" @@ -206,7 +205,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector auto& event_ = events[slot]; if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == EventState::Waiting) { - event_.kevent->GetWritableEvent().Signal(); + event_.kevent->Signal(); } event_.status.store(EventState::Signalled, std::memory_order_release); }); @@ -306,7 +305,7 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v } event.fails++; event.status.store(EventState::Cancelled, std::memory_order_release); - event.kevent->GetWritableEvent().Clear(); + event.kevent->Clear(); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 5e7b7468f4..9d99243959 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index cd58a4f35c..5ac06ee30b 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -7,10 +7,6 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" -namespace Kernel { -class KWritableEvent; -} - namespace Service::Nvidia { class NVDRV final : public ServiceFramework { diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index d4ab23a108..77ddbb6efb 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -11,7 +11,6 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvdrv/core/nvmap.h" @@ -110,7 +109,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { core->override_max_buffer_count = buffer_count; core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); listener = core->consumer_listener; } @@ -623,7 +622,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { slots[slot].fence = fence; core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); } Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { @@ -753,7 +752,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { core->connected_producer_listener = nullptr; core->connected_api = NativeWindowApi::NoConnectedApi; core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); listener = core->consumer_listener; } else { LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", @@ -802,7 +801,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, } core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); return Status::NoError; } diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 0ba03a568e..7526bf8ece 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -24,7 +24,6 @@ namespace Kernel { class KernelCore; class KEvent; class KReadableEvent; -class KWritableEvent; } // namespace Kernel namespace Service::KernelHelpers { diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index b62615de27..99509bc5bf 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -25,7 +25,6 @@ struct EventType; namespace Kernel { class KReadableEvent; -class KWritableEvent; } // namespace Kernel namespace Service::Nvidia { diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index 2c31e9485f..1ac97fe310 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -37,19 +37,19 @@ public: void SignalChargerTypeChanged() { if (should_signal && should_signal_charger_type) { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } } void SignalPowerSupplyChanged() { if (should_signal && should_signal_power_supply) { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } } void SignalBatteryVoltageStateChanged() { if (should_signal && should_signal_battery_voltage) { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } } diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp index a649bed3a5..cafc04ee72 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/service/time/errors.h" #include "core/hle/service/time/system_clock_context_update_callback.h" @@ -20,13 +20,13 @@ bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& valu } void SystemClockContextUpdateCallback::RegisterOperationEvent( - std::shared_ptr&& writable_event) { - operation_event_list.emplace_back(std::move(writable_event)); + std::shared_ptr&& event) { + operation_event_list.emplace_back(std::move(event)); } void SystemClockContextUpdateCallback::BroadcastOperationEvent() { - for (const auto& writable_event : operation_event_list) { - writable_event->Signal(); + for (const auto& event : operation_event_list) { + event->Signal(); } } diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h index 9c6caf1964..bf657acd9e 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ b/src/core/hle/service/time/system_clock_context_update_callback.h @@ -9,7 +9,7 @@ #include "core/hle/service/time/clock_types.h" namespace Kernel { -class KWritableEvent; +class KEvent; } namespace Service::Time::Clock { @@ -24,7 +24,7 @@ public: bool NeedUpdate(const SystemClockContext& value) const; - void RegisterOperationEvent(std::shared_ptr&& writable_event); + void RegisterOperationEvent(std::shared_ptr&& event); void BroadcastOperationEvent(); @@ -37,7 +37,7 @@ protected: private: bool has_context{}; - std::vector> operation_event_list; + std::vector> operation_event_list; }; } // namespace Service::Time::Clock diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 288aafaaf6..8ef74f1f09 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -10,7 +10,6 @@ #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvflinger/buffer_item_consumer.h" @@ -74,7 +73,7 @@ Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { } void Display::SignalVSyncEvent() { - vsync_event->GetWritableEvent().Signal(); + vsync_event->Signal(); } void Display::CreateLayer(u64 layer_id, u32 binder_id, From c0fb5e876d83abae86d64a19d4acfb9867ed7dc1 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 13 Oct 2022 11:01:28 -0400 Subject: [PATCH 128/245] result: enforce reference check specialization --- src/core/hle/result.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/hle/result.h b/src/core/hle/result.h index e20e0bfeed..d67e68bae3 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -402,9 +402,8 @@ constexpr bool EvaluateResultFailure(const Result& r) { } template -constexpr void UpdateCurrentResultReference(T result_reference, Result result) { - ASSERT(false); -} +constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete; +// Intentionally not defined template <> constexpr void UpdateCurrentResultReference(Result& result_reference, Result result) { @@ -412,7 +411,7 @@ constexpr void UpdateCurrentResultReference(Result& result_reference, R } template <> -constexpr void UpdateCurrentResultReference(Result result_reference, Result result) {} +constexpr void UpdateCurrentResultReference(Result result_reference, Result result) {} } // namespace ResultImpl #define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \ From e2164f34178c0cb8d08d07567bd85c896b5c196b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 13 Oct 2022 12:24:04 -0400 Subject: [PATCH 129/245] settings: Update aspect_ratio range Since 16:10 was added, the maximum value is now 4. --- src/common/settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/settings.h b/src/common/settings.h index d2452c93bb..0eb98939c1 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -431,7 +431,7 @@ struct Values { FullscreenMode::Exclusive, #endif FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; - SwitchableSetting aspect_ratio{0, 0, 3, "aspect_ratio"}; + SwitchableSetting aspect_ratio{0, 0, 4, "aspect_ratio"}; SwitchableSetting max_anisotropy{0, 0, 5, "max_anisotropy"}; SwitchableSetting use_speed_limit{true, "use_speed_limit"}; SwitchableSetting speed_limit{100, 0, 9999, "speed_limit"}; From d3114c620d169b05ee16a72826cfc55e9c10a56a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:52:56 -0400 Subject: [PATCH 130/245] renderer_(opengl/vulkan): Fix tessellation clockwise parameter This should be assigned CW only on Triangles_CW rather than not Triangles_CCW, making CCW the default winding order rather than CW. --- src/video_core/engines/maxwell_3d.h | 4 ++-- src/video_core/renderer_opengl/gl_shader_cache.cpp | 4 ++-- src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 12dbd9cc40..75e3b868dc 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -390,7 +390,7 @@ public: FractionalEven = 2, }; - enum class OutputPrimitves : u32 { + enum class OutputPrimitives : u32 { Points = 0, Lines = 1, Triangles_CW = 2, @@ -401,7 +401,7 @@ public: union { BitField<0, 2, DomainType> domain_type; BitField<4, 2, Spacing> spacing; - BitField<8, 2, OutputPrimitves> output_primitives; + BitField<8, 2, OutputPrimitives> output_primitives; }; } params; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6bdb0b6452..609f0a7725 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -317,8 +317,8 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); graphics_key.tessellation_clockwise.Assign( - regs.tessellation.params.output_primitives.Value() != - Maxwell::Tessellation::OutputPrimitves::Triangles_CCW); + regs.tessellation.params.output_primitives.Value() == + Maxwell::Tessellation::OutputPrimitives::Triangles_CW); graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); if (graphics_key.xfb_enabled) { SetXfbState(graphics_key.xfb_state, regs); diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index eab1b8f936..f85ed8e5b9 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -73,8 +73,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, patch_control_points_minus_one.Assign(regs.patch_vertices - 1); tessellation_primitive.Assign(static_cast(regs.tessellation.params.domain_type.Value())); tessellation_spacing.Assign(static_cast(regs.tessellation.params.spacing.Value())); - tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() != - Maxwell::Tessellation::OutputPrimitves::Triangles_CCW); + tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == + Maxwell::Tessellation::OutputPrimitives::Triangles_CW); logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); logic_op.Assign(PackLogicOp(regs.logic_op.op)); topology.Assign(regs.draw.topology); From 40af1111c27968b5298a6ecd07b319422fed665d Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Tue, 11 Oct 2022 13:01:29 -0700 Subject: [PATCH 131/245] CMake: Try add library "LZ4::lz4_shared" if "lz4::lz4" is unavailable Right now this looks like a distro specific problem, but we'll have to see. Over on Gentoo: with lz4 1.9.3 there is a lz4::lz4 library target, with 1.9.4 it's no longer mentioned in the cmake files provided by the package. (/usr/lib64/cmake/lz4) arch and openSUSE have lz4 1.9.4 available so I checked there, they only have .pc files for pkg-config, so asking for "lz4::lz4" works as usual MSVC does require "lz4::lz4" to be asked for --- src/common/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a026968734..46cf75fdeb 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -169,7 +169,11 @@ endif() create_target_directory_groups(common) target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) -target_link_libraries(common PRIVATE lz4::lz4) +if (TARGET lz4::lz4) + target_link_libraries(common PRIVATE lz4::lz4) +else() + target_link_libraries(common PRIVATE LZ4::lz4_shared) +endif() if (TARGET zstd::zstd) target_link_libraries(common PRIVATE zstd::zstd) else() From d2170075e658a0ccc6d8bfa62f52e307a307572f Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 14 Oct 2022 10:59:33 -0500 Subject: [PATCH 132/245] audio_core: Revert sink name to sdl2 --- src/audio_core/sink/sink_details.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index b878bf23f8..39ea6d91b8 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp @@ -47,7 +47,7 @@ constexpr SinkDetails sink_details[] = { #endif #ifdef HAVE_SDL2 SinkDetails{ - "sdl", + "sdl2", [](std::string_view device_id) -> std::unique_ptr { return std::make_unique(device_id); }, @@ -76,7 +76,7 @@ const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { #if defined(HAVE_CUBEB) && defined(HAVE_SDL2) iter = find_backend("cubeb"); if (iter->latency() > TargetSampleCount * 3) { - iter = find_backend("sdl"); + iter = find_backend("sdl2"); } #else iter = std::begin(sink_details); From fcebd36cde76a94f86e9a78efcd9c9afb22f3e4f Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Tue, 11 Oct 2022 16:09:41 -0700 Subject: [PATCH 133/245] Translations: new transifex client Currently we're using the python client which uses an API that they state will sunset Nov 30, 2022. `tx push -s` actually appears to work properly, some of the other commands require tweaking, like instead of suggesting `tx pull -a` in dist/languages we need to suggest `tx pull -t -a` --- .ci/scripts/transifex/docker.sh | 8 ++------ dist/languages/.tx/config | 2 +- dist/languages/README.md | 4 +++- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.ci/scripts/transifex/docker.sh b/.ci/scripts/transifex/docker.sh index 6237b3f73d..af75299442 100755 --- a/.ci/scripts/transifex/docker.sh +++ b/.ci/scripts/transifex/docker.sh @@ -6,9 +6,8 @@ # Setup RC file for tx cat << EOF > ~/.transifexrc [https://www.transifex.com] -hostname = https://www.transifex.com -username = api -password = $TRANSIFEX_API_TOKEN +rest_hostname = https://rest.api.transifex.com +token = $TRANSIFEX_API_TOKEN EOF @@ -19,9 +18,6 @@ cmake --version gcc -v tx --version -# vcpkg needs these: curl zip unzip tar, have tar -apt-get install -y curl zip unzip - mkdir build && cd build cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DYUZU_TESTS=OFF -DYUZU_USE_BUNDLED_VCPKG=ON make translation diff --git a/dist/languages/.tx/config b/dist/languages/.tx/config index 0d9b512ea6..30e76b9259 100644 --- a/dist/languages/.tx/config +++ b/dist/languages/.tx/config @@ -1,7 +1,7 @@ [main] host = https://www.transifex.com -[yuzu.emulator] +[o:yuzu-emulator:p:yuzu:r:emulator] file_filter = .ts source_file = en.ts source_lang = en diff --git a/dist/languages/README.md b/dist/languages/README.md index 61981ab1d4..c5ea1ada0e 100644 --- a/dist/languages/README.md +++ b/dist/languages/README.md @@ -1 +1,3 @@ -This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. Do not directly open PRs on github to modify the translation. +This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -t -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. + +Do not directly open PRs on github to modify the translation. From 0ba03d1b3a36cd0b4398f57ee6c22ffb4679a91c Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Sat, 15 Oct 2022 14:04:42 -0700 Subject: [PATCH 134/245] fix a tiny spelling mistake Kreato pointed this out over on discord. --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f45a254100..a94624be63 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3280,7 +3280,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) { QMessageBox::warning(this, title, tr("The current game is not looking for amiibos")); break; case InputCommon::VirtualAmiibo::Info::Unknown: - QMessageBox::warning(this, title, tr("An unkown error occured")); + QMessageBox::warning(this, title, tr("An unknown error occurred")); break; default: break; From f706b3bd24261e808d6598db819ffc21c8c36e27 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 16 Oct 2022 00:46:22 -0400 Subject: [PATCH 135/245] general: Fix spelling of "unknown" --- src/core/hid/irs_types.h | 20 +++++++++---------- src/core/hle/service/hid/hid.cpp | 2 +- .../service/hid/irsensor/pointing_processor.h | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h index 88c5b016d4..0d1bfe53fc 100644 --- a/src/core/hid/irs_types.h +++ b/src/core/hid/irs_types.h @@ -14,7 +14,7 @@ enum class CameraAmbientNoiseLevel : u32 { Low, Medium, High, - Unkown3, // This level can't be reached + Unknown3, // This level can't be reached }; // This is nn::irsensor::CameraLightTarget @@ -75,9 +75,9 @@ enum class IrCameraStatus : u32 { enum class IrCameraInternalStatus : u32 { Stopped, FirmwareUpdateNeeded, - Unkown2, - Unkown3, - Unkown4, + Unknown2, + Unknown3, + Unknown4, FirmwareVersionRequested, FirmwareVersionIsInvalid, Ready, @@ -121,20 +121,20 @@ enum class IrSensorFunctionLevel : u8 { // This is nn::irsensor::MomentProcessorPreprocess enum class MomentProcessorPreprocess : u32 { - Unkown0, - Unkown1, + Unknown0, + Unknown1, }; // This is nn::irsensor::PackedMomentProcessorPreprocess enum class PackedMomentProcessorPreprocess : u8 { - Unkown0, - Unkown1, + Unknown0, + Unknown1, }; // This is nn::irsensor::PointingStatus enum class PointingStatus : u32 { - Unkown0, - Unkown1, + Unknown0, + Unknown1, }; struct IrsRect { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 46bad7871e..79375bd2f3 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2118,7 +2118,7 @@ void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); LOG_WARNING(Service_HID, - "(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, " + "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h index cf49307944..d63423aff2 100644 --- a/src/core/hle/service/hid/irsensor/pointing_processor.h +++ b/src/core/hle/service/hid/irsensor/pointing_processor.h @@ -37,10 +37,10 @@ private: u8 pointing_status; INSERT_PADDING_BYTES(3); u32 unknown; - float unkown_float1; + float unknown_float1; float position_x; float position_y; - float unkown_float2; + float unknown_float2; Core::IrSensor::IrsRect window_of_interest; }; static_assert(sizeof(PointingProcessorMarkerData) == 0x20, From ddf5577799f623c8a6e2061197b2464127eed8da Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 16 Oct 2022 00:50:53 -0400 Subject: [PATCH 136/245] video_core: Fix spelling of "synchronize" --- src/video_core/texture_cache/descriptor_table.h | 2 +- src/video_core/texture_cache/texture_cache.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h index b18e3838fd..ee4240288b 100644 --- a/src/video_core/texture_cache/descriptor_table.h +++ b/src/video_core/texture_cache/descriptor_table.h @@ -18,7 +18,7 @@ class DescriptorTable { public: explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} - [[nodiscard]] bool Synchornize(GPUVAddr gpu_addr, u32 limit) { + [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 413baf730d..0e0fd410f8 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -193,11 +193,11 @@ void TextureCache

::SynchronizeGraphicsDescriptors() { const bool linked_tsc = maxwell3d->regs.sampler_binding == SamplerBinding::ViaHeaderBinding; const u32 tic_limit = maxwell3d->regs.tex_header.limit; const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit; - if (channel_state->graphics_sampler_table.Synchornize(maxwell3d->regs.tex_sampler.Address(), + if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), tsc_limit)) { channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (channel_state->graphics_image_table.Synchornize(maxwell3d->regs.tex_header.Address(), + if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), tic_limit)) { channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } @@ -209,10 +209,10 @@ void TextureCache

::SynchronizeComputeDescriptors() { const u32 tic_limit = kepler_compute->regs.tic.limit; const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address(); - if (channel_state->compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { + if (channel_state->compute_sampler_table.Synchronize(tsc_gpu_addr, tsc_limit)) { channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (channel_state->compute_image_table.Synchornize(kepler_compute->regs.tic.Address(), + if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), tic_limit)) { channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } From 5c7eef3756ed2814fdc8e5e42a2ebed489c37405 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Sun, 16 Oct 2022 03:14:52 -0400 Subject: [PATCH 137/245] sdl2_sink: Check for null string when loading SDL audio devices Attempting to place a null string into a vector of strings causes an error that closes the application. Don't. --- src/audio_core/sink/sdl2_sink.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index f12ebf7fe1..dee47cf0ec 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -230,7 +230,10 @@ std::vector ListSDLSinkDevices(bool capture) { const int device_count = SDL_GetNumAudioDevices(capture); for (int i = 0; i < device_count; ++i) { - device_list.emplace_back(SDL_GetAudioDeviceName(i, 0)); + const char* name = SDL_GetAudioDeviceName(i, 0); + if (name != nullptr) { + device_list.emplace_back(name); + } } return device_list; From 9fe077635e00aba362034a694c5d1a701abc288a Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Sun, 16 Oct 2022 03:15:54 -0400 Subject: [PATCH 138/245] sdl2_sink: Distinguish between capture and non-capture device names The function prototype appears to care whether we are loading capture devices or not, and SDL_GetAudioDeviceName has a parameter to use it, but for some reason it isn't. This puts `capture` where it goes. --- src/audio_core/sink/sdl2_sink.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index dee47cf0ec..cc13c62546 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -230,7 +230,7 @@ std::vector ListSDLSinkDevices(bool capture) { const int device_count = SDL_GetNumAudioDevices(capture); for (int i = 0; i < device_count; ++i) { - const char* name = SDL_GetAudioDeviceName(i, 0); + const char* name = SDL_GetAudioDeviceName(i, capture); if (name != nullptr) { device_list.emplace_back(name); } From 4b773b15a6cb7d78d7a9d71cf0f73fb5af591cba Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Sun, 16 Oct 2022 20:37:11 -0400 Subject: [PATCH 139/245] sdl2_sink: Inline variable init into if condition Co-authored-by: Mai --- src/audio_core/sink/sdl2_sink.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index cc13c62546..c138dc628e 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -230,8 +230,7 @@ std::vector ListSDLSinkDevices(bool capture) { const int device_count = SDL_GetNumAudioDevices(capture); for (int i = 0; i < device_count; ++i) { - const char* name = SDL_GetAudioDeviceName(i, capture); - if (name != nullptr) { + if (const char* name = SDL_GetAudioDeviceName(i, capture)) { device_list.emplace_back(name); } } From 20139f8a551098633e4ea3eab079b590c47ee0f5 Mon Sep 17 00:00:00 2001 From: FengChen Date: Mon, 17 Oct 2022 09:40:44 +0800 Subject: [PATCH 140/245] Address feedback --- .../frontend/maxwell/translate_program.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 3aee33a961..b58741d4d0 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -272,14 +272,14 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { auto& stores = program.info.stores; if (stores.Legacy()) { - std::queue ununsed_output_generics{}; + std::queue unused_output_generics{}; for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { if (!stores.Generic(index)) { - ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); + unused_output_generics.push(IR::Attribute::Generic0X + index * 4); } } program.info.legacy_stores_mapping = - GenerateLegacyToGenericMappings(stores, ununsed_output_generics, {}); + GenerateLegacyToGenericMappings(stores, unused_output_generics, {}); for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { switch (inst.GetOpcode()) { @@ -300,16 +300,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run auto& loads = program.info.loads; if (loads.Legacy()) { - std::queue ununsed_input_generics{}; + std::queue unused_input_generics{}; for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { const AttributeType input_type{runtime_info.generic_input_types[index]}; if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || input_type == AttributeType::Disabled) { - ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); + unused_input_generics.push(IR::Attribute::Generic0X + index * 4); } } auto mappings = GenerateLegacyToGenericMappings( - loads, ununsed_input_generics, runtime_info.previous_stage_legacy_stores_mapping); + loads, unused_input_generics, runtime_info.previous_stage_legacy_stores_mapping); for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { switch (inst.GetOpcode()) { From ae453ab6a829942a0db25b59e2cf97b5f7f1ecf9 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 16 Oct 2022 22:58:44 -0400 Subject: [PATCH 141/245] savedata_factory: Detect future save data paths Enable compatibility for new account/device save paths planned on a future implementation. --- src/core/file_sys/savedata_factory.cpp | 58 ++++++++++++++++++++++---- src/core/file_sys/savedata_factory.h | 4 +- src/yuzu/main.cpp | 10 +++-- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 8c1b2523cb..1567da2310 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/uuid.h" #include "core/core.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs.h" @@ -59,6 +60,36 @@ bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataA attr.title_id == 0 && attr.save_id == 0); } +std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id, + u128 user_id) { + // Only detect nand user saves. + const auto space_id_path = [space_id]() -> std::string_view { + switch (space_id) { + case SaveDataSpaceId::NandUser: + return "/user/save"; + default: + return ""; + } + }(); + + if (space_id_path.empty()) { + return ""; + } + + Common::UUID uuid; + std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); + + // Only detect account/device saves from the future location. + switch (type) { + case SaveDataType::SaveData: + return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id); + case SaveDataType::DeviceSaveData: + return fmt::format("{}/device/{:016X}/1", space_id_path, title_id); + default: + return ""; + } +} + } // Anonymous namespace std::string SaveDataAttribute::DebugInfo() const { @@ -82,7 +113,7 @@ ResultVal SaveDataFactory::Create(SaveDataSpaceId space, PrintSaveDataAttributeWarnings(meta); const auto save_directory = - GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->CreateDirectoryRelative(save_directory); @@ -99,7 +130,7 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { const auto save_directory = - GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->GetDirectoryRelative(save_directory); @@ -134,9 +165,9 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { } } -std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId space, - SaveDataType type, u64 title_id, u128 user_id, - u64 save_id) { +std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, + SaveDataSpaceId space, SaveDataType type, u64 title_id, + u128 user_id, u64 save_id) { // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { @@ -145,6 +176,17 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s } } + // For compat with a future impl. + if (std::string future_path = + GetFutureSaveDataPath(space, type, title_id & ~(0xFFULL), user_id); + !future_path.empty()) { + // Check if this location exists, and prefer it over the old. + if (const auto future_dir = dir->GetDirectoryRelative(future_path); future_dir != nullptr) { + LOG_INFO(Service_FS, "Using save at new location: {}", future_path); + return future_path; + } + } + std::string out = GetSaveDataSpaceIdPath(space); switch (type) { @@ -167,7 +209,8 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const { - const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + const auto path = + GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME); @@ -185,7 +228,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value) const { - const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + const auto path = + GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index a763b94c88..d3633ef039 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -95,8 +95,8 @@ public: VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); - static std::string GetFullPath(Core::System& system, SaveDataSpaceId space, SaveDataType type, - u64 title_id, u128 user_id, u64 save_id); + static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, + SaveDataType type, u64 title_id, u128 user_id, u64 save_id); SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a94624be63..d05c7ece80 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1895,6 +1895,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target case GameListOpenTarget::SaveData: { open_target = tr("Save Data"); const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); + auto vfs_nand_dir = + vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); if (has_user_save) { // User save data @@ -1921,15 +1923,15 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, - program_id, user_id->AsU128(), 0); + *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); } else { // Device save data const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, - program_id, {}, 0); + *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + FileSys::SaveDataType::SaveData, program_id, {}, 0); path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); } From c70e1d0247508d933fb0a66ab44711af96da766b Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Sun, 16 Oct 2022 22:13:19 -0700 Subject: [PATCH 142/245] Set TX_TOKEN for transifex client I did some tests on my own fork, and we're writing to ~/.transifexrc but the client can't seem to read that file. maybe issue with $HOME or something. Workaround is to set TX_TOKEN environment variable and now the pesky ~/.transifexrc file is not needed. --- .ci/scripts/transifex/docker.sh | 8 -------- .github/workflows/ci.yml | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.ci/scripts/transifex/docker.sh b/.ci/scripts/transifex/docker.sh index af75299442..6ddbfd0dda 100755 --- a/.ci/scripts/transifex/docker.sh +++ b/.ci/scripts/transifex/docker.sh @@ -3,14 +3,6 @@ # SPDX-FileCopyrightText: 2021 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -# Setup RC file for tx -cat << EOF > ~/.transifexrc -[https://www.transifex.com] -rest_hostname = https://rest.api.transifex.com -token = $TRANSIFEX_API_TOKEN -EOF - - set -x echo -e "\e[1m\e[33mBuild tools information:\e[0m" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa58248249..25ef1f0789 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,11 +19,11 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - fetch-depth: 0 + fetch-depth: 0 - name: Update Translation run: ./.ci/scripts/transifex/docker.sh env: - TRANSIFEX_API_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }} + TX_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }} reuse: runs-on: ubuntu-latest From bffbaddb797e0229e6d9e30fe0f75d56b4530903 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 17 Oct 2022 02:52:41 -0400 Subject: [PATCH 143/245] general: Add missing pragma once --- src/common/fixed_point.h | 5 +---- src/core/hle/service/vi/vi_results.h | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 4a0f72cc9b..f9adfccb00 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -4,8 +4,7 @@ // From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h // See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math -#ifndef FIXED_H_ -#define FIXED_H_ +#pragma once #if __cplusplus >= 201402L #define CONSTEXPR14 constexpr @@ -702,5 +701,3 @@ constexpr bool operator!=(Number lhs, FixedPoint rhs) { } // namespace Common #undef CONSTEXPR14 - -#endif diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h index a46c247d27..22bac799f0 100644 --- a/src/core/hle/service/vi/vi_results.h +++ b/src/core/hle/service/vi/vi_results.h @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/result.h" namespace Service::VI { From 88ccdaf10af1a055854c7b094882a3ed7af37e4b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 17 Oct 2022 03:16:54 -0400 Subject: [PATCH 144/245] fixed_point: Replace CONSTEXPR14 with constexpr As we require the latest C++ standards to compile yuzu, checking for C++14 constexpr is not needed. --- src/common/fixed_point.h | 92 ++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index f9adfccb00..6eb6afe2f5 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -6,12 +6,6 @@ #pragma once -#if __cplusplus >= 201402L -#define CONSTEXPR14 constexpr -#else -#define CONSTEXPR14 -#endif - #include // for size_t #include #include @@ -105,7 +99,7 @@ constexpr B next_to_base(N rhs) { struct divide_by_zero : std::exception {}; template -CONSTEXPR14 FixedPoint divide( +constexpr FixedPoint divide( FixedPoint numerator, FixedPoint denominator, FixedPoint& remainder, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { @@ -125,7 +119,7 @@ CONSTEXPR14 FixedPoint divide( } template -CONSTEXPR14 FixedPoint divide( +constexpr FixedPoint divide( FixedPoint numerator, FixedPoint denominator, FixedPoint& remainder, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { @@ -195,7 +189,7 @@ CONSTEXPR14 FixedPoint divide( // this is the usual implementation of multiplication template -CONSTEXPR14 FixedPoint multiply( +constexpr FixedPoint multiply( FixedPoint lhs, FixedPoint rhs, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { @@ -214,7 +208,7 @@ CONSTEXPR14 FixedPoint multiply( // it is slightly slower, but is more robust since it doesn't // require and upgraded type template -CONSTEXPR14 FixedPoint multiply( +constexpr FixedPoint multiply( FixedPoint lhs, FixedPoint rhs, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { @@ -283,7 +277,7 @@ public: // constructors public: // conversion template - CONSTEXPR14 explicit FixedPoint(FixedPoint other) { + constexpr explicit FixedPoint(FixedPoint other) { static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); using T = FixedPoint; @@ -352,81 +346,81 @@ public: // unary operators return FixedPoint::from_base(+data_); } - CONSTEXPR14 FixedPoint& operator++() { + constexpr FixedPoint& operator++() { data_ += one; return *this; } - CONSTEXPR14 FixedPoint& operator--() { + constexpr FixedPoint& operator--() { data_ -= one; return *this; } - CONSTEXPR14 FixedPoint operator++(int) { + constexpr FixedPoint operator++(int) { FixedPoint tmp(*this); data_ += one; return tmp; } - CONSTEXPR14 FixedPoint operator--(int) { + constexpr FixedPoint operator--(int) { FixedPoint tmp(*this); data_ -= one; return tmp; } public: // basic math operators - CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) { + constexpr FixedPoint& operator+=(FixedPoint n) { data_ += n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) { + constexpr FixedPoint& operator-=(FixedPoint n) { data_ -= n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) { + constexpr FixedPoint& operator*=(FixedPoint n) { return assign(detail::multiply(*this, n)); } - CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) { + constexpr FixedPoint& operator/=(FixedPoint n) { FixedPoint temp; return assign(detail::divide(*this, n, temp)); } private: - CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) { + constexpr FixedPoint& assign(FixedPoint rhs) { data_ = rhs.data_; return *this; } public: // binary math operators, effects underlying bit pattern since these // don't really typically make sense for non-integer values - CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) { + constexpr FixedPoint& operator&=(FixedPoint n) { data_ &= n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) { + constexpr FixedPoint& operator|=(FixedPoint n) { data_ |= n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) { + constexpr FixedPoint& operator^=(FixedPoint n) { data_ ^= n.data_; return *this; } template ::value>::type> - CONSTEXPR14 FixedPoint& operator>>=(Integer n) { + constexpr FixedPoint& operator>>=(Integer n) { data_ >>= n; return *this; } template ::value>::type> - CONSTEXPR14 FixedPoint& operator<<=(Integer n) { + constexpr FixedPoint& operator<<=(Integer n) { data_ <<= n; return *this; } @@ -484,7 +478,7 @@ public: // conversion to basic types } public: - CONSTEXPR14 void swap(FixedPoint& rhs) { + constexpr void swap(FixedPoint& rhs) { using std::swap; swap(data_, rhs.data_); } @@ -496,8 +490,8 @@ public: // if we have the same fractional portion, but differing integer portions, we trivially upgrade the // smaller type template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator+(FixedPoint lhs, FixedPoint rhs) { +constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator+( + FixedPoint lhs, FixedPoint rhs) { using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; @@ -507,8 +501,8 @@ operator+(FixedPoint lhs, FixedPoint rhs) { } template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator-(FixedPoint lhs, FixedPoint rhs) { +constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator-( + FixedPoint lhs, FixedPoint rhs) { using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; @@ -518,8 +512,8 @@ operator-(FixedPoint lhs, FixedPoint rhs) { } template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator*(FixedPoint lhs, FixedPoint rhs) { +constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator*( + FixedPoint lhs, FixedPoint rhs) { using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; @@ -529,8 +523,8 @@ operator*(FixedPoint lhs, FixedPoint rhs) { } template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator/(FixedPoint lhs, FixedPoint rhs) { +constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator/( + FixedPoint lhs, FixedPoint rhs) { using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; @@ -547,75 +541,75 @@ std::ostream& operator<<(std::ostream& os, FixedPoint f) { // basic math operators template -CONSTEXPR14 FixedPoint operator+(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator+(FixedPoint lhs, FixedPoint rhs) { lhs += rhs; return lhs; } template -CONSTEXPR14 FixedPoint operator-(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator-(FixedPoint lhs, FixedPoint rhs) { lhs -= rhs; return lhs; } template -CONSTEXPR14 FixedPoint operator*(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator*(FixedPoint lhs, FixedPoint rhs) { lhs *= rhs; return lhs; } template -CONSTEXPR14 FixedPoint operator/(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator/(FixedPoint lhs, FixedPoint rhs) { lhs /= rhs; return lhs; } template ::value>::type> -CONSTEXPR14 FixedPoint operator+(FixedPoint lhs, Number rhs) { +constexpr FixedPoint operator+(FixedPoint lhs, Number rhs) { lhs += FixedPoint(rhs); return lhs; } template ::value>::type> -CONSTEXPR14 FixedPoint operator-(FixedPoint lhs, Number rhs) { +constexpr FixedPoint operator-(FixedPoint lhs, Number rhs) { lhs -= FixedPoint(rhs); return lhs; } template ::value>::type> -CONSTEXPR14 FixedPoint operator*(FixedPoint lhs, Number rhs) { +constexpr FixedPoint operator*(FixedPoint lhs, Number rhs) { lhs *= FixedPoint(rhs); return lhs; } template ::value>::type> -CONSTEXPR14 FixedPoint operator/(FixedPoint lhs, Number rhs) { +constexpr FixedPoint operator/(FixedPoint lhs, Number rhs) { lhs /= FixedPoint(rhs); return lhs; } template ::value>::type> -CONSTEXPR14 FixedPoint operator+(Number lhs, FixedPoint rhs) { +constexpr FixedPoint operator+(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp += rhs; return tmp; } template ::value>::type> -CONSTEXPR14 FixedPoint operator-(Number lhs, FixedPoint rhs) { +constexpr FixedPoint operator-(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp -= rhs; return tmp; } template ::value>::type> -CONSTEXPR14 FixedPoint operator*(Number lhs, FixedPoint rhs) { +constexpr FixedPoint operator*(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp *= rhs; return tmp; } template ::value>::type> -CONSTEXPR14 FixedPoint operator/(Number lhs, FixedPoint rhs) { +constexpr FixedPoint operator/(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp /= rhs; return tmp; @@ -624,13 +618,13 @@ CONSTEXPR14 FixedPoint operator/(Number lhs, FixedPoint rhs) { // shift operators template ::value>::type> -CONSTEXPR14 FixedPoint operator<<(FixedPoint lhs, Integer rhs) { +constexpr FixedPoint operator<<(FixedPoint lhs, Integer rhs) { lhs <<= rhs; return lhs; } template ::value>::type> -CONSTEXPR14 FixedPoint operator>>(FixedPoint lhs, Integer rhs) { +constexpr FixedPoint operator>>(FixedPoint lhs, Integer rhs) { lhs >>= rhs; return lhs; } @@ -699,5 +693,3 @@ constexpr bool operator!=(Number lhs, FixedPoint rhs) { } } // namespace Common - -#undef CONSTEXPR14 From 99507d0188a1f7f7d8e11741f935918c7643ce76 Mon Sep 17 00:00:00 2001 From: FengChen Date: Sun, 16 Oct 2022 23:49:32 +0800 Subject: [PATCH 145/245] video_core: Implement memory manager page kind --- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 12 +- src/video_core/CMakeLists.txt | 1 + src/video_core/memory_manager.cpp | 61 +++- src/video_core/memory_manager.h | 21 +- src/video_core/pte_kind.h | 264 ++++++++++++++++++ 5 files changed, 342 insertions(+), 17 deletions(-) create mode 100644 src/video_core/pte_kind.h diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 6411dbf433..b635e6ed13 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -311,7 +311,8 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out handle->address + (static_cast(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; - gmmu->Map(virtual_address, cpu_address, size, use_big_pages); + gmmu->Map(virtual_address, cpu_address, size, static_cast(entry.kind), + use_big_pages); } } @@ -350,7 +351,8 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector(params.offset + params.buffer_offset)}; VAddr cpu_address{mapping->ptr + params.buffer_offset}; - gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page); + gmmu->Map(gpu_address, cpu_address, params.mapping_size, + static_cast(params.kind), mapping->big_page); return NvResult::Success; } catch (const std::out_of_range&) { @@ -389,7 +391,8 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorsecond.big_pages && big_page; - gmmu->Map(params.offset, cpu_address, size, use_big_pages); + gmmu->Map(params.offset, cpu_address, size, static_cast(params.kind), + use_big_pages); auto mapping{std::make_shared(cpu_address, params.offset, size, true, use_big_pages, alloc->second.sparse)}; @@ -409,7 +412,8 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorMap(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page); + gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), + static_cast(params.kind), big_page); auto mapping{ std::make_shared(cpu_address, params.offset, size, false, big_page, false)}; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 40e6d1ec48..cb8b46edf0 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -82,6 +82,7 @@ add_library(video_core STATIC gpu_thread.h memory_manager.cpp memory_manager.h + pte_kind.h query_cache.h rasterizer_accelerated.cpp rasterizer_accelerated.h diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index cca401c74b..d07b21bd67 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -41,7 +41,11 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_entries.resize(big_page_table_size / 32, 0); big_page_table_cpu.resize(big_page_table_size); big_page_continous.resize(big_page_table_size / continous_bits, 0); + std::array kind_valus; + kind_valus.fill(PTEKind::INVALID); + big_kinds.resize(big_page_table_size / 32, kind_valus); entries.resize(page_table_size / 32, 0); + kinds.resize(big_page_table_size / 32, kind_valus); } MemoryManager::~MemoryManager() = default; @@ -78,6 +82,41 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { } } +PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { + auto entry = GetEntry(gpu_addr); + if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] { + return GetKind(gpu_addr); + } else { + return GetKind(gpu_addr); + } +} + +template +PTEKind MemoryManager::GetKind(size_t position) const { + if constexpr (is_big_page) { + position = position >> big_page_bits; + const size_t sub_index = position % 32; + return big_kinds[position / 32][sub_index]; + } else { + position = position >> page_bits; + const size_t sub_index = position % 32; + return kinds[position / 32][sub_index]; + } +} + +template +void MemoryManager::SetKind(size_t position, PTEKind kind) { + if constexpr (is_big_page) { + position = position >> big_page_bits; + const size_t sub_index = position % 32; + big_kinds[position / 32][sub_index] = kind; + } else { + position = position >> page_bits; + const size_t sub_index = position % 32; + kinds[position / 32][sub_index] = kind; + } +} + inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { const u64 entry_mask = big_page_continous[big_page_index / continous_bits]; const size_t sub_index = big_page_index % continous_bits; @@ -92,8 +131,8 @@ inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value } template -GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, - size_t size) { +GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, + PTEKind kind) { u64 remaining_size{size}; if constexpr (entry_type == EntryType::Mapped) { page_table.ReserveRange(gpu_addr, size); @@ -102,6 +141,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp const GPUVAddr current_gpu_addr = gpu_addr + offset; [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); SetEntry(current_gpu_addr, entry_type); + SetKind(current_gpu_addr, kind); if (current_entry_type != entry_type) { rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); } @@ -118,12 +158,13 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp template GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, - size_t size) { + size_t size, PTEKind kind) { u64 remaining_size{size}; for (u64 offset{}; offset < size; offset += big_page_size) { const GPUVAddr current_gpu_addr = gpu_addr + offset; [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); SetEntry(current_gpu_addr, entry_type); + SetKind(current_gpu_addr, kind); if (current_entry_type != entry_type) { rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); } @@ -159,19 +200,19 @@ void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) rasterizer = rasterizer_; } -GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, +GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, PTEKind kind, bool is_big_pages) { if (is_big_pages) [[likely]] { - return BigPageTableOp(gpu_addr, cpu_addr, size); + return BigPageTableOp(gpu_addr, cpu_addr, size, kind); } - return PageTableOp(gpu_addr, cpu_addr, size); + return PageTableOp(gpu_addr, cpu_addr, size, kind); } GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) { if (is_big_pages) [[likely]] { - return BigPageTableOp(gpu_addr, 0, size); + return BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID); } - return PageTableOp(gpu_addr, 0, size); + return PageTableOp(gpu_addr, 0, size, PTEKind::INVALID); } void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { @@ -188,8 +229,8 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { rasterizer->UnmapMemory(*cpu_addr, map_size); } - BigPageTableOp(gpu_addr, 0, size); - PageTableOp(gpu_addr, 0, size); + BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID); + PageTableOp(gpu_addr, 0, size, PTEKind::INVALID); } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index f992e29f3d..ab4bc9ec63 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "common/multi_level_page_table.h" #include "common/virtual_buffer.h" +#include "video_core/pte_kind.h" namespace VideoCore { class RasterizerInterface; @@ -98,7 +99,8 @@ public: std::vector> GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const; - GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, bool is_big_pages = true); + GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, + PTEKind kind = PTEKind::INVALID, bool is_big_pages = true); GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); void Unmap(GPUVAddr gpu_addr, std::size_t size); @@ -114,6 +116,8 @@ public: return gpu_addr < address_space_size; } + PTEKind GetPageKind(GPUVAddr gpu_addr) const; + private: template inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, @@ -166,10 +170,12 @@ private: std::vector big_entries; template - GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size); + GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, + PTEKind kind); template - GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size); + GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, + PTEKind kind); template inline EntryType GetEntry(size_t position) const; @@ -177,6 +183,15 @@ private: template inline void SetEntry(size_t position, EntryType entry); + std::vector> kinds; + std::vector> big_kinds; + + template + inline PTEKind GetKind(size_t position) const; + + template + inline void SetKind(size_t position, PTEKind kind); + Common::MultiLevelPageTable page_table; Common::VirtualBuffer big_page_table_cpu; diff --git a/src/video_core/pte_kind.h b/src/video_core/pte_kind.h new file mode 100644 index 0000000000..591d7214b2 --- /dev/null +++ b/src/video_core/pte_kind.h @@ -0,0 +1,264 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Tegra { + +// https://github.com/NVIDIA/open-gpu-doc/blob/master/manuals/volta/gv100/dev_mmu.ref.txt +enum class PTEKind : u8 { + INVALID = 0xff, + PITCH = 0x00, + Z16 = 0x01, + Z16_2C = 0x02, + Z16_MS2_2C = 0x03, + Z16_MS4_2C = 0x04, + Z16_MS8_2C = 0x05, + Z16_MS16_2C = 0x06, + Z16_2Z = 0x07, + Z16_MS2_2Z = 0x08, + Z16_MS4_2Z = 0x09, + Z16_MS8_2Z = 0x0a, + Z16_MS16_2Z = 0x0b, + Z16_2CZ = 0x36, + Z16_MS2_2CZ = 0x37, + Z16_MS4_2CZ = 0x38, + Z16_MS8_2CZ = 0x39, + Z16_MS16_2CZ = 0x5f, + Z16_4CZ = 0x0c, + Z16_MS2_4CZ = 0x0d, + Z16_MS4_4CZ = 0x0e, + Z16_MS8_4CZ = 0x0f, + Z16_MS16_4CZ = 0x10, + S8Z24 = 0x11, + S8Z24_1Z = 0x12, + S8Z24_MS2_1Z = 0x13, + S8Z24_MS4_1Z = 0x14, + S8Z24_MS8_1Z = 0x15, + S8Z24_MS16_1Z = 0x16, + S8Z24_2CZ = 0x17, + S8Z24_MS2_2CZ = 0x18, + S8Z24_MS4_2CZ = 0x19, + S8Z24_MS8_2CZ = 0x1a, + S8Z24_MS16_2CZ = 0x1b, + S8Z24_2CS = 0x1c, + S8Z24_MS2_2CS = 0x1d, + S8Z24_MS4_2CS = 0x1e, + S8Z24_MS8_2CS = 0x1f, + S8Z24_MS16_2CS = 0x20, + S8Z24_4CSZV = 0x21, + S8Z24_MS2_4CSZV = 0x22, + S8Z24_MS4_4CSZV = 0x23, + S8Z24_MS8_4CSZV = 0x24, + S8Z24_MS16_4CSZV = 0x25, + V8Z24_MS4_VC12 = 0x26, + V8Z24_MS4_VC4 = 0x27, + V8Z24_MS8_VC8 = 0x28, + V8Z24_MS8_VC24 = 0x29, + V8Z24_MS4_VC12_1ZV = 0x2e, + V8Z24_MS4_VC4_1ZV = 0x2f, + V8Z24_MS8_VC8_1ZV = 0x30, + V8Z24_MS8_VC24_1ZV = 0x31, + V8Z24_MS4_VC12_2CS = 0x32, + V8Z24_MS4_VC4_2CS = 0x33, + V8Z24_MS8_VC8_2CS = 0x34, + V8Z24_MS8_VC24_2CS = 0x35, + V8Z24_MS4_VC12_2CZV = 0x3a, + V8Z24_MS4_VC4_2CZV = 0x3b, + V8Z24_MS8_VC8_2CZV = 0x3c, + V8Z24_MS8_VC24_2CZV = 0x3d, + V8Z24_MS4_VC12_2ZV = 0x3e, + V8Z24_MS4_VC4_2ZV = 0x3f, + V8Z24_MS8_VC8_2ZV = 0x40, + V8Z24_MS8_VC24_2ZV = 0x41, + V8Z24_MS4_VC12_4CSZV = 0x42, + V8Z24_MS4_VC4_4CSZV = 0x43, + V8Z24_MS8_VC8_4CSZV = 0x44, + V8Z24_MS8_VC24_4CSZV = 0x45, + Z24S8 = 0x46, + Z24S8_1Z = 0x47, + Z24S8_MS2_1Z = 0x48, + Z24S8_MS4_1Z = 0x49, + Z24S8_MS8_1Z = 0x4a, + Z24S8_MS16_1Z = 0x4b, + Z24S8_2CS = 0x4c, + Z24S8_MS2_2CS = 0x4d, + Z24S8_MS4_2CS = 0x4e, + Z24S8_MS8_2CS = 0x4f, + Z24S8_MS16_2CS = 0x50, + Z24S8_2CZ = 0x51, + Z24S8_MS2_2CZ = 0x52, + Z24S8_MS4_2CZ = 0x53, + Z24S8_MS8_2CZ = 0x54, + Z24S8_MS16_2CZ = 0x55, + Z24S8_4CSZV = 0x56, + Z24S8_MS2_4CSZV = 0x57, + Z24S8_MS4_4CSZV = 0x58, + Z24S8_MS8_4CSZV = 0x59, + Z24S8_MS16_4CSZV = 0x5a, + Z24V8_MS4_VC12 = 0x5b, + Z24V8_MS4_VC4 = 0x5c, + Z24V8_MS8_VC8 = 0x5d, + Z24V8_MS8_VC24 = 0x5e, + YUV_B8C1_2Y = 0x60, + YUV_B8C2_2Y = 0x61, + YUV_B10C1_2Y = 0x62, + YUV_B10C2_2Y = 0x6b, + YUV_B12C1_2Y = 0x6c, + YUV_B12C2_2Y = 0x6d, + Z24V8_MS4_VC12_1ZV = 0x63, + Z24V8_MS4_VC4_1ZV = 0x64, + Z24V8_MS8_VC8_1ZV = 0x65, + Z24V8_MS8_VC24_1ZV = 0x66, + Z24V8_MS4_VC12_2CS = 0x67, + Z24V8_MS4_VC4_2CS = 0x68, + Z24V8_MS8_VC8_2CS = 0x69, + Z24V8_MS8_VC24_2CS = 0x6a, + Z24V8_MS4_VC12_2CZV = 0x6f, + Z24V8_MS4_VC4_2CZV = 0x70, + Z24V8_MS8_VC8_2CZV = 0x71, + Z24V8_MS8_VC24_2CZV = 0x72, + Z24V8_MS4_VC12_2ZV = 0x73, + Z24V8_MS4_VC4_2ZV = 0x74, + Z24V8_MS8_VC8_2ZV = 0x75, + Z24V8_MS8_VC24_2ZV = 0x76, + Z24V8_MS4_VC12_4CSZV = 0x77, + Z24V8_MS4_VC4_4CSZV = 0x78, + Z24V8_MS8_VC8_4CSZV = 0x79, + Z24V8_MS8_VC24_4CSZV = 0x7a, + ZF32 = 0x7b, + ZF32_1Z = 0x7c, + ZF32_MS2_1Z = 0x7d, + ZF32_MS4_1Z = 0x7e, + ZF32_MS8_1Z = 0x7f, + ZF32_MS16_1Z = 0x80, + ZF32_2CS = 0x81, + ZF32_MS2_2CS = 0x82, + ZF32_MS4_2CS = 0x83, + ZF32_MS8_2CS = 0x84, + ZF32_MS16_2CS = 0x85, + ZF32_2CZ = 0x86, + ZF32_MS2_2CZ = 0x87, + ZF32_MS4_2CZ = 0x88, + ZF32_MS8_2CZ = 0x89, + ZF32_MS16_2CZ = 0x8a, + X8Z24_X16V8S8_MS4_VC12 = 0x8b, + X8Z24_X16V8S8_MS4_VC4 = 0x8c, + X8Z24_X16V8S8_MS8_VC8 = 0x8d, + X8Z24_X16V8S8_MS8_VC24 = 0x8e, + X8Z24_X16V8S8_MS4_VC12_1CS = 0x8f, + X8Z24_X16V8S8_MS4_VC4_1CS = 0x90, + X8Z24_X16V8S8_MS8_VC8_1CS = 0x91, + X8Z24_X16V8S8_MS8_VC24_1CS = 0x92, + X8Z24_X16V8S8_MS4_VC12_1ZV = 0x97, + X8Z24_X16V8S8_MS4_VC4_1ZV = 0x98, + X8Z24_X16V8S8_MS8_VC8_1ZV = 0x99, + X8Z24_X16V8S8_MS8_VC24_1ZV = 0x9a, + X8Z24_X16V8S8_MS4_VC12_1CZV = 0x9b, + X8Z24_X16V8S8_MS4_VC4_1CZV = 0x9c, + X8Z24_X16V8S8_MS8_VC8_1CZV = 0x9d, + X8Z24_X16V8S8_MS8_VC24_1CZV = 0x9e, + X8Z24_X16V8S8_MS4_VC12_2CS = 0x9f, + X8Z24_X16V8S8_MS4_VC4_2CS = 0xa0, + X8Z24_X16V8S8_MS8_VC8_2CS = 0xa1, + X8Z24_X16V8S8_MS8_VC24_2CS = 0xa2, + X8Z24_X16V8S8_MS4_VC12_2CSZV = 0xa3, + X8Z24_X16V8S8_MS4_VC4_2CSZV = 0xa4, + X8Z24_X16V8S8_MS8_VC8_2CSZV = 0xa5, + X8Z24_X16V8S8_MS8_VC24_2CSZV = 0xa6, + ZF32_X16V8S8_MS4_VC12 = 0xa7, + ZF32_X16V8S8_MS4_VC4 = 0xa8, + ZF32_X16V8S8_MS8_VC8 = 0xa9, + ZF32_X16V8S8_MS8_VC24 = 0xaa, + ZF32_X16V8S8_MS4_VC12_1CS = 0xab, + ZF32_X16V8S8_MS4_VC4_1CS = 0xac, + ZF32_X16V8S8_MS8_VC8_1CS = 0xad, + ZF32_X16V8S8_MS8_VC24_1CS = 0xae, + ZF32_X16V8S8_MS4_VC12_1ZV = 0xb3, + ZF32_X16V8S8_MS4_VC4_1ZV = 0xb4, + ZF32_X16V8S8_MS8_VC8_1ZV = 0xb5, + ZF32_X16V8S8_MS8_VC24_1ZV = 0xb6, + ZF32_X16V8S8_MS4_VC12_1CZV = 0xb7, + ZF32_X16V8S8_MS4_VC4_1CZV = 0xb8, + ZF32_X16V8S8_MS8_VC8_1CZV = 0xb9, + ZF32_X16V8S8_MS8_VC24_1CZV = 0xba, + ZF32_X16V8S8_MS4_VC12_2CS = 0xbb, + ZF32_X16V8S8_MS4_VC4_2CS = 0xbc, + ZF32_X16V8S8_MS8_VC8_2CS = 0xbd, + ZF32_X16V8S8_MS8_VC24_2CS = 0xbe, + ZF32_X16V8S8_MS4_VC12_2CSZV = 0xbf, + ZF32_X16V8S8_MS4_VC4_2CSZV = 0xc0, + ZF32_X16V8S8_MS8_VC8_2CSZV = 0xc1, + ZF32_X16V8S8_MS8_VC24_2CSZV = 0xc2, + ZF32_X24S8 = 0xc3, + ZF32_X24S8_1CS = 0xc4, + ZF32_X24S8_MS2_1CS = 0xc5, + ZF32_X24S8_MS4_1CS = 0xc6, + ZF32_X24S8_MS8_1CS = 0xc7, + ZF32_X24S8_MS16_1CS = 0xc8, + ZF32_X24S8_2CSZV = 0xce, + ZF32_X24S8_MS2_2CSZV = 0xcf, + ZF32_X24S8_MS4_2CSZV = 0xd0, + ZF32_X24S8_MS8_2CSZV = 0xd1, + ZF32_X24S8_MS16_2CSZV = 0xd2, + ZF32_X24S8_2CS = 0xd3, + ZF32_X24S8_MS2_2CS = 0xd4, + ZF32_X24S8_MS4_2CS = 0xd5, + ZF32_X24S8_MS8_2CS = 0xd6, + ZF32_X24S8_MS16_2CS = 0xd7, + S8 = 0x2a, + S8_2S = 0x2b, + GENERIC_16BX2 = 0xfe, + C32_2C = 0xd8, + C32_2CBR = 0xd9, + C32_2CBA = 0xda, + C32_2CRA = 0xdb, + C32_2BRA = 0xdc, + C32_MS2_2C = 0xdd, + C32_MS2_2CBR = 0xde, + C32_MS2_4CBRA = 0xcc, + C32_MS4_2C = 0xdf, + C32_MS4_2CBR = 0xe0, + C32_MS4_2CBA = 0xe1, + C32_MS4_2CRA = 0xe2, + C32_MS4_2BRA = 0xe3, + C32_MS4_4CBRA = 0x2c, + C32_MS8_MS16_2C = 0xe4, + C32_MS8_MS16_2CRA = 0xe5, + C64_2C = 0xe6, + C64_2CBR = 0xe7, + C64_2CBA = 0xe8, + C64_2CRA = 0xe9, + C64_2BRA = 0xea, + C64_MS2_2C = 0xeb, + C64_MS2_2CBR = 0xec, + C64_MS2_4CBRA = 0xcd, + C64_MS4_2C = 0xed, + C64_MS4_2CBR = 0xee, + C64_MS4_2CBA = 0xef, + C64_MS4_2CRA = 0xf0, + C64_MS4_2BRA = 0xf1, + C64_MS4_4CBRA = 0x2d, + C64_MS8_MS16_2C = 0xf2, + C64_MS8_MS16_2CRA = 0xf3, + C128_2C = 0xf4, + C128_2CR = 0xf5, + C128_MS2_2C = 0xf6, + C128_MS2_2CR = 0xf7, + C128_MS4_2C = 0xf8, + C128_MS4_2CR = 0xf9, + C128_MS8_MS16_2C = 0xfa, + C128_MS8_MS16_2CR = 0xfb, + X8C24 = 0xfc, + PITCH_NO_SWIZZLE = 0xfd, + SMSKED_MESSAGE = 0xca, + SMHOST_MESSAGE = 0xcb, +}; + +constexpr bool IsPitchKind(PTEKind kind) { + return kind == PTEKind::PITCH || kind == PTEKind::PITCH_NO_SWIZZLE; +} + +} // namespace Tegra From 23b6569fc26cd1675737a0ef02d0a82021b4d998 Mon Sep 17 00:00:00 2001 From: FengChen Date: Fri, 14 Oct 2022 22:20:45 +0800 Subject: [PATCH 146/245] video_core: implement 1D copies based on VMM 'kind' --- src/video_core/engines/maxwell_dma.cpp | 127 ++++++++++++++----------- src/video_core/engines/maxwell_dma.h | 2 - 2 files changed, 73 insertions(+), 56 deletions(-) diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 3909d36c19..4eb7a100d7 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -56,68 +56,87 @@ void MaxwellDMA::Launch() { ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); - const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; - const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; + if (launch.multi_line_enable) { + const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; + const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; - if (!is_src_pitch && !is_dst_pitch) { - // If both the source and the destination are in block layout, assert. - UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented"); - return; - } + if (!is_src_pitch && !is_dst_pitch) { + // If both the source and the destination are in block layout, assert. + UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented"); + return; + } - if (is_src_pitch && is_dst_pitch) { - CopyPitchToPitch(); - } else { - ASSERT(launch.multi_line_enable == 1); - - if (!is_src_pitch && is_dst_pitch) { - CopyBlockLinearToPitch(); + if (is_src_pitch && is_dst_pitch) { + for (u32 line = 0; line < regs.line_count; ++line) { + const GPUVAddr source_line = + regs.offset_in + static_cast(line) * regs.pitch_in; + const GPUVAddr dest_line = + regs.offset_out + static_cast(line) * regs.pitch_out; + memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in); + } } else { - CopyPitchToBlockLinear(); + if (!is_src_pitch && is_dst_pitch) { + CopyBlockLinearToPitch(); + } else { + CopyPitchToBlockLinear(); + } + } + } else { + // TODO: allow multisized components. + auto& accelerate = rasterizer->AccessAccelerateDMA(); + const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; + if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) { + ASSERT(regs.remap_const.component_size_minus_one == 3); + accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); + std::vector tmp_buffer(regs.line_length_in, regs.remap_consta_value); + memory_manager.WriteBlockUnsafe(regs.offset_out, + reinterpret_cast(tmp_buffer.data()), + regs.line_length_in * sizeof(u32)); + } else { + auto convert_linear_2_blocklinear_addr = [](u64 address) { + return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | + ((address & 0x180) >> 1) | ((address & 0x20) << 3); + }; + auto src_kind = memory_manager.GetPageKind(regs.offset_in); + auto dst_kind = memory_manager.GetPageKind(regs.offset_out); + const bool is_src_pitch = IsPitchKind(static_cast(src_kind)); + const bool is_dst_pitch = IsPitchKind(static_cast(dst_kind)); + if (!is_src_pitch && is_dst_pitch) { + std::vector tmp_buffer(regs.line_length_in); + std::vector dst_buffer(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), + regs.line_length_in); + for (u32 offset = 0; offset < regs.line_length_in; ++offset) { + dst_buffer[offset] = + tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) - + regs.offset_in]; + } + memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in); + } else if (is_src_pitch && !is_dst_pitch) { + std::vector tmp_buffer(regs.line_length_in); + std::vector dst_buffer(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), + regs.line_length_in); + for (u32 offset = 0; offset < regs.line_length_in; ++offset) { + dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) - + regs.offset_out] = tmp_buffer[offset]; + } + memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in); + } else { + if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { + std::vector tmp_buffer(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), + regs.line_length_in); + memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), + regs.line_length_in); + } + } } } + ReleaseSemaphore(); } -void MaxwellDMA::CopyPitchToPitch() { - // When `multi_line_enable` bit is enabled we copy a 2D image of dimensions - // (line_length_in, line_count). - // Otherwise the copy is performed as if we were copying a 1D buffer of length line_length_in. - const bool remap_enabled = regs.launch_dma.remap_enable != 0; - if (regs.launch_dma.multi_line_enable) { - UNIMPLEMENTED_IF(remap_enabled); - - // Perform a line-by-line copy. - // We're going to take a subrect of size (line_length_in, line_count) from the source - // rectangle. There is no need to manually flush/invalidate the regions because CopyBlock - // does that for us. - for (u32 line = 0; line < regs.line_count; ++line) { - const GPUVAddr source_line = regs.offset_in + static_cast(line) * regs.pitch_in; - const GPUVAddr dest_line = regs.offset_out + static_cast(line) * regs.pitch_out; - memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in); - } - return; - } - // TODO: allow multisized components. - auto& accelerate = rasterizer->AccessAccelerateDMA(); - const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; - const bool is_buffer_clear = remap_enabled && is_const_a_dst; - if (is_buffer_clear) { - ASSERT(regs.remap_const.component_size_minus_one == 3); - accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); - std::vector tmp_buffer(regs.line_length_in, regs.remap_consta_value); - memory_manager.WriteBlockUnsafe(regs.offset_out, reinterpret_cast(tmp_buffer.data()), - regs.line_length_in * sizeof(u32)); - return; - } - UNIMPLEMENTED_IF(remap_enabled); - if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { - std::vector tmp_buffer(regs.line_length_in); - memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); - memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in); - } -} - void MaxwellDMA::CopyBlockLinearToPitch() { UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0); UNIMPLEMENTED_IF(regs.src_params.layer != 0); diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index bc48320ce2..953e34adcc 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -219,8 +219,6 @@ private: /// registers. void Launch(); - void CopyPitchToPitch(); - void CopyBlockLinearToPitch(); void CopyPitchToBlockLinear(); From 40d9107b2315d09ccf96ef3888cdce5b1c7b09d1 Mon Sep 17 00:00:00 2001 From: Frazer Smith Date: Mon, 17 Oct 2022 15:08:07 +0100 Subject: [PATCH 147/245] general: compress png images --- .../applet_pro_controller_dark_disabled.png | Bin 4477 -> 2712 bytes .../applet_pro_controller_disabled.png | Bin 4173 -> 2630 bytes ...pplet_pro_controller_midnight_disabled.png | Bin 4459 -> 2774 bytes dist/icons/overlay/button_A.png | Bin 3494 -> 1647 bytes dist/icons/overlay/button_B.png | Bin 3375 -> 1534 bytes dist/icons/overlay/button_X.png | Bin 3968 -> 1748 bytes dist/icons/overlay/button_Y.png | Bin 3337 -> 1504 bytes dist/icons/overlay/button_press_stick.png | Bin 5225 -> 2477 bytes dist/icons/overlay/controller_dual_joycon.png | Bin 7312 -> 3475 bytes .../overlay/controller_dual_joycon_dark.png | Bin 5889 -> 3107 bytes dist/icons/overlay/controller_handheld.png | Bin 4645 -> 2250 bytes .../overlay/controller_handheld_dark.png | Bin 3745 -> 2000 bytes dist/icons/overlay/controller_pro.png | Bin 9493 -> 4531 bytes dist/icons/overlay/controller_pro_dark.png | Bin 7488 -> 4531 bytes .../overlay/controller_single_joycon_left.png | Bin 7489 -> 3605 bytes .../controller_single_joycon_left_dark.png | Bin 6768 -> 3447 bytes .../controller_single_joycon_left_y_dark.png | Bin 2639 -> 1035 bytes .../controller_single_joycon_right.png | Bin 7497 -> 3603 bytes .../controller_single_joycon_right_dark.png | Bin 6729 -> 3406 bytes dist/icons/overlay/osk_button_backspace.png | Bin 2919 -> 1272 bytes .../overlay/osk_button_backspace_dark.png | Bin 2958 -> 1262 bytes .../colorful/icons/48x48/bad_folder.png | Bin 15494 -> 528 bytes .../default/icons/256x256/plus_folder.png | Bin 3521 -> 1948 bytes dist/qt_themes/default/icons/256x256/yuzu.png | Bin 6751 -> 4425 bytes .../qdarkstyle/icons/256x256/plus_folder.png | Bin 3931 -> 1924 bytes 25 files changed, 0 insertions(+), 0 deletions(-) diff --git a/dist/icons/controller/applet_pro_controller_dark_disabled.png b/dist/icons/controller/applet_pro_controller_dark_disabled.png index 416e1e2fb0d8c62d9fc767df45b0aa899d6869c5..d45f91db55a020385d07450ec87856710206eeb2 100644 GIT binary patch delta 2705 zcmV;C3U2lNBA6ABBYz5CNklqp5bbB%wr!i&m^7w4vu~emTUhDYwr$(C zZQH7oN$&Q|COw(#&Yk;X&iC~qok^;ztE)~`_bzLofd=Y^|Ni@Lf{u=kS*=SAbar;m z+}74{duwa^E8X4QQ-qfrIO2#SCU0wNe~H?EZEbBQSX~XYw10H1K#KW;+P|%>orA2d z2HM-(4}#TD`{cOej+?2sr?I3aB&FOB<&eL**6M12BGUq-lpjLnG>zTcSW?a9CDrj$ zOH0ebT=_#hsYe%!#hF=+F=|xPOk5ewrN1v%dOLbA7ea5qdDC2MT!V8yqbL3Cxioi> zKwlQLUWB646@Q_>d3dX>wwjarz8A{>$5Z+qF7-+D`!VJ3{uyYVGM5VJUl}xEuBHC_ zNTK&f4cDT6+#7vPNOOu%<~aJ^Nx$DhpZnF5=12PehbR1B==u-z^LMyT{XcTy-zJ3F zqEsqPRlC_Km1gyXKAB$ni${&V|3~c~YOf!7;DNJ{Qhz=Sm(&45_#^51ZzSCBTSa%6pd6q|OviwU(mU_aabAR9P;lrn?mK9UP8qCHc3>!9VN-p$W z@M}VtQ*!zhzd%a)z*A-;Rwc+<4ji9ZG3TWXxiBAMjI0_E2bqmN_yy;BrGa9_T>2*! zgx-_o1WccN#@fS&ZON@6xu5>fCl8b`dta#U5 zcbzN`j{#`*70LB5&G4>C{Yt5M`jR|-ynovnZ#N~z8ZJ*ac+uaS3w@7!sriS}1OK#y zAfBUlOpa15GuW({)exRA`+;YLGQ9}@VN(y50Hh$9mbWMLQE2JrXZ#eZP9acl#u5-` z#brT@Ym7E#?uz7+nBagX@rGTvEM4J`UEWOW#c|CoC{9}UzQJJ4X|Ls0x(y{mc znzo!hWzLD+`#C9VqTVHH237Pk&@$GX)_MkqVtz?ijQ@5PRtzP67LYYWGq9Qn87C2Y+K0!ffSm zcILEl#L>8MKZn&EN$oFtjU$X^WyNGI_tl_Q48PQB$@H0&O^a42r|b!(U_2;-xk%KL zhy>IQr5pc5wa&p~7J(ClrUNt14utUeBUVfnv;`XgAi_npm#7J;FK|^NQQ$%ZMx>(q zA7e>?$x`RfQI(4e^AQLY%YO-&tR?7gb|e6x5W7~)`UxpZOD6<_8&j=?6u~JnM#~Lj zPg=GNl`S~dF{MoDFUL{0m7230fWx$fgx7bm;w!Jbaw2EZTl%NcbKa!(Dz$b@U|+vu z9*^ZpqtjS43RhCfrLEQS8s}!2M8_L9Jt6d2C}*OySOdjmV#TO6(|_kbPH^_GKsgEN z%wb3lQ^+P1IGvzqSCIPDm>anSDNd4UortVDflqu)A9x~TJ~ILi5G|VrB2SPch($_*UaaJ6Y>s!aOUSSy#WZwdO zMID|P)qH;r?2{#2G+!|XAcTwf0a&BaMj0$HHi+w^K5WNiWW`YS6={kIOg|$)R)ULDlh{)0|gH>~3KJ_T6bdgWM&YmGN@v0)L08Bbc zoz?|?t|$F%GQQ{(Xx~(*rAXpImSX<42%ZyNU0w5#QobDsR*c$=M+qiZ`s{2wBW7FV zRkjH|jxqqt2!9cfEd7aV^DRp0QNqYlI;;UJo-pJ;FITneiqydN{8>%2mqP2GxH8wX z8i5pZxtkTcL9*RU?PuC{nU-ZlSnd0!_ZM<*tV#%D*23BqN*~T@fS#V7=_6K*QU$95 zNDRxAzj<|5C14~-L4x9xWjLGP=C?N-1& z9O-$M**T*p*EEdd%GIIEh%X2orvf)zU}bHaRqLRl`w z6iXXoi@KZxVN)vya6m9%iZz0p6}t~5;KV~eDvvKmS@q00&gHF(#LU|ItT}8}i~|EM zzX#bMBdZe71#|+%{E4-h!%01e?OHaDfg>eZSbs5O)=BVNplFks<%7 z8!HZLz^_8qYLD&0N3q~kBiE}|OM-;(1qKG& zp?~%r4ffS6T7_kS_k^3`b;pS(o;X>=igEZl?Tjx1MHX6t2?u={Gd^gDE_{eGqI0J& zILV2Ecn;K&72{YOh$uLUt5^;l^r=Tfqc|b~=hN527uO3;3n(L5z2UAEf06dry-0gd zxB|CHDNwyDl-`Y{e0XFz88nZg{)xuJJ%3*7lvPgDpB3YH2#$@|#>(!GWZHd-m-1A34^tOGJiR>^4z$ zfgA=M1||zYMTZ&{F951u6^0|ynTuF`7m+A{6^)9@k@jRYif|YuOOc77mn%dCvQ;H& zM5788$Oeg`JRxX9cD7kxU0t0K=jwA|W|x$V_O!(A5|JCV-Ec7NN$flC+;hHk#u>x^ z0-UW%`S$HE?pD!y5J7EL*FpD$s%m$!h)Vj@G@g1)5UcI?JS2N(Dn}6U2>P?^EVHDq z1?p2_{QjcL71@Z$Akb_D71fxEcu-=9=Fj-t)Z-wJ0#R$trO!YAoF{7RKYgn2`Fy@? zfad1r6O$%QdOI8r+rGq&qqMa2j94r-96(h)t=4{0+hiz4#;Mq`Mj5Tb7*QsGi~({L z460E?Jb<8_06B`wfAGn;Zbe1ay`Z-M zFZ+Dn>8FzwJI>s3&^90AxdzoKA{YgO@Sah*JQAtdp3XjPVI0TF!jttn%1A|Hf-G|* zwRh8ApdJW^!-H~i20sH#0sToR6uQ0tLmNSa_CI`43dDUEf&uM5ci+yX>+*`+UBcz!VX4{$r1Ai8z7c zdnZnse8I~*cRZD;KY&@YW@S&CHsj*+&bz2-&z|R6lj+=0RP>3-c~e(Snlxp8-sGv? zT{~ZVA(`H;T`%sRGI{Fbiq=oPaB}raFTK=brAqoJ#Z-z!bd&4W{iQ#H2#3Sg7`aiE z_e7YzVnyX#5#4P>R_6zTSN6we?$F_nV9bAI=d?T(4u^Z>U0PZ?$P+VLL2p91O3(-L z14Y49{vwf@?IOCV&6>hw@}z}Pm1|VY!~GdR9E%n$+HVZE1Bl4|Qzl*Tdtj?k&h3{& zk?9@3F4(tkUv8rKNONo|=%>4xAUCA-sk%wv>SUrGg$V|O9|NWnYxzSupL2?e&UFfk zN}YnDlJ=#sC(6po)`2#H=4NGOuN^ab#8r8D=dVedY^pJ!Hv>;7e6O~)_Ps>8kG6yK zSQzv`I-e&_4BZOMxc1s_4o#HxC`_xBOHh5*b={O4#9vT+r-{jKRMw*MASSlg2^4)d znXa|=JHP?p(sAR?UXUrdi(`GHuECAeUba3`dv7v%Lqo#>Rmy=_$E_V+qs!xR$8oX} zZ60}K&2dDY%gSlFI8oN4FdDO$D5QMh`u)LTRb2}By4)TNa=+hS^tD9&^78T~(3=1i zVd?VaD`&JPT6pmd!Ql8*aiE=CI!-Vz@qRcQHvT|xrBe`m#|aee$qy7=2e7`ewi4?b zZ;hRL=&jd3S6^TMmg6|1oIvnVOxAHHQ2a`MLGhQm4_NB^o5x@bEX%O z&-=taoho7AQ>jIgQr6sjybRa@Ol)n9R*1EQA{PPu(!mYDB9?!?)7tt2qlfXqYXU_C?Rq9rl(WA$GL6xB`Eu5JuVc^x&BHf=#(v=m9 zaLbGt(}p)jYOhG8FNO+`W`QFb(_bXSc_!hy#VfI-oJHVI33vY5(9rPr_*HiHiP1pr zm@(tK6}wwu)aV5WG2@Z{OqDRAsdfewBb6!bXzvw+h5=FKm+5-NjrBG4*p?Az3>$K0 z4?bnv)_~pz90C4U)C3zE8V&%=DJ;Cu?=Sks_1Bl40id?^PLN*%2Z3$Y#IB>gZ_(}w z?`DOVwR(QHVRpU9F9`n3AO}_LU83^C#>U#D19bwyyMa4^`1CuW5K6UTbVWEEHiHKb z-m1z>RW>eOT$Y||WPp5s@d^wAlJ(`E;zlw5`M`EApOHrVm6~|(+PF7q|Q8BMCcD><- zU~s&MmUX!`Mv49~mA>ORBUDTUs>@a6F@?`{m3PjV`Sa)dnhqap8kA+us;;g+=Jlv3Cyqd%WU|HX=zdoo zaU(U?Ce!x^f=?=pbtAQ(bQ~uallA8CVMB(mS+k}My3ClSrlXU9*W#_esL0ibT$~>$ za{7~~iOL{O7Jks7Lx;LOwxLkyfjBER#>JxpE`lyUMCRT`?-!Bm%)w!g>*8eLiO-~RUMqGUR=OG-w2vtloReit|&msWK^qrew>8)$x@=sMI~44_8O5j`n8bM&}X`TpV)?K0W8p`rTe^78Vgii(PxKpyR2 z#aqkEfABwDeR^B%N+8*{Sl)>iy%SCn&W>-WrD>CttgVXvV?(6w-@4rveGZkW@$1eR zqaqhtK zr!JUNP;xWySsT^M6J=KAMTIHx>ywX?>Lj1oml6#kk;qVN2?Gq|K)*V(Qrj>~3sqxI3Brp_-aE zp#7Z&9fk44n2c(V$EYg?Wtqzn&hZC=KS`zIHq@;9N%TVB5KIaQXBz>2Mhg$T)d#;>Yv4X4XFj(mUcKG5-lvdzDw*VLW9me_?5pNf_# z2s_f5R99EOZPgY5H|GZe*Yzc#OwrtYyi$a1z{fov@2>zJs;&JyG0#*%8i03D-caF} z*w*Q;>mKgSXMP|!%Lw1KMoX)!tKUwiE3m_gOoonCNwAYGsap+kY-p_CkRJ&CT<|mo zgTYVN*4F+#m98-+YEOQW#72!8rKxn|2#3RV)v8rEJk}W0cm7Dy-d5Utiy=fwDtHz5pFFMv*+Rv7LPhwmwpOvmk$Mjq1~+dHF>mH7~Ta z_=fvDI-=!7_VBK(Hysu(TzCMqV+Az#EnmL;yuR2afx)yrE66KuL*2LfVqlHz1fA4w z+>Qwl-P6~IYFjS?KIRkzdl0Cuc=+LU|E#O4YudEwH%I#F8{({(NMBfSDmVqfO$cWk zeec~%`f4MwmVLkk0OL5$CwJIFSP}WTzaaQUZ6{-6>F2@f?$F(#Q0UebD=P03;nogTJpIWhpUlN%)g>s$1U-m| zqLdsM4^AI5XOSC9!-HRBl*hiF@+k~IhAN{rqkme)ABh0$+_$YE$_#W^{H zX981NTcZ_2h72h?e!R6JLEWQd2m&~<0|_=CwNsk*I<6z?-3YZCCdIo zZ@)ex-kBdrgu~(H<;z#iGbZ+w2)E?r<^8U#tSo8kb^iSMYnu)qduY^IcKu7&fX(abJwW;jn4njFY?id(LH`5DJC1fZhvujj<1e!{Nb+ zI^l5GD!N3)EKPPiEd`3L@Q|7Gne(;+Ien?>uHAcYN93^EQ1eKJ<2WVA$;s*L73bs( zNxD3*kJN1fj=c2pzT0~9=M)rQ3!1&Yq4p0Z91dF*ZdEb&r#r|K2$W1#D~pNAjda-* zKb^Skw%b}xo-5XyQ=BVS>(Np{?np=a3JMC&M)f{Xy#-JMu)d*otHNqeRJH{I!7nBn zIZokMEcQ_hx4CY0%A+2q0|~w2wN+JBqZ4&D)Ybh>l-rFpk0#?b`31$7#MJY+h^~zH zqq}XJ`u)Y%D|#=61EM?&pfCd%i;?fR^)iu?@W z_0I7LpGO&w$SrO|&8E0+kCWk!<9P69Umm P00000NkvXXu0mjfJ-OS{ diff --git a/dist/icons/controller/applet_pro_controller_disabled.png b/dist/icons/controller/applet_pro_controller_disabled.png index 72a549ea98d988c7e10d82bd85126f163b409ed1..8c6bcd3083e8bb67cf18262ab161b64df79ace4b 100644 GIT binary patch delta 2622 zcmV-E3c>ZwAjTAsBYz4FNkl_G4yd zW@ct)W~Rd#N9l8YwxhVor9V9H?UfXJcePs0u5=gF-(V1#fZu`a0#DCr>`vm^)W=|A35uJY+eZYZkq{X?wl7qCAg+&s|>=jJYjm zdyfW%zw7_I?SEdQJ_m98@GKh+=)(K&!q0!_Z23FB{)cgY;p=ZX$Nk0P?;ZDde7y(n z-K(>+b28B?6bkN4RE)|%DJn6_H{NzXUTm?&X2y`um2*z6&Z5LP_g^SDl4Ik<^8X$d z$-mVVtLP2K=l_TE|Ho%WM2(??;OxKX%%(dCMy`vQ`F{;B|F8NokID~IMi0YFcfzm@ z)b{;9WQCmFyNY4=ENnR5oR-xy52d4{Z)U?V#%(xC4LNn~8Ah=@ztt(%N91gScOQ<4 zDPW>5fbn-AI^JW{&`D<1F~b5A!^!+U=Tw#w%|PDqR>cGVtVIaIle6>+W~mHktr8mE zqoU^Rc7L%h-GAk|=y4S&^e@+`4^M7n-=Zp+Dcz3yGv0~G9FGNl{R-^<& zANg+eq{z+Zd*`woyr<~hiKLrIIB6SgMP;650k+*lNC=i(L@VkHlKpRho*Ck`*Uq)G>poW zV}DMjyHW&)xk)5|3>{-06guEb$psAKyh#(s73I80%6o%5?2$ylgRFS(z4xB@`s=SB zFO_2)Z)fQBBxu4B(aWfN4mvFqF|J4eZKSPQ8waKL+968(Q8yDqp(V$Ar}LbEahjK7Z#^K+10UL&*w z3$WMFS<4SGY7+kbW_HZr!vCgaE2{cW6@<<-U11Zc}qWH(z9ZO_bzze zKgb)uh4~coS?JyW(%6{Dli|r@iGR@ltdhGBZKmz<%GmCHs#^XaevG$AT#jK9Ya%OV zAvg)^H4y@r0^ZkCU25{vAcJLo;qqW4R3oBq@+#?bzk)oHbIOZ_Ci)`?i_CqK^2<3^ zi93!Qmbc}J@!8)s`LN&`=Qi2^D}JOEy<#}X3F!2VVRtWBrxrYhb|`!Ll7AfYJa+=K zB>nBUjBZ`*Lp!_$m;~wRKp5*{?f@&^OEdx8;TW}{D84MIElL}?19PW>Q=f@#F>nW9 zmrFV63k-fzqy$4Ypy)pl#BQ%(uUHHcY_UeeZb!PV*$or7DF<~7vCL-@VJ8-9x+lmOBnU}lJwqU-& z&+p*pCE^nAWuaNP0Ez2g6lhboz%g2^SXKI8MkM>U8Qo))Gmd*Ei^Djw={V<2WnmX% zP6_4Bt61VhI7U{Oj3PqrOYB6?X(d0$+lmR0f@fVFGQ_L>jRjuG zle6#?A?lYu2qKB`I)C2#$z$Z+wSzK7;6Nkft+l`Lv{087BkZq>bqqsa6C`q`og86w z40D>OfsD0YtD&>=oEc%%|I9_HlW%>T9A?GjCo#+E6UT9;5*mQcz0mzXR@!wO^9Z7T z{a`EeST=R)Gs|u^$VZq?d8^cp(PzcC2nANoJgQ|>L%y~g_kVmI73*yXJ)8JC7c0AH zIIuFH)rl8fj8NZSGbH=y{ZgG?3Qzn3jv3xS=Z-A;=Vq{Co_um^X04iQ6E2eIWy}#MWsVhr^0hwPnG>N{xzRBU;KbTNO>u_x{ zD^7-ggkrmYR(~pNoa=!(2%q_>{|6?w#`~*1tNClUB+nA9!PEg(d`g3LnrT8eUMxs+ z8o?5TiFY+Wd{Us$A@`+oET?jSXaNGOco#oS$~6MHRbZ;$ba z+OL6lsDG7mJ>F$G-ch3Uh-SsfmlEzAMCb8Mzp74i^)&CeYqBt7juEX!G%MaaDeqA? z>L?lneLTKK9gM#ncVC=7}`d>*$kI5yPeq&x|$dpSH1L)dPN; zx~91_MaR6HFO^qpm;^cAjzR)jb-c>R zBJT+tinkjR1z2%U+#O$T6gqe9IN_u({r1sA_{Ha}L+YILeaCC-A_dyWiq*9^_j}qP zh=2I2YM+nMkETa?MZ&L9LLf8dZ2|Rv2HJ~cR{R}g#)Lb3Uy|EI{Xy|Iggz@a9k~f@;jUVb gLfTipWXOf#lc7*Vsnm+>L4zVwX6$V&~ADo8{ ztt*Ege-7-#56;zx*p~x}8~_`0fR(_fz*ie0@#%o^3KP${H{SU10kEHyNMyyhgz>&5 zs*@GjspxjBeC(*VamkVefBZbQ2OSu){4k_t9*)5Bw`EvPGwBeoYQs^sIEAReL?1Xb&Rm<&R@vEr!cts??GwfP1p zufQ}!Tfd>o!wADb3K3D%dWbkEp2AhNRb|_N$AJWJR()hez@pnRc8}9z9DrcZ?A^2H zPanCiYxC(hiR-o&IZj{Af*~ zfX@)~&2VMOy3ZY$##q~#R`r*_QGgXtg!Lf5QfnUwhsuAGB7QS4A9x*@4eT9E01hNA zEiIEj?KD3U;ZjvjMbv;8lwDT!e7Cy%ce(U)2BxEy)(uM?4M zcYGM|B5*3u4wMdQR0kMUmB!f3ixpcB6apU=9c4}^DJkj7rqlnOOyQ$#d!wQpt=Ol7 zPS@CQXhCg`pa66N*8ttX4Zy`i7{>ueM6{-=q}g`u7z6U82*>RB%(mpz$<|O8jsLAc ziyHZd+KSROgY=~ehya^`S->ZQ)IX>+H7!5i8s}~c<+W914`!0d7#MfivhgNSupZ>% za8>C`L+IN8PXXrw4+Gz?i^T5`^tEuPJm;OtMtggEaMvC^y+AtGRa8{$&Lq>=xZ=wi zNYtpxL_}AGE6eW5X0JZlHXHO&$1Ev0rVeLPvGxCRu3^K;4dz(1$F_K16Ld2 z4MdhVMB6VMioqS9=`XD6ue)sUdDnIO`R%$Zhgp+Yqsn!lm!RBT7j6APHhccqWVI)H zWv2GbbBvMiis~an89)l}vVZcurO~Z;M1+b8la%}(jF1Lv8J;8)(qlffOYg{#Xu~Vv!93h zNZVpWBY*?4-m&`4z;*apaXqjG_-$jX?d*iedu~O^2if{+jJ2Jau(HDqmAsaE z-F00f!J?%Cb3i^3)kQU-ven^G=~8#;E%(sVGoGI9uZ3MV5s9=PlMwHxAQ#j};_rdI zHXK^;>wU&lp2g}}K%c<$n_~ny%V3_%<}6BOmKG6}8?&@M0j$7(!tS0hVZu=!`hc-s z)-<3#7H_huj~THqHb&bn@8fiE%ntlZsc>IoW6NX!ck#Rr-RiPeUDr(jBt&B% z7Xl8#iHLb%)v8ra%6Oj@%vtIE_X&*ax&e@{1t@qSo5T1un9QhVZ-d47wc;6Dw{DFZ z)hktHXAWDJ0HZ)gchn~qKX;&t2S1UN5U3j~?dcnTe*BLGoR2bzIj{bV8Sf9WIkm)F%4d|m;9ahOUd8!&+noX~l zmjM@l(!ZS?wc^^U@_R=Hy|EL<4LdqLpE_yEMm%_xgb@2D@4X^YQBzrZ^8kSX|7NTK&Z9Rg&O;|J>Z0+OKt=DbA6oU!bwdj`W|MD- z#>*9!qC^mm4p)`UOLa1p*`lmbL%kJgY-wp3o5^4}R8}jF%mkSWdY$pSlkyA-^v{Z~ z$x|;UZgcx_;?HAACK_?7v&lyyE5<1_D;fp+q{3gOw(py8IyyQEwr<-yGw5`$TeN7= zbM?{qk9K#N)xg=A4A(3!eG_;yTgUaWmXlGl9{;KAoPOf5Ba4fRz24^cfD-%!a0PyP z@K%m-4@^RfFzI>O{t{|GaY_6KDw}IVWntI7WuyuAoZ8XRQ80Jz+#W~?%$AS$oNSD| zpDg`3RxkC8yyQk&uW&=J0NslmHBS?X}m^lgYdB%M!)-0Xi4($Ur2rVq8KJ zjlcy!Lg5$MRZ#D`^FB?=%$z#u_20j}`2}F#Br0m}%49Fx*m@;!I>ws?;JWiZt&hex zZQcIabo?*p{#QvvW&{6~{Jpxm{1t2EPRGbkU3X<>p}QG-5(PD|KjG|ML#1csedyM`Yf$|-94epJw?$q? zIBlQ5`z{N!td&>O4cS8P8)BH<&=fDv)ggdUd$~Y`K!rm3a=s+lCeml#>XQ|dc7j5Y ztW`h*mki`nDh*Ao^8~&ko;g3gO+;SBFUclt?`t|afc|&7g1d0xM$oVtmd0XjCk)1s zL9j0?2Ab2_vX)@|*JUvy>^|>?_E&9nv7Vk_^Az6hRDD5Jvrc~D%93@qOUD#YI5udy z#-wDu1F;E6egRhiL#h$0s_FE4erw^an#!^t=A+{n*#;+Cs9Rp;=*=UHhuSOX&W~4beUmauRO^Z$g5WnkSMh7bA^b@>Ki^_4b*g5GXyEK{X#Q^o z)=1Hf#`8=iF>y@hTtlYY-^T7|d z?QNoO(P|LLTL%4(QFNk#EkiNU(A+vhkShZkxc(poB}pSB(vh}__@=Ztb*M;{|R(PuaAT4Bw)7! z6AUnV(x_1bt~vS{P-#S{&9onU5ZMHj_5OS_S+Zi+b-NL2J*8=|9rVUxv;lT**}Z%8 zKnkh!o^hEleaf+UtUnGo_W<_;Bk{B1jH$u&DaYO`FmABtaklK*H3ryjfK36=a~S0Ry-nI zz;#`#VrmVhKGX5EjEG#NRvt3CcgGr4hUXJiCsbg{oy zJdX_LibX^%b`ByTx2f%vY=#;emmjaf3bis{MEpWiZFT7yR9lQ^*ETk{|5dtDL$vig zWAt%_lA4+uavk+J6r^UwE6MQJy>3TUFB0WekPqqI*i-nL zz+_S8*M!PeCuRE`PF%HWmGj2ie>`82=>TeF!;Gnuo;ie{=6pF0zpyn1|1`y2nS@rY zTIIa{=9Y8B=*a+T<@Fg;C$0TIFuu>l)%c(A-3+l-{QsHz@PqT8WXX!Zs1yNzzztx7^zsL;7|HmAU;s2wKBcw2%1M|1#M>xU}hQ|K@ Xf-zkcF-jto00000NkvXXu0mjf@=_E? diff --git a/dist/icons/controller/applet_pro_controller_midnight_disabled.png b/dist/icons/controller/applet_pro_controller_midnight_disabled.png index 2907f3be42b56b014388582d0bffca97e7e3a4df..d27dbfc66ab2a17842567e5bfce928e18a8662c1 100644 GIT binary patch delta 2767 zcmV;=3NZESBGwg-*vNeG`xrX?8R-`(}1yQAQbMlniZcZA&yaH?ON4 zDx;;PW$nhs=4Trkn!f7SuipyJ;W93}?6Tz>8=Jm>v^6$14u4cml~G^cs=+WbA@dp< zS~AM1GMbv2E}+*y{<`LxYt|BF%nzw0VU$;C9ORpal~ZM4X4(Qqndlhj5K+bgA$3@L zNHtHcudm-)Pnu>tY4+A-c5P8)?2+!ZmYy^Yh$oG+@yuiNl)1N_Hj`R)-G<`-M(5hv zHRIAa7D_x1g?~oHDy(fVJKg8xn>VZL_S+CjPS8@;+<4M@3D*sZr_8J3(j1KA*HK`+ zyYc;gik7q<)TH?e?)7R;cr16`x%N6ohH6RU8eHFk<4-}R;Te-4GjVPzWFlldWSTB< zTv|`-(mZt6nwphFv9M>lZapYH#8x0>E77JUIe7D%^MB7je;pX*cPFIIgSYgIx1Vdv zE7JZxPZj4_UV&leKxV}u)WzqTNz|Ff>~I6cQ<)TuF#!*l+5SLd9%Qy=e>Z2xl12}B zO*N-)$gKMMmfd6Nx@v~iuBmK%*NW^Gl%1rp{??hya-u-&jF*zT2f}CDozq5jyu$3Q zrOcCK@P8m(8aLuuPr^IDa}1F)5_GhU-R5}IC>U@z(Gf@_GhK1>DfZ179`;)@K8Z=Q zzm~Q-3kAqCgPoB2y40+=bIRHXM!Fo~^s&yDn4+-^jKQ3!<7J6O!A^Vcy{w+J#=s42 z&(JF~m*en~d06r1iyt(1H1qF~`dA4B;)tUZnEDE@)gt zwy7vF)z1@9Kdqs%Vo^ju?{mnrAml5bGk>Vm!zXRL8YaByURO~34QCzbL($1~YJ~^f ztXKdfhr5AW*04;+0i4rFA2|S!Slu-v+0uS1IMv_H(*t)_=j^ zpQFL-9F#V@3k9s>G!VfsR)2|JBYXfwzVxeI;O@^0AHZ7%sjQe(08{3-zEdg`cIf?- zn9THa<(p$%&z|OIWSqOnb?rZU@4fd@|9O8VlQ+-M$~t0hmu}`7{)THWAbsS=FsztH zqQ~F`yd3bHPjj9KovwGi<=RP00uVp@(@0GIW>NqxuV-K#2q%m*L>OR{9 zDEw7`VP5DcR-8zi`{91mAq&+uddfWB_1qa8takxy3~;|Zbpl{fYNvrpvwKkHKaw>O z!s&;A#Cxz`j!B^E?MPP4nNipEWrPb11*gBmQ2SM7u*@%sq-D?(?4&FAUVqskeSc{v zZFWm$cXD~(Pfitng#YvwG;@eB=`s^52Jf{T&KXW@;lDSW`r9g`p}ZM$)Ja0=f%$BN-Whl&os zt}?SRz1GEiZxcRna?30oGk-8=Dn!XU!V7d+&H(IkIbrpO4nid&f-b8lI$fO!2pUA~ z6)$>WXzuKhkRKO5pds+`f6av($4(n~SkQWsOb&Q9EN=GJ>+W)s);Xfp}dnc-*p+Mw@;qjUjs`{$eNuMeSg2vD~w${6R+F# zbZOndjE_OBUIfF0V#P4Py7>D)J(B%bG>*HRftO#x;?R$5>Qz;%>F{7ZnHjcV-n8yw ziQ`4w26?pws1*L_JrM%z{W1Yk*xh&6E&rV}_6%UfZkNB^j~?(|5nkAb8ph<2xX+5C zUF%shK>4(X8*j^QJbyEIh;riAP~ClLo*Bf7T>}{taIY^4GK#cLw8yen-F6=RnHo<9 zjDw?nnl?Ol0apd|s_dr4FpQfOd$k*UggG&6*MPIu?1Z!~^$fA@wHvT;<#lj$p61n8 zOnxE&jM3ae89mUozVm4A9G7FlvSM6+pRZx)`XNCgm(Rm!7=Pw8kpmfPaI1kyu-CeA z$%QR*Q1s**3?^VVD<(hj38!C}NW+B?S+X@#%VgHc9jAFeRQ zlGz@`FpSEIA&&?Lteo-2@1a*i{)Sx7rQ%ThrZSVE&WTE6e~|~AfY|C(!{laN8b^hq z-AQ98trJTN;;&B7dNN|Ng7zSTS1_q5!a3rf!Ua zD60{462*NAM*@{K6PunwekuYKRy>=mIMl$>#@Mp#Tn-aL=A!_GQC-))Ej(@WC6yaBazx|0hMidSHte6`Hg7O}9 zqmH6L&=#}_4KvH#>kL_#y$(4@{LkQ)k}#~8x?yZ@hyp=d&?b>QhD{x=8HcEUO2&$* z8%vi$_v5hbz#PTG>ElGo+M`esggD+ugg_}-F?Wh1558AeBxE|V{VCXeFJKE84VmS- za(^NFTuQ}?sUvlz&LSk(g5lzu)1)##3OO~fV*Nzjc2Q#E49u}&%vvk@HR!2x*cZtN zV(;4?EMqZ@Rq0veJ=`}d76S|#G-$aTE9T~FzdgR3D8kSRXY1Kl^T$UEVZgt$jTpSr zXN}|*GZ6u$WW~EznOl;G29Z84Gt&nA?|&7cf)?c#2__$pmS!b)3&f?huP8Nwd&MLD z{=1iD57)@ZZM+&(2)Cd0q;ZNLx#?*agVKDP{ZC5!8n5}QvdbzWO3#Y9Jp^ObyjJtn{q741ZQ!hRTZhFG$eJ zsWLD#RpnSQa?c*3jD=V+z2+N*6_>%rIg;^L{D2OAP=|7=43a&5q>Ct{j511ve*yl$ VJWtHGUWWhx002ovPDHLkV1oMmUmXAd literal 4459 zcmV-x5tQzUP)xnK~#90?VEdal-0GsfBSrsgoKy8L9kY>Rt15XAQY)MGqKQQ z62z;x7JVqL_O`wCRZ1bmwlY30kbvI1q}ATmThw;lT2e)UB=QI|pj3GzQ7Emi7F<-+ zN=tc@%*@&Mk7RH%nMr11yn0>!)?$(G+xzUZ&tB&{-#+_01E2CKpV9+3-D#w%(x*?K zp&K`DJkiq*eO!=i8!TVGd~htLzgE?Aa&ui1zW@F2pZYKy`=~M5HaLF#_;JvYB3v4Y zM3#OSPJJB6dd2`OS#rPInAnRzj;aYPT=;{!9`@@aC+islP*E|jNrbxr5wVXfUc7iv z5Bv3z)2+Y&oIH7QDX;}N+Zfj`dd{Vf2i6ln_?Vy-#o~|$w1E=ZK9~6M^MQ%!X<& z1@c5Cs_?v^zm4QZez$wWhOBa~i~}>NXvVOJjs6(qW&}_Q6tRN7HvJ1@r=E4zfCk_k zRVu2VeeM+%eG;_mHB13PlkQ0-o(aSNtF28ONztFcF$G7Jhhw>sMVSUDqriCl#eOhP zq2vR(pbEqk96@3hy?*wWuQ~~`9*A0Nru=r(*0UTN%Z-S6n<$3?VkUN~IbRRIQCHWT zNxyN%6wMfBB3b~@E60^{lIC-BBhpu0j8xSTjxrRH;RqL^j8N!J>x(7Y>LUW21WqXO zrKZrjmp^b|Cise{#4zgxdLv>{RYCW}RKnuOs;2sNFT`~#E0?VRy%qRVPL8Yaqs)pY z`%6c*sD4XT%0%pCR>X*^LFA}ld<~n|Hg?v&)4+_LH7mE*p;HH~>Oc`O( z`W$y47!3Bx&o9^kTn74Jd3pIA-5*+yB5r?Rfxtajaxwb1qj~z7&Sq|>Ye3$i6Zctk zkP)_==<5t@*iyHMG(iCf27{-qb#4M$KyIt5sw(OJSb7v~+FVm9z0Cj>HlZ@K-)ZOm z&KjhhF>YVUb%NZl;@sG@dCk^ryr`_KtN>XI9E`=HWAD84&cA2V{=;U1ccxb*`nbi; zZ3@*snMx<+jG0t4W0+tb07)!y#74iO!kWfVO+)rW zfM76ajggyGIV!@nOP5yNXdGSvd7#i=Hmy50`<*uHRP1x7a$_$N40iC_>-F{$m3oD5 z00BWCEA*9oC7r#7&1)M~VJSGL>fD01uDRNz>7FE}xl%^7O2+XA7;(iE)xu**@cV=_! z)UxwD-jX?Pf63R~Uz%}tDxC!j7CZ{N3bapdZr;i%W6ldSO`Y)bPMu9V2EMDXO@;4m zud6%SVIZ-bgCxBe6!~jsHccP13AjAfrjHYnS6HTF-FE(wiS&>S zhC$eqYs})feDthYxyfxB)~-DX+H^WkCnw7~1V%-!P;^^52R+`>uZqgOKu*H#0#SX) z;|okr)~~3jI1KuYR@2N2cosV7xzYg}bQu(hh=P(76494{{>i9 z&6+jq%trvlZWHJ{FkY|MCFq1mRJNycSmC^MG7-`)tfx<(Tmb9=E@^3rR;rQNsy1zD zz$&E*eSrc;O#4JpNS^xnjpkWnB$T^$*T4( z$nP~cox{NXPGtMi2@V5aIHU`zA-EXP6Njf^Rq;mz>XyKX2<5F-WZBL zpLFNXJN@pr;upD&3nkVv37Yvl6?+i}f@SQ)X;ODeHn#X`Q75!@K;tQUl8Dl5-N=wFjXPyPn zYSG`Ig5Sp!oUYzoyZvN5?7__o3~oS5tL znFIw|v*K7Rb|x#XtgO5#lXhJ()>}MEl?4g6hPy03Os7BIJM%1yR02!HunEMIC_gh} z3JVK!wBPW<(VWPz-5WNXaC-v>jPPbdsOF2^ade!oY_x;j)7Ho73kDbTsjjY$#pSI5 zkw@dT;);rjH@eeLoC)5*7%S{)cT;t3W4LxkDt(Wy^aZdZ8bh_89qpafCr?hkBS+sE z(9qBjfrJ?o>pN^T@MgU9=W1ywQ<2H;qSEWT#K%gY~&XT`=i zkFHv^D(!dliAC2Aa{B@gdHjLbJpRBRJ-*VavD2pYPSiPXNWq_g_HR>>M^o8z`%7jk zI?-sf6ku<-`Z#ca-oq}A>zoOUAccbNieK;i{l=HX@=LM$Yp>UPR_9^p9*J4;U;eUx zekz@@)20oL+Lo6E{te|qluJP8yCOL|5`$A+T^-BmWv2-&Qjtf1FE@s2o=Ub?c=gr& zjmj?#_P=(8H~b-fUy(*NXZB+WkCHF&prZR4L$&{rXvpIW{8f>+nnLR)bv4lOzLIMk zk;!d6eMaHT&Q*~IjcDsOLv?jSsCGL*Wo6};5^Kd36+hV7zW>t4LBF1=KM^V$HrFid z%CDsH>{1xs6sj4Oc)!pe_<>qRHim2FLc)wubgW`_CmV8^T)#mJCuC4d5#(A`%2g>( z3`(m^Z;#7RIkRr1%8J`t)wESWRRUf4l!(V$dW*n#C(kMBC^fqgE<1CT;QHuXWB!n= zzN3EiYYKN+s4Bd=>@(Rok}1hqak9sqG|l)E^Q7Y9QHcJhsNB2j*)^|rP>8hb0mim7 zTzKX55dhok*Swp`Xk(~mG4PsNyQ#CzMKugmxBk;D?PTywT&Bi0CCZYs;-p&f#LCtOurjL3J5wR4*4>fY;2*T5bz zjA{ED1Up779oCm*nnG)z6ztCox@uB!@u<#p+rBBsi47StMAIs_5ex>c<2W}0hk=># ztoY^4>)vYITr;~dR5PS8RC9J?sODQQtY80+uIydzoi*G#X0@OzcWtTtO;`FVvPU!_ zFd&y&$L#H@AwWZ@_7+6{9BDS&>16S13axwDYR-U2gn{`j&VaR<}cWr7>J{ z+tQ^~4~lRbZ6_#xwR&S8ahd-~(vE>11W}MVs`iIX;o5E;we|P|zfzTrE)_0P;q~tN z-#oZrd9U|QU61H=lzV|URmo_f?F@Cy5opbd^YaV5z-28h(aQeLsRc*0cf0IZ1PA0? zfx8uL?9a*N-R)PD*AczQC2+pBW~JRC@9!t7RAgU2Juxv}neR!0!Qkm7OYXnPnAnRV z+*(pn^6Y{I3sRO|3kwTZ(tpHLN9nT|$ZB1BAALhqN)0gFvF7dWnD9&uT!PA1TqgHh zA4X8(w9kq)?YZKHhK5L9PUI%wrg6Tq(cS4wMc+|nxB(dwb7Ft%mJx^L2#d3Mqji_} zvtk($&x%vgn)unJ4mNo+nh`N|y(_{{0}M4WSJr`X`%9(@T--dU;Gym~kS)nsaaC2- zoOHU)gM0rJxWw%*nVM}sF6CW?VFu{Wv4dUiIg$}8_n|D>TV36fZQJfLZ{EBEs(cSX zRaT_SiuYDmx1cN%m4DB=lh#=DH^344p4~s2wuSz(X(9}}U`Roi`;VTc?euwiEqJ{AlH3pywTgVv&pMD?Cj$J0_F!q*ge%IvywVm(5B zCQQ)WN18%4YqK9p&y$~@f2LNPpWi#>>-nWn?HW~%dA9Dny(@ceUtk8Rc}<~p zn+?IBRh8RN`_r-B^u?zM-oO|z3!^dTW}wTW)Bgpx-+p`Z`{#=FmNe&z)p0Qg<<8EM zzRRaxbB>@t5yP#-e+1DKT33(A11>RPkGJ>>$&K8;va77H9_9AkTi2#N>hV#KR4ZP& zY}vA*$vQhX)%-<8?{Ki|Q{^_}{edg26Wt=PyrI4(1RYL0l+8r!3RMn>unRzu%Rxt= z`klsb?Xw@!_&yMcCn$iY%gYyBn@Vqjue8LX52L(wW_-#7L`NxdYg4FZOYU_u&7wQQrD=SB}ed!n=!p*HGMmys5dR<3N{#3=ZK30}L zeQroWC^_}`z`t<&II(nT)r}%N))OZvy8UC(k0Y68#hLJl3JkQ*iudo|U*45|IzCa4 zQk-SWmR)VFZlJZ~Ii_-}e1}9M<>V>vK~#Nm&J_GL@-JWhzsd%2e57a5f%R z<_Ux_ho@OnU58a*1gur}TmMPl5A0AdI@I8_G#~p0Yy~66v2Tl1GH*Xs z9(%zwuopGd{(li^CQj1y$_TN?O>Yrq(=zFdw;%?q#{j0xM5 z=NZyO65Bv9Myk0cG)rI|7&A6zm6nd6Bn7#ImG8^hGV9eQ?EEd-DKNt!Y00xEhwC@oM4S)9I1PxzEsooJLZ4Z~b;Jb&8 zmgUSPBv_YsL@d&_J)IQ1D3!id18OEpVYig#8QhRkbg0~HsYD=AGy$b}pH51)@hYht zXc$DZ5O&CkPNcIB>WK|U15B0d+H#sV(Ag&|qymYk;Zrl`ID9K7+C(E~CXn#0&--$C zUQRQQtA8|Vh60J|8GoXJyOmeyc;H&G-Had21SrQajh4YcqK|tu>I*r|%el>y=nCc$ zBB+l&i%L!tYH6i(sxJ{T%=M_5b&8y378h%jUeU(WFuDKr6i=e+emT)6H9~8co1)=d zClyHdcoS_VC)!7&(}LhFTG9H142?aBVuh6E1%KSE(Yc!-ILTxnE%o-m1*NE{k(p1M zAo!8dKvGA$imG$uW(u=4DpiS}IfolHzdKy2m!9b=iif2{@28{Tk1bg@gMp+Ta22&H zNQrjTXpAC$=TWU_144q&T}9DNM)OSVOyS)>|9Eksxj^dTE;>#|^aG8=MxIUliJEjY zynjrDXSBQMEORr3?!;fXN_!nVf(XwHchTh%nzz!?@Fej!#^?hnicTtX1ERTR`71R8 zlXp!8(k6G&9TGEzHJar&)%?k`xWP;yeIJSFYjiZ+L1^e^CXl|5RlbW#j5|Av$bSh0 zQX``S?6b8O=;;U~`)*I7Wljzz`Vk!iG$Q=6H*u2XtuQ}`=oHN|H}g84<%u)+oK|#| z|J(j^EU$&n{cGN?U1lG`GdWx?5=iPJ&!SnS{foBLF7kQ82aQAm$=6GJmmY>3N{zTu6^007<}OI zTJ7={Fd1OJzmblH*SvPnYL?$hp6Y!}R$sfgT0{Ukg$bnZ-EZn^3XQzBg=K=9tOvpb zQoW$B^y~uN(Kq@D0y`j|fwVL5ll68r(4GF;g?&T>q-xMWs$>PNGKXQc+kdhy(#{mN z@PeSC`hU?qkk*EJ;a%P7wybPk{v%iLmcI~gDtiep{|A@3d`H!-R=84w!2-YlD{P?< zHpgCbYj)9p$17Vm2x#ilH&#@%q)P2?kB_b9n7sWgxym^X^JA&GSp290yExXyPIGOt zn31Ci;`TDTu_FrX^Wnrs(|=LtTIMPVtd51|I+bnw2obd)2Jh;Zw9FSyNOQ9plXILE zO6_N9>F0xlm-stK7=^5YTI%TJnf8O~3v|I4Ove&z!HyrrndG0;Wz4zR6?mhQ&?maih$-x@REx`={0Myn7aS7zH{}oPl zuy(BwRRtL&#=_Q>6QmoQ0a&oj6>Wo#0RX;N{}q-J4L(V*Q#{tvJ=P^EC^o_GdLWRH zkN^*f42!wq7aa(Xx_-5C>zo8=LD3dr?wVLde-IWfJS#GCe04YoYT(;O-rF7iFebJ_3-#1A9^3KE$-;ZwMd-P{%7VI zD+<`&`puT%rl&v5+vgDexpRAKDQS-6XwiCgyDBCAIAy!4J7%jtJ@fx4 zsc@Q44C(Fd?RN3v+e<(asmcyv$MS(i&SXEz>Sr48FV~94&*MsfTGm~H^$60&sfWnfTkcxM$DN4gnYP-9;=ZjU4`|RmYfEWIqX3h>Qi6+9_{qt1n z{v#2-yT<>S!C}`qfEQ5T-07oNwYc&GAuQm4n*31le~kIXaFrzK+*y>$EF)@@&NSYUHdd z0|{vQ9VKcR6OE+&I^nCR-uAGTytOL=(4PEy%~{685@-dI*T$|GoA-N1iRcdG9!VbM z*sH=!V4RR6B}z*K#H%YD9SW+BSJ;Pzui}VW>B~9{etjg*s8;YDO}Lrau>%UVoRzwz zip;_~=)|D3#KIk^rM$QA$tU->W{)bKGg$Rk5}co!nu>*sE0%I^>?ZdEe7X!Njl(t= zy@!A^FfgdLok~}rR3`2u_tVO{-@{@!&*s)k?J;kJ3F`aW@_@cJ1yq3m59ec}T07_a zyEg}pWBXJq;4LGc(U4lc!*L~Pe@Y#GV`o( zoTZ}{U7{!PyFZRV<5fwGWTFQKZi-1^gugTs^78Ue95JH$^3DeXY5 z3$wep5%qvz9&n=tS`3=i3``S&kv5L^U9*3>JV$P@{yr=jCANvW9L~j_88wnj(K0`& z>3uHZ$Lc6DUXn{)O>L0i)r%SP8MrB3X&7atru4-R%M0(lOz0~x-EWC$G=$QwgA4X< zt1JVY&}i|$nIw{Zt5@%gT$JFa{OaoJn&D)MfH{M~zyZGru&!cx6BB9L>_`pd`${Ii z#0v700NI5Md+w=)a6+MK*lH#GjKvKis1V$k$X=4ArDd66R5go?vOPDZ%kg1Dhw5=rFD@nM@yW?2k+Sz3b)q{O}mSjNV4pG$~2&<{^@jy7fGDeAh%wYofjlE>A)N*Q^8Q?~PWSjd5~!S9|A;Mx!mB zn_3CxAY{Z7&mbq(13VX;(P*yvr`ATs#@14{y==zp)`*`wgJGv`i3$sovY@&!SCnN) z$Rnru2gV8Yy}h0-tOJ6`D^9mR1 z>lIGB%cXqFUY&^R7b~3oLcLyEwtoIbM8p}3IsrbJb86uW*+g>k`FphpSB0T``}X$s zHviY$b?wvB(?EJ@sc4n4s)0~lN5@no(V0umeB#5058;5%MXL1ZeH>0vIdGF}8K>!E zB`5IvPZZfLbZ8|wV}V{>-ckGL(IdMNcpw~sX>Zr5Yvd0khTZ_wOBU><~FDzKV*9Qq_%e5ejyyjkWcP z7c$NRIQU}E1}TG;qj5Hn9dW;}mQ?jy_Y~`!H*W@e;%Jz+e2@!6XX7@6375MS0zPMO z$h5gcgHmntjf&(}jFTFs!MY&-cQt`#bmMoMQ!1s|@#GOp`T2Zab6usm!tC`!ztmKd zo*sWq@!0i=I(gz`@SK`ah5Chw)fzin1x-yyBMrY+(Zp|)%6bL{-1A_}wAn@LJ%xxs zoqBqDqBo`F2c$gXJVD(as?WF^G<5~Pz& zSy|b}(NVTn&fog^(6_BEtJKtfCv-2wS_jfEPqdoE^1T{~iGX~f#YYH8#f=HLZpznZ zkSY!N4>eZrBIDvZqknP(s*csKuGZw7(T&Pn3HImoaCVlzUouE~xkv|!Y#^PTo!6ND z@}RYjF%(Lq(jY=mePLoEy{oJ1Us~dJ{~l(n6o>N^2i3bfxhH1vDY|!tmN>bc08XRZ zAt}E$&A|})I+C1{avRZyz}&rCmvM0al*=H=qW`>=6?^WTJD<0>l*rW`wWJRUKg{zK zs^M@j>UmDW^(HEB;_>)a!H%=v;D0*{yUbjY&Dq=xZ@t_-iQl;LiNQS0m0#9vIZP29 zgFIX6iYCIpEB)INLPecA6w#F}_EVJ!*AX*SS5Xa+Wm*05r9yqZBZSfxgnViDwj#Tn z2!lzpt_Hrp!~f7mj@&8OI@sv;_G9#KF}Z6G6beQtyo2{KPZxU=u8`|tCt1WTjHHIU zA{KVOHUIckLWz?e!)cEEqAz5<1yhm+JpS!^T)spmu~xFMcX!GMIHyWp!hT5ITeR>$ zamY$LshQuS=WQ`HJ^gWim-dDvm14g2$fdcyu@PZpeF|2Vv^*FVVt5UYf1xrq6Z}eu zS6ttNGCP}KXSy9Mwn)e0_ity+PD1kF4-fg#zpNC_oPkOjV3D!CNeq>ZjSXH#;PKJn z%Rne!{*h_o*9-GQr&E*{BwuDjB{9zg^JUE<*W+j}vv`6dMP*&I+e-MD?;nD>S=JI{ z$E@6dL&z~9Y*jxjJ+VnvLeG;v06L){lix-}uLt>kJCG{nd-R9-63s=Y(}RP9wO;1Q zztVkrN>PzjS^4@^FG2FyyLay{OrB~i2-nDMXHEb40$x3Xc5z|#nhorY<}r-?Fqk8x ziW#$$Y1>8#e|i0p;~~$+-oY`{x4^yapHvUC*-+eVW~rvnV=KdCenOa7G^ARN0dBbEBaa64d(T__97dlwYxfs^8;xE zJNlj$(Ni-Nicri4@-&F{Kh_R4&>0KbMuCAU(jd(N4 z%dhQjpgx`wRjfMJ2NRpu$;)?BJwtQel+wiVE`;79uC$QrK60H!bU@SHH@b&{FWcGL zGT*yr@t)KZ!DiB9k=1_iTE0cF(Zv{=62;Z?QqVuhtwq`PR1> zu|Dr@iy#7Pbn|>%fi6|p60JWIi5Yqta4C0|wyVSN`52CuxdZo_y(1|pIsPi8UYWsp zBIA5I77t+0D5j>6M@e6Duw3qap9$k%8S6ba#nMwMhd#<+&9?NlB-~ z;5Z;w6Y47~Hs}!Kowi0Q=M!g-cplcjKqvGef6=!B)<*P^2hZ#qx)we^YiMZD*U+Fv zSUR5r?$($^LG^));HXoV-$LgE5#dRL16|ZSeP`^tSr5UiIizB$>M?PSkj2*o&WxjW zg!}_OE>;MiEF^)+HNmkmOZwB+6q5ZO-WJCrZSUvz@lUkTAH8%{mPRzaKu#WrvQ{Xf zBauk|gWF1@|H5K{f|24OlA{pW1!!`oJMt!%Cq2jG7aBsIG-+=^q~$aAfB{0RI8EKgai8#p5f+NJ%{eP38K_Keq#l z@{hhu>wbTY|6mAQwm#-=Fa$bSgZ82um3-8*K*zd*A*_(ZJ( P|5Sjjr2~Rs;d|?U@cf2? diff --git a/dist/icons/overlay/button_B.png b/dist/icons/overlay/button_B.png index e8927addc4dd0ba42485d890925e4d0e157c437e..4a19d8176c74bf98c1be41a923e1fa765a7f53cd 100644 GIT binary patch delta 1527 zcmV;`)nI)=K7yJ9oM!k zoNZfLsn)h_+qP}#;k^7WwZ^@Z^g{?@$v4Dx#idC48VX`4!M6R6oiL{D**s3Z!VjCEu`+qRVdexUfq#fXs4X_E+ zQ*OjBR!-qxi9QA&^_f8BRe0fJQ6{dO}45EH%YibHpFSr zfTz(?-YDF`_kRc$gXXjr;30Q@nVUevZu4amk;SsCy`Tp?@I+iBGxH+_^oS|`ydpBd zk0cQEP-=W6GP7Vm=rIq)WEalje-N8M54u%8ESw|jO+j`jMt3$2?pM(Y^scg*Q<-IO z8uYNItqRVZP#*<7ZlXVv2$O3x&QMG^S%imG8%!fs7k~O>y;JRguPmQW)tan)iDg;r zocZ{aGokwv?sDzlwl9;lJi_&y%;v1kctTs*T}UZgo2%I#_lIHv}%VI+TBd($2MaR5IS}dp?`p#W?M|6Qb~QDQ1=%R41endBcW#;WIKK$p{%SYf`MG-Li6yD&G@6t zODOupGa?xBUUQ+~aN8}st#>IU_@2~U=&!b0_*H)|YW{Mky`R`_p(&weXEPVD75v6_ z3(W~7ty&@!ay)7-w5{zH8WAd<_B|p(0~<_*E_UMH_gWDu_|;748%(yfI-#KaL4Slw zOm-8Y6}cysXOH~4H-dwMMec^i^9LSq<}%qlp#t7vbg)yvrJ zb*J>4Fc~_C32K?gwV$lFT9^tw%zsAeHzk?~U2@!kJ?@7latMnNs_qMG!n>^DX6P19 z;MXibXeg_Q@Q_oW@VG^<%k0Xwm)VjHF8DLQ4ER3Z;p==pU5IZACf(jnJn{&$l(X}cp`JZjE(pbB66b#=IUn^kp~YM-r3BJdqB8Df$Q-- zPMKTg$L&S9jvDv_sw~{cmjRPq&76pICKp%RFslJtO{YIZ)dgg|64 zosvYQBwYc)8$^i4Zf)~H;?dtIrgLxICf@gos!2*<@AiJk4{v7OW=7!GB|zl=*-k5o zPgl**(6CM;y;MLK3KdrA;$b1NPm&I4v4@aQKcI9aivgGlTCm~Xvgq2W!Ai?ZN_I^T zC-g73o0_N6)G_2dUo_f9wJYSv$*iiU1Ax02t|6y?wy^c?a&rbluJfJ1cZT0_!Tw$` z96#&Jw}nev!j18W>dvLjp}Dz+E0&nPu&^+Mo0}Uy8y0dmu79QD2j}FN&&9nE0|SGn zP?o%mY^Br%k*;B1pc$glJxk=s4v!gkAW zCydE|jk0=gmPZ>5@XoIXysp45c5b2FR5deC9b2N)U~2K=9AF&5X2k>Zwx3{CX`jU+b&(Bdbg&6B^&=rHKr53oX4Z?~w-v2Q z7sWl>-9KnvQ*(#u(Ym`A-L*u7vQRClUOS|PqEfo1+Zash&?cVZS0R%gcWs&8%SHz*v zWTOm9HArXT2~uHRsc4ju$(1egdP(LSM}bYs%v?183A}M0xhyPzIxcRFE{$9V@{(~# zLSfDsm-m=HhLon;H<@S(61|4IWG`mGx z`1ZSgjF|;&VU-`Z3pkSYzb|b*R>SH|-SOFr{Hq5H#crtgxFyRJ_t4iQn=z;QZz$nN z=+izB2nNyu-Lsj{rb6pP)_USv9&w5@n3JBPt!K2qj z6tSUo59o2{C!$@|;{N<`&d_|w6ViNQcXP=|MO0$>nZZe%!JEDlz+ORfl73m=uV24D zS1NL{e1b2s=^7eFmW6E?-M%ee?LCn&KkshaNVG5;vH%R9=uWyW3FWf`r*deW7%Ua#`E@>*!NUC+f>`H0s^#(;+ zFf-Sd{oJ{8)XbQa<@c}L6>Vz;HYXl8+cTw<<>lvBih9v~#xj)xx*TTd#h)sjXexeh zmApAs@7dV*?E_1){!o{|v$5?{ElFlN#ZQ${dqF>mI7hpAn<{6A{hf7JH)sP_d<=6rfEsD%;Zn;D zyOZ^qCUJ8-aEY4dMbwf~w5u2D?CebIzD|yKrRtJXU$5yl5e|LY8NxAnIuw^Ae#i5& z=gRIralW{4x%2oP*D@;%Mph-KM5Rk=ey7KXxmCnr6#K~NXvG7%=?$l~-ORr?L1P zdx6kMihgbVhDx=w0t;d%xczk8D}Ymvmxreds69RBW64!GaEW*d_?RFmD1qW%wNn~; zh~jmx8RdP1Ou8l{;=Y^AFR3YWk(~Ld()RiL?3Er{>8PF_lO8a)K3`zg>gx z^eCq~-{VTj)^c@oYn>k~5rowqhaA_tG4VStFF&7MSXh{gBm8PKEAT361}myW5>w^y zZd~Ee@Ei_x8z>Ny*4q2iB%Wzjem!NMcL>jynh)hE z6bh;S);T@HNrGmD`=GoulG-x;=F{IB_)U}}_~aX#Eu~-iG39ge9AkdZr*MGVCM7W` zDZH_Pw1sYgF)_jffviH|rIQ7XLeCeuPI-FZyGA26PO(}_=?i$D(gNkyWfyJ*1zHe%3 z;%TcNhUJKTI(M!MEr0ec~L+)QMQXL^?8MDUqW@-;N+RP3uZ%nmE9}ewi7p)YbXH8f%;l;(pt=-8AhO+lb z9k<(bxS(nRToO(sv3l7Su6oRCGzPQrne^q=P0kS$UF_8bFktc+K8tfw)uPMZHbZc12q;F?uhZoczHKdA7!)+B1IT1AtjhyOgkq;j}v{|)0 z)I(ku7Z>m4C)hs=zU_zTs-+|9>6w9Lbz2(8Vo!p*5OrYjzrN*P4^H^#SfTJy*{(hU ztN33#2IP~pw6vYl5;k>^N7A-|2wqsPPq2XQuiF=~gu+4Rs{Q4@HwJQLXyYx%eBF~i z$66c-_>*{qS`1?2X=?|BaP99Qqh>MX@^qozNOHdF5%u{au>5F=$J6uIz1cYv|-(DAIvGx6 za*v^G^ct)zEiJPt^Kf{8Kxp;;$?f|QG1bMCoih6`^B+V-!u&oitdHv={fjA@=?D$+ zy#}2^mhb1g{Gk0`w}iw8R2CtOcn}oRlo?jIJeb{c1Sa(BYsHLNlk&{?5}P_@R#yX; zsQ#zGSJooAoB*4Yh1jC;rA-gBU`L1lYlxohE4f59jGfN;Xz+6bpbSlsW%_r+{s(Rt BTUP)8 diff --git a/dist/icons/overlay/button_X.png b/dist/icons/overlay/button_X.png index fe70fb6852e8bda84daace73b0b85fffb1281368..f50a539748f6b7f4ec7c2915025ba6a969f82115 100644 GIT binary patch delta 1743 zcmV;=1~B=6AJh$y8Gi%-0091z-mCxs2AfGlK~#9!?VJOYmE83D@BS7$RlTxT>ei_`l^m=2D6O>8N-M3j(#qNc zC*n@k5?wJCGf;#i*8Z6!iZBCX(M7eyoj5UTk7jF7?Q377K7U6ss7W>cuu@W@M8ud92ij~R4VR#>_!4O!7 z9}=1IFu8+jiS=NJtjGID?wslYdkHGQ2&h)KTAQVAzzzYULk&tv@UbsL5{#H7_BD~3 zEZj=9#3nEWY=6T0YU_w>X74A*$|$hMVV`JznG3)aQ=ENN$Tn@;t^#8~ZBBleHc!J+ zFeWTboDz~vB(N9+V`Q};44X%w28n+JO^ppcgJ;z2nW+^Fo)NH*&Es8VU&y*kAau zc@Wlu-hXH>JeQ%g36We*a_p7ljh>UEKC(*oN1#{oIaRRw1R?US(B~V%JMVk6nZFlS zf!?{6NynQm<;g;iGg;%-AW;6 z3lY`n7k95MuTP(j^jc{15Lbw_$QaJ(XrKV+C&iMXxhv@01V3g!m}F9#6MS3qvYj9KynYz z8@RWE!RahmeS`QbFA3M%5rMI9(lh!k7@U)ZWqK{oA$Te)9Iwt~ArYLfw2Yz*G{Mut zGk@zxu%i%yZX--ktoOnNP0>%V`n~Y?p9n*!-Q5T7NuVjtVzppNTqYVKalETh+zFcF zH4jFgB#fbMb2VzW0?m=&2u+(qXcOjm*VQPxf+o3;bs9G7c^+Ys&hAFXf@W!>VYC%t zmQn6TXM$$Q%_SbF<$t+^ zA=Is|MsXr&h8vj_5+-v6s{@T~)pCw=AG*7PCMe)E!OpilpH;%O7otn0n2My*>UJTI zBlwJPwTfq$kD`Xso$~RaBlo)`&iWF;`7)vewtOJm@tR`@(&W)@Nnb9-+Tpeztg|)RvH8p?|o?=|p(M z?qGRU;y|xyvi`#DPIrC3^KLE?c*tW)EcXD*yYMBb&$D8B`Ub+4KZM>zJdx1nQ@@|| ztOxZtjTOSJeyN!dEfMVJBjMPFZzcjQZtv-ViW|1y}*6RxPMtzc4vz-B;yE2wY~ z8o`*8%sce|NA-#F3QG9|UOJr0a1qYJmP%qf2_%mZV{BIBhB?88wF#*FI3 zVWE^Jh}*~Pp_M4GPsI{2CM;1WTj41IRDv;}M&*TGLPX7v!MFNHT7TgS`=mJ7jLzT7 zibkhziPy0)u4R7A7vXA3!31=b6}_?#!N6#DC^#sU#nsBe@Ug4ZtyXGvdqgt9TX_+`MrnPj{Lxy7PSoNUV-HNwzGwio8$Nw>| zdLx3tYO>VRUkeErc}OcHEC-O-OYNRG-o9P6Kxd4`G!&r;YyV6WiZBhM&>8Ql+y2~Q lH!H2Q(n>3>w9<-={si0vexpcjv5002ovPDHLkV1h$KXdwUq literal 3968 zcmYjUcQD*vwEvRDDr>D?B5IHyQKI*@n-Ed68@(=x=q-pIJz5Y!^pYs6cd@LEn*0#a zB?ub?5j9xd+uxh_$2&9Up1F6<%)R&2&v|KNa1TPmNdo`?L{|rHLXv*}F_@CHB6C=U zNdn}nrHcfUCnPuf#^>6rTh0Da$oO!h{Uo{MzI;fFBud+7PtFTmc%2?z)X5P$07 z>WjAbauWCSd6K)W!b#G;(1mLv1M{}>!@ZEG%)xGA*SYg>PP7aIgAVmT>br2egcf%D zjL&Y>W7QSaNLT>>mw3HN8Yy;duoAXq^#KPJc}^P4tBhSI)!0a6GM%i_dq$ymIAg$% zsP_pftjC}gHY_M&{@Q@3d`6Yb#jC!(8DJYA+-b=2=IhS9ui@$3&QUON!B^bQ;r|n( zl3DvT$((}&KUXAjk{sB8XAH1g0(L?X?fCA>7lpuAXp#JG6ORTJOjddzhb(XZBt*a5 zApg{I{fZ_`D8EQ9*S$1D@q;Z;6b`vDbBNule1Pr_zqX9F$j!>u3CXm0rj zQ^IN#d8QOVD35f}?HH*?Pfyn+{Shgl zX!+(g#DVSxNea(5fJ>s{<^D-9fkY&&^YT+b=<2#DQ@OXzk|N>_Dli=b@sVZx3M=P#jBxnWrDVarcGuDx`&9{}E?GX#zQBA$hRot>R68PqkS zc&2cj9=P{%z07L>rs;>l1bJ#}Yg^aT3#N@Ze zsC+_E8#QH-&8E?L{w6;2p9G(RHqB2s?S~H^qEV<&p0aFU}=|c^8B3b7rSDtjf8IQI_swJI@)7)qV^dH-K4$!f!Y9h~qo!O|TZutk&aPa4 zuhnqqrc3&kwJO)v)`}m8s2TIEJXa}@zo+b+T~t|Fx%aT_!Bb{MCYj(Pw?7M5wuXiV zt0Q=HPmk8*%uFH;8F4XOUCwP5@4n;n!N|-Ej6xw|ywv36bP9Teo_BXgapU2Wyz72Bb-TzC<@i z#e#Fv(!50@%9D0Om+)lm3mzV485ztf=exaHr{ZE_a0mV?H>YRMT6Xz(0Cw~yxwOZO zU3^iam?wyri(rXDA(2S@=4$wj^YzkK{5NkROuG%rnTYeg(6-RvVCAE&x$>$}sd$H^ zH_3h)$GIMDb8F5;am^mGYdNocM%=g)4B6YAj+VCy^d! zy>ZaqRUe9}EXoom^RQpP*-q zd$zVb0X))=hw-@3Gf5|!#lp#niKz4Q5Ub9>G%S=mWclyOFZ$7Vrl44Q2t+lB;UzTy zfj|_zBErY492`o@s*r$)xcDztCg2}YQSF;Tz=Jd=MbBf}lDPSMpfj#4Ypg%v&$p)e zqoFK`xQUIGM(ajL>g|qAagRlw&;yv-nYzzDlg@Iq2WpX2&elF^BSr4@>)3G1T|rRfv=E`_|Un zKSGJt|Le$=wY67I|IVUc5z2Cp*AT6qizx*K^^OcZJH)Hq{hb~VL~t%Pd26n%6pPKB z3p@Nh)qXggR@gF{D-W%DRP*x)WouivaqTAO!0f+6-Lx=2LQhN+?AIIc#l=NMb@fVX zmJ`RQ+Swmj(@*Jw#nshx#>OEpJ#6aOB)Se5iK(6p9AuC_@bAATm%7nBU6a$(Kl@%X z%^%6-LZGUCtN6x{6Q9{Fe-ECisVQeq&$U*08WgIVDxS+nKuFJRuGKwfTbh=JM$&a! z4A3w!VJryzNxj|rF=ufBi2NUn>TL) z5zjWK2?Jc7D=zoovyD!)lvGqv2@?Pg3eNV_*VD@t8rmN{7!*jEo_^%`__4#6>o^sH zqVF%fOAEied=~Zj{jsMG^?FG@kne7FvtST6M?K3~< zf{ILy?tNJq<;X}*dV6hgO%3FXxWmSrW0;>O2K7Q)=*xE|G7DdOw{Vf@Z;=m!J> zk*yZm!C*^4K{0RW)f3I2`9ah#ItP)+`ZP#T?gptElIp-iL1+tlH*0X9 zXj6q1mzGA&{cva~YHmgzf2(6Nj*Jcc`-a z5nYU?;hZfyHsudZ=^K74ZER#zR5?=V9$-v*T-DU3U>B&01$yW=tFm>2oTHSin^38yL`M-nL}*E4)s3KfQUC z)PQJz`+UHGc!h{|ARk}fhQK`sk2AXj)s()#HYI_*Vd+s`H8mU4Zgf2shr7(_q$|DA zWx6yj*RR!JQInJ9fV()F%lWqUgT<_tWBO#pl zr$FrOl)a#?D|!tx%p-sQ)?YjCYAoAaa-7!)>tm(IE3XY@T5jFCMW|iCYYyV^c-m5K z8Mm1eQdHHs%gr?0fA9tJQ&H#T&%9!9%_Tci#M>ERUx_gdE7wq`O0a+Q2S3fE$ zD!e^$5VpzXy*#pA0juvC^MVTTQb)_9>R0FC1JW|p;^$r+9UVEZU%zJJJH`-|91;hY z379gL#+yqROs3SpB{Swy2o@fnoE(}mqwtPa@0#p}IweEHdoTVZ2|(;HP$j<;qJ=Y= zC3p=BmskKzS_?!_sdoCi_sd&%17+>aPq(}juEP%&CFM~($#qE7g$)V3=1<4k>z4Xp z7d!@LuK{=GQ@51X$YB@0k`CndKfGr){Jx`5sx)@~3d`}lUzopD}J#%tgZ1>*H<8Ue=vNSLudsl8uivZ5KxsZ#(qxw-vK$Hi@0c=KK3j5dT4_aH6@;O#9lIAD!9TsNkEDM>LK#mn7&R?8lf+2D$rK#&;e@@f!gNGP zWyb0$oA(h+o*%-`!%^Fbz^PYd&pHCg2Xf*obTe0l_+3aIJ=a^)fAIfFNb!p5WKdMv U3YsB9nw9}wgaN!(%P#VN0Ffwzb^rhX diff --git a/dist/icons/overlay/button_Y.png b/dist/icons/overlay/button_Y.png index ca0de569df161c6492d7e42ed06f291b13a75322..435ec30d5970517add8906b4cb0f0fbc31bef447 100644 GIT binary patch delta 1497 zcmV;~1t$858sH0%8Gi%-0091z-mCxs1*b_wK~#9!?VMBmgs%$_=xzt*;G z+qTWIZ5uahdu-c8Pi8$lgWIkAQb{V^mD!xK>9>L2;zKUUa}bP=AwU7$_geRY{Q|a^geG z0;3^j;(b}ya>d$*;t7lf4KNaq*FMxrNl_Us@CRs+zeNXBvP#meh8Fk>On{B(>DIAB zavxD2o4^FwgcddTFS4e%9E-sMuuR3FMp6`?kJ1~i-#P6BMS4)oCOw{dAV8%Yf34Xf)=o%URjcwXJS2Q3G3a{ z6Vil>bs%VwYIzcxHLx7Cj8!GIO_!jnwZkO z3|m2q*($Fx^MiUVXgPVeT3pZn;2SUnd>PYI-U_CK8-L?e+x>MWd?)h5?KEVYe{dQR zid)!5$x}oy%PS5@yxIEoL^xW6)GRv;e}ds?N>52AA^?}aC~vtSblt|E zA&<-92{0^W9Pd+-t_ry23-pvcLxe^=8cH1^ z7>!=^lsrxN(oT7cmAsIMM2KTG;=`z-CvzS>1%GpRf^QL9t-QqtL`1|p+M;n*6P~80 zq%EQ36XmtXvXqF*G)>KtW576k>@$(OLUG*(eahF3L<~xf(GnGR#1lP_%jqfjo>Fsm z+kuFMyj4%M8yJuG=_z?qYj!)6#}o1BtR;$taYaiwN~0v5<@3%zK*VL9-XAU)pX-

TrA~TgaO=>0!49;8PN((_b*9wIb3<^%Y+fc zoq?jV0ceH?=qY)g;H#Q-IWivvieeaOilIuKrM!X*6z8wvWrQh)1d9$gCwivO5{tUp zHX+RMQ?Te{&>XMO(_|9MO^Qwo7F}RcRDZd|q9D&(OlsgWa^q*+wFtu0zoJI$h@Htk_lYDIr>!UT_}R`lVpdwc&(rRX1_x5cAn8bxI@Cv4nf zu_1M$f0rENgbzBUPPARrEj^o3C4agx@;~ckvs8&Ta-ycrepr$k(PgnVyo;OCA}X$P z;zfJ~n!x3?3QjjA`neM~sDw43<>X7N{yVm8U1>m5QGIQrp<^mycR9jZ&7UiFbIhge zFwC%0bB^&=1+qGpS*N+ot(Hh@0^2sTC)T1so&nDqO;4QWBo`N0Vuj{%QGc3v3lTBX zg1!1Ro#cZfiagl-Ty~gKn4I;0yoD`kZ}X$ygllaP3edGq;ghR^FAJ*qIVu&ix!NKy zdE`=Yhg00t++Q?JxLw{xG#2|hHl(y_Vhs>Cpl7a@V^eU1sBgZdrF*QUy>!K+hQ`?8 z;t_GMV|Pkw%Xcv?`e9u0wo(-zR{d5_I~xhl4cAB*m7KIj9PFMX?-Czk2!6%{%*9%4 z*v?wa#RU9>A^1Stx&0akIa#t~$&w{Y7HhH%`)G@)JHB+>00000NkvXXu0mjf5uC+4 literal 3337 zcmV+k4fgVhP)k@0^qU?z?;6edpc_*x1f?4-{8zI7xJU>= zT`pH15k0{en+;%`k+Gv^@H>pLa-YvvV`S`}7_@@s{pYbIz9m zSPUS=?0f|20PvQgD1Y#Jy+I=aT3%3($CDO`L|%-Zl}TpjGeE)swzRdiy?FBE$t!vW zvb3O1r*jr#Yzu&Io1Kd!>WJtCP1DK&kRAanDQL8L-3DNi*}3Qe5gp*1Kc&YdkL3h) zyWNiv(QD|u;7?>OGsYJAe7@Z>omxiF2@@tb+S=MyF~)uk3+1IV1Yjc({YulckPL^G zc;oW&^6pSobq}m_J!vS6u_HwEh~Mw8kK31wgyJFkefc2_9^tVdrz4Tbv_K$mA|5BE z3hH*d-2gryqHmd*hlQx;oXsA<}H(E|+o4$aQaPUf8d6}FhMkfkWfdrqfwnCJn937V3U zvKB-1Ix`-K=so~{6ty>`$#uD0GXd-|DqaFYjIp2ie7^VkGt~VJj^2UO&yM}po0Gl?lKmuqfc2D&x5+1c46 zQ&Up|GDd>Z($etgqmKdrR8>VwY3%F#*M?2DN}@x88Ko6ii?Y7gue-g!-176 zS7Pkgu|oEdNCXQPE|eAi_O`aR-vL9e%76fG?+!uB&~&cr?U z+#@rEZ1zl=x1k6Vs+m6Sm*CZy!GU>@z8`d%&Tp>P_FHQk%tsf`Z#3 zk;oeW95VfO=gO5UNJ>gVK|z6#eQIhdl9Q8hB5Bz*SemNdWLp%bo>-Rs!X3fQC?n-)2I6!Fu9J|vu7haJ3FqJ z)2C05sgqS&TB_@1Qv5j`Qxmnd7dY(aB#v#|X$&pdRX0*REYL`IiA>?3+)V6hZUz^Y4U7l?W9T75L_tSlkh!oos4 z@W2B?ww!Ym7Z)QO4h!cZk-Vq|as&;B!#|SYXP~TGw+`2?UF+WvorC);R;=i^gXC~H z@a(hC3g@tU_ikvKCTuSo&iTWDye8MwTV&_YpU11OzA9{=pP!G(lPC9S6U%sQeSJM% zdF2)1_i6@_YQrOo^FTEt(Lg>h3 zK!Tt-IXRgC?ut8|IEh3eC@wAr=Um9XuMX0ZB}Q z1$8(a#@U#4TuaWiHE5R#t|( zx;i1-8#_pfqG0LLrNSdhn>KC2g$t&vUehCyNMwA%3EJM?j&ULP zvt}VLFHh*W>gwv4MyiZ}F&4j|M3f^V8JSd8R^sE2KNhxMwrm-)va+yf(IO%HP$(4B zSh9h{FDNWJ>1e}-4Z)s(dSdvNAL@H0YF(bfgGs`6joE*49>(mX-?J2Lb`S`|i6sW+WXcf(ozDVFh9-SE;C| z5WXE}5$a`(trnxLwxZkOfDCN@-6{d3o2X@W+V(*CzBe>TD#?!NP( z?23hQ$^LBm=eqY?s-U4zXeR*Er(q2tu7<!yrL6*Rg{vAGi^hY*ahvYMKjrtZ&4 zFA`G}r5J!^3s)0}c12N2ZhTU@pkA-{9Dp}u`?a}=*Q%?lZ(43euQJXx=-j5*Mjtw& zE#Xb*BBe|}cR`vFxZh{7b!4@L= zrC$DrDnzvOiI4n#f1eBX z>b8kM7mTq50FDC4&^wk0oO1+&gYw4E#iXR9U-W0B`(!1*-+vasd~|LgZZj~5F}C2? zv14cYGnBfbb#F8@G}Mk6GiDTk`;CZ~fRy3DI5!P3P7RX@2?jhtaVI8Fh-H6!y0p z;fsJ(IA`yu%bep(*PDB&q5fd<3 zPyq0Hy{8#tKOEwhG?fmA<9anTVd!H0>`kofK%{f_9Oco13Ak>SKzc%p;=v%+4pu zr$kf^;H~J|6NWQr1?^6Letr(;d>#?a1|YVrgFKEC(L0>;H;rmm`ywF(?T*Le8BIhF zan7eO#->32rB5AzLyWQgM6@qjVq+N+PS6{1I-TjNs^$<;F6VqaV=M=qJ6WWmb9wbq z`2VWhUjw*=&J{+#22c+mNJK$ZRf9wn^!a?2Dg(5!v9YnSv9YnSv9YnSF_ZrRVS7ty TfH!AO00000NkvXXu0mjfpua&z diff --git a/dist/icons/overlay/button_press_stick.png b/dist/icons/overlay/button_press_stick.png index 6d0254d50ee535c54ae1f5576d1fbb53d3f4b488..13bbff9efba849435b0a1adf67a6fc8b771d83ca 100644 GIT binary patch literal 2477 zcmV;e2~zfnP){o^_Mx<~>RWEl3sb%I zlsf|kj9(Y<$s0@U|OcAT&z>!H15y1**jh&d5jOne_=Y0 z`6{h;yh%7>WJmp78uXV=x5o>j!tLe0j2m5_9G`eqe4f*szxN`RHdcKbYAE=M*P{6t z*%9Yu9DB~|LV2PtL!Qr+ZIeO~=Y`N!fAPYGHdcKb+HrAvQb1w{%(SP;JhfPco}HAM zg=6?StV_pRDyfZC-{#{=`j!_?iAnpKOY@j86F6$QI+?$T=Q&X%x zr0hlE01s1*%l^TF`XVRf@Al+PNr6oYaJQbUA24cBnT5qECv;G?4EcYFX$LvGu=NJ^ zF3JIZNsDdqLYQP&$~PevVYtDPAu6YFNim^;VnPyfxvn4Au|CC}tuL9(o9wT9k42(- zO-|6aILO?~8&Gz5U92(bF13wqk*IFt9hJ&$rkKppz2qEoQsdrg`ctY+1k+mvoQ6L5 z(b#)YHX5Tb!!Xii)%3qY!h!a6i%P>v=9t(to`Qf|jFYa@eU~EPNV_!PEpI13xXBn5 z{=onh)NyBs#9J0vk->&ojY}-&u&d65dc1>#L+$;RIR!_Uu2!uOn+Lw$hlLm6E{qhM_N}b4tR|Hbz%A={+|Rky=A~>GGpWv_l=G zp%zSa_#y3)RCfq3X`Fbz{zk96Nv3MvzhGKP2e{d;62a`2$w5~0>0`s|I?L;# zUXp~nO;*&8omL_*wOGj1v>#CA?abiQol&%L9xz->IGki7 z>%=ulfoGf~@;E7EoeY1Mh`EC=AcdO_I*k+if`QyfV>?({_X|opiN14=$hT>)B7bAb z$ob+5FIwCdCWUP4pc7lcOIYvxWKgK{DkH_|EvIO_OZDc^YqZ0B?9_>^z)CKSSG*i4 zqCGLi?nP=1D&9{yrlsw>PE0+f>~;wx$%u_q7cH-&(oBI5{vFtiXv? zC2}koz*WHs9*PO$GO~B#Sjoz#yP}Hm^*Ty{S4G?w_1-8lp?Mx=`!U+nq$1~3X{@TV zs=B@pT=tr1WM4+r_5Sq03b&KV0^gJwD+x^E@K;O8{$xQmuV8}BG;E()i0(V*1_}Z< zVs<8DUNz&KXoaHh-}6Fb0c-kt%uH#ZVBmb;4gcY$%<4nFtn3*ow{vf@;4R=!mzL8g z8zS8T0t$co6IXbZGD`e!u~I<7&!7I92kx4?UOf?pO02USf`3X*;(tCh(#=mJr~rr8 zqTIlp0T1{iv%XvtDbB#8Qj*}_f%Wr$2Aq8xwFc=Gm@_v6+XpAt%Fgm((Imc*L!?)h zRO%T)=eJtbtA;f#(8G6jmKu&aULKd==YlBwjEi|Wf4QD6FU||RW-2#PIAE(Rk9|!V za+JC*!N>EH_&;1n!V`QP7FWR+JQ=(Ze64Ip;em^h$nrJz%ldFlnc^mFDM!n-{5(Fz zclaB#=C;4_9X{pfx?J&Oep1Vp^2cJGa91lv z`?Rc6a6OtiKCOpmTY1u*T47Zn>Ov&=|BdT;EpAVFLWhPWah^I8a<%hHQ0$7mLxp|* zlsmAG#5QUZ@;6uz``}Le6(&QAcc;?8TS0ybFE#KYU&BZ8X0jMJMn_7oSi4v|=@lKx zjk4I>y+_Hlcrg!Y^CK@$r9-r*F1sq-?r!Kqr3QAxV)@Dfx3)XNrluWTgVsmPyKn2HDwl$^wiyq1!vEF^|=@k>c4{3z$ZqiSNK zHnJkF#vcV!_zf4!GE`5lKt{{XxE?=*Ch!X$kV7P<6x@S1l$Vp8aR%Qjui^uIjlVH- rZu=Ww%ZGRkcgbnI6P9fnLM6r=WtFyhltC3`00000NkvXXu0mjf+;P@y literal 5225 zcmbW5_di?z7sqeR*t?A_C}I<}T53c?T57aZ?G+8RcWc&)U9DZCXremoT_Z(}3K~(` zYSAI6y^G?T&%f}!kH@+1yMB0|_c`y^d7hhMZiZxI7GMSdfX&1hVM!g`sdgXCKs}r2 z?)Oj!x*!9Sn_%h@33kU)@0t9KZG!-Sz5hQ0<;t-OQXldM8{H1J@!oHy3{oMZZALr|Xvm0Dy;Lg3!MiUc6Qk5h?UOcQB^*YEgDZBD6(~uEBBeZ)(Sb zd+$RzE~T$z+W-|tv_guWWDHaCup3Eq2Uc!GcX(y{Bl(F@gJhegzs zH10WSDxX7Tg6&1Ni7E8f)&Iwar)gOG+HwkO6_H_(|oVBv34l>ueQD%-=hI4>FKV z>|`MP4V<#l*OjQi;W4Mx_Ix=fPq>*AyD_#?zW}Mcw>7Gb@r7B|s5eTU7x)ebTAP}P zJO=;x^@1+^k)u@^X~;uVlBP!-ER2%6NOyNbp#4x__6;KH0>y!PnQP8q{=?!C9%F@F z;s+XKZ8wycoGE^CIN5!}mrMQTu)P}2pPb?^hiZK^IY}!9SVP%rR%bf&pKN1E%E2)Q zz4_EwfTr>eOLWiKM`KCuSHa&UT(vRY-ZNZax^wWLFG*Q_^VLa7Nf&&4d@fzB*yeiL z6!rHN+^%`uP-Z{fv z;wtg0AbYY^W7suVh&nuf(%6QT82Kh=Tj=u#WmdQGY_DtYuh)J-kv__hRggbK_JE?w<)Db`RL$jNPGonZ*c3f|n#Ifz@4#|SehGWEl5c0I z_OtZ0>Agz8o=L1YuNd%unG+Zy`%XE%u^>Y0!`VJwzC~{0Ya1(}3;2f#Tq8!}EWIwq zHpt;unMrDEfOrQ3p0BBpn!=mldO0En#`}ZwY@@TN;Z?OxCBf_{LTgx^DT8P z@aiC$N(0L1)9D;zNnbjD6ll*~VLyjtS?p1-fVRje6x}(DTr?8T?#XX+06y&6epFj) zLWpzG`^z$09Vw!x&|}3~TDi_e6e|Pl1puWb`uQC(jhw6EH`^(No&rT$8-ozRuRUy0 z1S8oag53lWM90uhz! zh|R>vmzG{D$}{T5!@RDLfk?C?y)#OLBpI7rcz6{z`O2532tNv{vxR+P1j^0t=6TqT z^P0Du-(ZGz3#TBoSGP|9iY(4Cex$SS)Jc)SUue6B-Y$`|3L&1onDn`Y6;;9cz&%)Q zJFGX$E-xfPP2kp;V7d8{9@-IYI~9h#D+@oXE-!b%{2Ap>!fE!oQo}!Zu)BL1fk1MS z7>WNNTyIoOca+A`J8F`Fe>4P(h(C3Jy%esqd#J@j#Xb@2FkMUQA0X0hN{lQGYWA6W zZJQAi5>oZ0are=+?!7p@`K25O7%%fh_)b}2gd;RVqGCGbuE_^u2z#>h{{Fr$Xx{n! zRw&xh!pSLSdLTpaib;9SVe?;WQGUl+ep%F&Z*qc;$8AfYnD z*Mfu_v~dJBYCfmj{JFbK4inYz{ia2XWQtW_Nf?} zQ_O%c)tyhvQHGjAH5+SdU33IKKp^mU%**=vfaPmSU6UnMA~s3SIqmp4K$=p?pRYpu zQ>r&YWkq9IQkIZFs8R)Ld^|JxpH-aRvyg(awONO<@7JaPJwm5 z;8agRLBTBHRLfLrEFKdL?H53wRtdQtP8EIC8SAbU70YsS=v!@3MoXJ(Z_s(25 zXzPg3R#{GPao&B2YszBbX@yERQ97ll0__m5z62(o8hU^I%*Yrn10nttL@1w?Z9Oy8 zVLB#jHTjXaKEksI9}y-C6=Ki&&oKeQq2Y`1;ZU@$xKA*piP!V?y=$xX^-0(MAgmpH*bnOvlqj4 zHb3fRQSDjj5D~9P{&DIRP2*f|`KPv;u1`6dc^=JiXlc$k|CcJ~3R~g!+Fv_Z3GqWZ z+ycJe2h}w+7^zht2;j^bY*oK_QO6J=4L}wjFK$vOltt>B2Zzk#-f4xpQAg{5$o7jD zY1nfnFsH^^6LOQ2>JH!G2L;d`qRDc4?_K66svZHa5r@&>W&4_yBcHmma!W4>xY9eq z6468$e#LyK*Jf73eWUB3n|2+)BN=NATR3~p9qiDPfgKySK{G*tYY*Q!-%y5JE-3Uf z@UYQ8;9!Vox}j4+Ceb*5WytP(*ZM_}*txV2H4Tk0TFq}pEx77xlYw7bqmGFY zr?f45`b7y+*6C~WK_}+zzyxku`+NHG` z@H5u+58+vbvS*Ya#q#UOCMq~lfQBzjj zv9!G0&PND6-;OK7d)zLE^G?jo#pv1}v7@`YD=RBmfOCrWw~>e|Fr!yUEt{5>mLuR< zW8;q1gNZ-SGqGdWZa{7tdwaji!kUE*Pbwrp)MTka*?878HADK)H2PG7cj7z|;W@wQghLBEjNS33@#M!N+7jimc{j_BM27)BAU9 zA*1Q_Y-4vFK;!(RTkj8l9cMX7kt|X00bech5oK$XcxFE_l?h7VTpmvcwP|^8%#z7F zAp~nIr)xbjBA(@Vzhfx+g4bd}T;W04MQ>}jKSkKnC8_+0xH`BnMVzdZ5UvtJg^=zz z(s^S%_uqffXVX^azkmNOIRCTz196T9a5)gQeR5e`mF#dkfs-)@*X{3E!{rfTt#*xPUMJ3tPCW3aqU)!)y6^G+T4SPzZOsV{y!F!K z{g5ZGvPIFMx|bFk=>h6;p3recoNUhp+Hb&D^D-<74&^eR_p}c)LD68~Y(9Woh%sRL zsxAes7})hjhn{}6TDGE#jR}7HWqP`GH2bF2&__(L{af2;kaH?f*E&-Y@M}P;wIJhe zLcEeD|F{{Lv;b|*iXBv%Tins=YWZ&6<;8n-VBpqBgT2nrl)D-rjBixAy30pNdSdf8jq^rO z8NFf^nY%00tZ#lmRaF{oizpd7#ncJ>*h`9(ohRAySPUS{!O-u&y?!Sb z!b?l2OCn(hIF$NN^XQ-T~`BkS)i(`4-LShhWl1G5;-ldO5r9$0j1pMDQ;F}Q!2ytO!e2no z90+~t900MndCGBy#Zr?BLWSrJ!{Efi8KbbU13?HlV$s^!nnD$E$;x4enTT)We1=#H>IhjmN?980^Ij>KXZE})W=dukuc-FzC3fn= zKIL!Q+ppY`7uUTX1Pj}t0?U2qtbsCMyU7tO|AWRxzOPNi0^8rtbZPk=qLjmt^c1_PCkiO? zrSXr+L`id#l)m}O;MPH#I}eL=wc9_K@&oe+$F)Y8{z-^n(Fq2C{{bsCpbkT84uSYC zeq9#WLJUn?-`cFq;{Gm~Z-S)e!xS4cn%!E4UB7-~$Q#go(r>D`ih7n)X zM$@it`{ywSHVe;@_P}YSrKJm->scT%Si;aBnuh%c{lB)tgMxxs0?Sw~N<&yf-@B?z z)5{(E=P3Z0dL9o`Zao$OJWXJJo7~E*zbt|C3ouU{rpUG|y!N)V4RkzE0q*1(BjKQ8 z8gv0JO2;wyQ%Pn+We2*vb*^NoBU%mqmOXmHm@|7tZ0HsgRV@=MPp+pGE$NISujYHGmj%|Pixcx$JprfZ08celO)m2o0oyIPyvLepC4DBd5{E85F;eC|>gw8MWIc{;5e13W%T9D|B) zU)KecXxnb14qY9GWvP*jPvq2i&IrgnVtk%cRgM*PBdPGL#orVG`3KiWfG%fWmx`GW z7I|e&Ar`{PnIav$M!It1N0oCMPDRm^R{DpjTxKdEe0Hm#HJ zG_PLW=${6Fvd7zcYVl^vRRp-uvo!OjfFHGLw8(oSqoPhc zhus5{=Qie;fT*Y_o=DxfeuTJ+BUpvh@VeRnyA14mP?iD8vt4}ALE)hqZZamnqVDlk zeCegIb&->klb*x_m~UmqmED7bG8d$f0+R<59~Hf|fEva}3-%hQ&rJXI{4u#5^9>2B zY42TY*V-3L!;T}n(X7Q-tvjy$@Yv+X!@mPachyE5ns{?g^f5)D0M~ccc|zdyEwp3f zjep&L`$9kc2R3 zoV1(DTyNA(cj(jopS5t=V0kXhpNXS&A+&IeCoue@fsf#gXc8gZp`sd+&uHY8cA_Rz zqJfi%)Dw6DC%hnn0Y1)Yu=<|(*2THBkx{oSYJ+Z*i7RS=_!jtAg4I?S`6F4Opj+lQ jf;WBoEwka3z^Q0m^IzrolG7^cZ$ZGs$P7_q;Qa7^A(6KO diff --git a/dist/icons/overlay/controller_dual_joycon.png b/dist/icons/overlay/controller_dual_joycon.png index 8e8b5ad417f9498bf2d3a158fb960671bdaec021..286b8d8aabb45985d8952422b62a977e27b8eb2a 100644 GIT binary patch literal 3475 zcmZ{nc{G%5AIDK>24ReSD-4p%EiFPR*@m$zWYA6ay{tX9m|LO92=mB3gC^NZQARQ} zlumaApS1sY$HQK~oW;OLn=#-?UCjTP*zUuM^qEYx~R> zwa_EuWX)PjyPxD^8^MT^y(UJLJ@b~7!o~fE(jtx2yL@wL*#sD*IB~dFxHynRwmqEs z-p6RkLPG&pCp4v;_-XIQ59AtqDYiVi>!*ew_=_W(9qi$hu%9K=(ACMm4=WwG+Hulc z&XL7a*x3o~$6|(7`Vz0gFe^{5$~?ylSrZ9H>=7-@K9*Pxt&?BpW|yNvm)+9+i$*hD z4#W<+Qiz+E<&jO4!GtEZknsd`gjFU+9>8HVZ+58|+FR{^#;24@0wkTfm#Yq5Vx=HF zG_iqu^K>E(Kiic}>%LB6j8PsfaQA+Q8WP3dm=%vesyi4o>gDjnmo?=x11^}X3oy6zW82IpA z7Fu~@F}zmL*hu19MIV;oO@~i2Du9!(51y&>S-c*BtoCmzQB(L1Ky#5gdG<0?fdYX; zjKB2XsL%4Q%a3HBB1+xF7^MwKqB^di)VRFk*kgWIjt1@LVUOxw$FBT962|3BMd8)jKiSYak@POah_E4uMBEuHX>D?5= zpU`mz(;w*Mvd($-jjHmAnTNxiiQXfl+O`mz2=U6iZ%QnJe=M%%5_ zoY>m77QtU^p#(c4GnUn(M2~HH5=9F2UA%sG;O|18HGg0z65fwPvwGJE4^{2p3V^%i=t0i)8) zuNRb5vq7qkt`b&Q9-|;r0Ufigb30&6e;LYV;df0-eV|L{&+D|PEXCq~ELCOlGI@XiMszY20oAV+C z8#)kuFHGL-;vm%PM|i^Hck1SWe42BHqRJ>sDIUgbEj3LQs{K_bfKh9`#?@`a!kbT^ zLAih8HwfrEG7)#~27S>`!{{<5%yGxe{_SiZ^&_e)^>-t7_{9I}9QL z8Qx=3Xn-nUTkG|r!eA)oUHA}+vSn08TaFH243Sv1N`P5_2xG=V?4d%o2dCvYHKj}U zO-xU0WOOv@G)OS-Sgz>1CxhG8Z1 zadk8=>;ld0s0Y*i{UOy%<m%Q|aQivaKlv|c-RrwJ+lup|eazeB{2Q**R>0R~i z-#~p+$(?OO(!Gf9sL%vLsObCb`(EKkp_Lo#p$EC5B(Ie0)8XOeKvTSU&yn!$ z4cPq(W%y?MsREM%>R+`kd6uhIa3>F$v1T8{(3IIL3z;1`WvT_}jfbsfx79i-_}g>O z><;6hb?r7IuW)M8j zO;Z@E0Fdc4Zh^Dl|5=FsSNGyt^3)Vt#fC-X_dUcU%lm!>4?r2|G;B~yEaFZzhrvVGNrp(c_VV!P|IDg)Op zGZp$FIR@V%{LyP_zjnVXx0gf|LjDnMkKWi`rDLbH-ZeLsW_kZPq`YeG#lkPAiJal{ z1+EYJM|-g{aFTJp<#)E6v>)ZSGh*PF*zrH*JNpr^dP*Q@K=M zlD7<691p=)0B<}Wmi&&)aViPR`5LtK-C@VVh($K8CQGE#qUbo8BO=Eap0*zNVf^?; z3^Le+nEml6ivN^*F48ZfG;1RHX-SKcJL?st=!5jc5%%RWPnE4zt&p@wm81DBCBJj< z@!eaoVPqf!KbSG3!X8NP9(G$s%2mnpkEh|FpLLt!$sz}#4D>}V+mi?vWFH*zJyMrN z`DueOFrj96F`S63Sf%LQxW}`5b?p#ekI-0tlQwH;6r59J>McrpIRnesjh;p^8hIyg zZKu$j#p%C2R65^o$2zUuD8|o)EU1n;pHwCRo9R38QHz|8t(kSV_KG;44oWo~`ix%s zti5wEFFMN8i4b;RbaoCo6TXQ($Tbg#ZpM4k#@i-uopiF1gkNbeP3;@^zEWbEI{X7+ z9xiUz7_;_|A>8S`6vcAt>N!J2X^TYK6Jh$hk&l4a6A>hObqjN8;uJ$-$mKPq#XZkx zy~VB}v+$wu9p)))Ldiv==s4ZKb&RZ^HHBUb@S3AEc<>k207PbMC7He# zZ!M>E>bbAaj_&#Q!?tm!Y?cX2>T5f3aaYjP(w+$)Om^-aVR`!`K67wV&u*Dk)vBnc zYFFj&EslH1pewc_W4(27ftSVoVCmLcD(24cseCBr^2AP`*MO9QvRD7zy8sFvoTetw z;VT{&L!&Yew@z;ijf~!l2NkOS#;7ccMfv@S_1vKaYuU5sdj$K==>4wQOn0sb^~H%4 z>LBS;AN+@py%kqCD}545x@)~CNX+-G)rR8Ft8|in!txGpd={1elvn>=`blRp==iaJ z6FCFyHqm@e`Nm- zb}#G5Idn`aoaP_{mnh?3-&fBR1eEt?)24;WI{t?cWs~Mxv4*a8Gzm>dqP`}BZ>wK@ zyY0C~N}nbciJaLsA>Fyw;G+v2xK3l_%p)6N$ZBD@kAxbcJ&QK2dDG_8;K%cFY{})X zEzDYpjX6{<3xMZk0V^%9XOD9v1Zpf5qK}o(rZpI`Qws?kB4KeDk;S$FHL;lONQKr^ zF5i$Y`4gUSCO2zE-3~dj$m)Ygzt=JCC<3kH`@sy_o$jQ4x#LOP-mEy7N9rBSVlFyr zB>Kt1Tl)(fi8!c9*~$#i*(e%)yVzUrqB0~l(#xD5fjSdKwzb-KZUzTc?n$T^_0^aP zC=NyY*1@5beg&$1f$XAOIiNjA{ueM4P@Udfm>QUf`JIAZX$x^@1b5CmD>ft98baFw z7jKE-pJu3H&o{O05;NbqOS>d#c!CEE;p4iD;iri+=j*|TnYRc$_bd!8h}ee#EFKtd zTR!a);W|oq^;0q7@GD(}fQ>vUPZj%?mj4MFIm0%@6=OnAAGe0*>6?XURHS)DSXmWMK{v(| zvVn{r_tqs z-@*>^m>A*A2ZsJgT9p?FX2!XS2Fz-x8N$<|!cR&wpL;vK!{Pjm11`7}NM4#%XF2Vp z^sVYrQ*#DNpLNU$h2F-#n4ts{3HO%489)8!f^_muU+C literal 7312 zcmcJUXH*ki)V3$|DnyVfqCpU(B_JpW2pW`L6MC;oq((qGh(aiWfK&mc2^p$j=p{;3 zBs`BarAv!+l!q?xj^D5M$M^T0wPt0_tXXr;*=Nt**L^3>$lxA3ivSA%0PNaYXcGW{ z@%=rS7@#|`Ny~}Q4;}utHiij0LYZ6$&^5DfD z3Uu^y1p)&DWgd9p@Xn4tt}@!-i>)!@e2@47ePB;3_-@lC_cDrC5Tz<`U z_a~y$;XlyLAZq#licLJC__Wh#GzNik^U*$A`uEg@not>g38s|#YLJO%A zHgYL!%``-3n4E;_QMq4v%;>WJ4K|XT6V6mYa)(*4&h9_{S|Iih!)#jn=FHU8RM_Lu zPe%2i403dwgSL?#;N8D}9Sn9NVuHK&ju$Pdx4#n`$&d5L94n6%BW0r+@*Zs3m4kcx z?Gaw1aYl+)uOjoQ16(OBKyIdy+HmMn*~-r7c(Q9AQ%Ut2O<$v~N zk|iq?@rY7z(?EpZgSikix5k;fixODTKlx^vVomCYSW z)8?h7B#+^7JL{|IUcXeTMW9iY&a z)lqvz+ME4#jDyPHO)c!LEUet{*OPY*4GmQ})u!tKSCKtg9FDa@2;=c-q@0{zz^JxS ztDOW=RaNx}T#2pKi?NzMr5$t1`7BBA_VD4_G4M?^zjk>2=p_E{ds|H$Fgu2|dQX`E zOnRkyj+P)GW%d5PT4aN3E~_im9Pr{zaimU`p_YuWwo#uv!zN5Ai;tk#7K#h#D#?hx zVF$)1Cd@A_db$v09@1}(Ir4?`wrt)C(+mvUD~yYaE7!a{(4S#Wdjn3kmFOvc$_H?! z$k!P>HQ9TQTFOGQRrj*Jn&4n(_q?$`8*K>N%DcwJ1)tXUIP2z7C$+)yj}-rpSHJKlDE=21;VA5Quy~uRfuk_f* zXDbM}PTQ#TdeE9Wc?I}SG{-T)hjJ`&RSMa@xmV_qv+DnFY@CiQQi-|O2-_yb)*0H= z?P~t)_9eE?EmbgBCejl{9rHsk9OqM|i4FwgkwMXnfARq9co=2O~R|e&A7s! zX~6r@Ple*m+Sp{;EzCNR^gfp`IXW`pe_x5w8Yg@2uqC*bd<_=8FshgfwqK7gw4Teo z7GGFkLVQFM={}d+K;L#LvtRY)Ak{r|@c#1G62_zU2$9I-w^?InCS^*QFYi7nBG?GT z{K(V(Vk^ZHkv>Qr^rsxtxo%YJhdgS;Z%VM{DOi&ma4H zloLY5nV!6=IkNuOJS2ZF*!fF37<~7O?{(V!ek7Z@^WF|gQgzT;9AQuIWAeq978JtwX((UF!`J?;P;PNL)n(!BN6rcY)BCXUsii zy79}jW6gJxy%`l>SbN%DBrd>}aw>So#zWngH%oYTZ!gm+A8!Wc^i6@mX8Qflh#!qA zM5|D5=NdIQ|1d`O;^K6lON`QpAUb_1<{Fdt>Ikn&e*OBz2%fiE<#$u?Jx7+sCF-(^ zZ^&nz3U7zH`cgbUBfK(nO1@@z^u+aPu{$>KvOJqHSE|ow9Dd^@jifZ`vFOQUhFnT$ zwDj=yzEzgv3tIK-KE;Ow2k%z;N!i;y)bO=OtaDLA&)sigY)(Xt)W5Vb)17?x?j1M> zdKgOk5>ee&4j58JCUtHpSGNvsV#}GbhgF99pceoQ16ujo%DTex84NM2svVrW)YN=iyOW~UO>Rl>j>7^M{8@p|VDDm&@j=mn?BHs)U&xc~00udj1h;bu@nu!GR>sLi8tm<_kHjR(@VV=e2I zygOwXIFVioa(AgzWvHC$*m1zNOIB{X_fa z9{LUV?U#Et1FV7pDTSE8?jZH-4BA-hc;gm)6wO!Hz14)tB;Ua{ zNhp@)*bQf!wo>fQNAdWLZ=HMX*@INx8`P?elHEYs@6xukakS~-d6>54fS{IlCXpl0v_EjQeHcC!zv zc>xqv%n#GjmUp885K6v|>znCGw=M_kHOwUycMHdhNsIS*BacF7PEY2#6WVkbtZ>N? zSkb0UttJ~@ZW*0>aIVPd_L;=OpWk&D!Ie<8`P=DQB(mPN7~n0}6Z%z&5x0&9 z?0}lnVe>P9@HZWb91w_A)7DeYPxj1&V(HjN6BSfwL~+gb8tosC=tTEjYycPFm1b(**B&m|lY`XIcPj>PQNixn=o>?rOy zQ<~&sV`GK$PGqlpj=vWow*ebFVW&FSHX*XGYw9KVh*W}n7L=m1$8Hh9i2O$e=$DD_S-*@!DP88G&aHWiVN3n?!9T*S zbhBf?D9vM*G@J`sLEE4GF51jo-T-T!p!MB$izp=m9@4^_Owk+6PiC8zXVxG!;`y!S z$8#<&uG09vWgv<6=0%fdU#GwO+A7Q^qE^56pcDt=``oDmul5!daHFj%LmGdR7Z7~g z-u?~hvBjt*6Ziq^@Yp;%ruVB_G=HUAE=bu=P(OFjldFVRQ@(LyKeU84p?l65kog^U zx(c*cg6Gyz29**ch6w89ldG{8;5bC3`)%wk?wB9+BixzGPcHJU{rqXc@CNL+^^hh) zq^d;xTp;PJUrc(5*{KkZZ$CTsG&(w3*5BWMhq&_GcR+hUVjZ_^3iUUOPm!T&9RQi1 zd2xtE2BHH>H8j=N{qjmPQV+M=sAR#znhqeJF%TNub{+F~PJ?YuFLP9Zw? zjIU+%FH-2YCMy_CbwAH}kv}`Vdz?oN5(>FlR$5v*Wp$(g)ykLVb>F=b4=lcF#Kgqp zbEfpCCEa;0Q9kjU%I|z(E9~?#P}{%PwcXl~oRl=pI4djHS1D1p(!s;>tape&@|>uT zpxq1-AgGaLfW*2Z=&D z5Nq3ONq&2)|EfMDKk(e3%YAdw$?c@_l`+`)n-M2}l8|g1ES)v9WNY+-E>NF|>iq$j z#q~V{DCnSWS8j4T*j!OwUM|Ac8Qt96telAIhlQRaKNdZOgUzd!n4+V>d%|Il@HsGi z)8yVz;=ShuEAo8UDLMx%x8|Le(EewR`^d;x)xo29)Y5L2@MkOU#31tmBMT0|m>c$Z zv8sm_xX?%4Hor;rV%+NCn^#Qzwka_1)=0c!PsKZ$Af^3}=i`O=euF~s z@t?UjFOYk#&abVYfGca_GV&7KERIBs@)>nY94F!{X7f^+hnQ?^Jd?NOkO3%-rqM9w1LO1MnBC#E6|HEb=DO2 zb`^;?8=}L_G%J=j5(>)-m7ZMOzZCgFU|umdXnV=k#DPq8yw7`%mk&<#KzW15uF{}@ z@SVxKMb6I7+5gF#VRv5?03FU&f8LWs@?a8~vU}>q)kW~l)tR??r07@XQ1m%y}eu|_} zD4jsAEwr5J#iMY|kjwsofwDAPlTIC3h1v92r3Ltq_iMeVUEthZRRjI;Hcytm1Yn6CzF>K+XRHBM|^yojwnLYFM9~B=V)%+C--7y%fAUgps#f zO>1;d!otFoaO)4Ymj*(#urq@A$-8);kdUK$sb^~cifGJEZSlO3^I4qd`Be^U%#H^9 zJKO9vKP^?#&mo*NYsxaL|6bL#KuY07pK>i4t6YT)FTU9NJ~Hd)OAj6dZct@OHsgUsA+! zkX-|zm4j^WxNDYOO@DKGUH@j}NIq3aP}6vAlNs_oQZc>-OD{&i(?c7XF(JASGkUTz z0H@}^Mtm{Cw6h6XFO9T~Wj}`l?ar09^G7Gl>1TFTsyp|NS?}GuH%7akwm($l_Hx&4 zi6h2wKTU^$PGbv?TW$@QL6V3*078igj;K&xTD$_;H&2dED=VKXTMbet`1) zhd+l0>H%*SXr)T%+ry^Qi`hwH{av9=GOr|Q0;^d6I+U9YcF%Z3MDy}y?#&ykXrgb4 zl>>Ugd?XQ1Io7${RG!6S5&c~dR>Mbjf=8_^*lVUn%nU1kwTmW}O`KlCl03xC6DmG% zmdK1i3ZuH(Ef*Ctc7kRXUzg7c#K>!5%LVSCzw5M+b88{%Cw^Be$K&4@_aoAH?4vK~ z=#A5hxhFQgoiyWUYisia57~jm`j|47F$Y3wN_RPpH&@E$;`V|0`udy2{>)8z$c&)1 zYFwl-F6v*gEtb#2N9R-XriO-w)Q$!JzMy@xdK_|SIswWj@mq*^jd6}~{V@qd1Eh+* zphgbIP8EWT&l3}+x?TDi5gtAe7YNx^f>&t1t4?-n+7-IgXv_a^j3CA;@agm@0)YvA z$VlEid@P|)4(R{D!K1^>s<-|9D&qCu;YMC5dX9VAZ~P@3s3w;J!;@1<_Z6j(1Fl7= zrT4cBYQo>r#{ccCl)BLqI`Z|DEz_^Vc*CA|isA^G<_P1%lZ$ zFxX-;2tINo`W4SC%<<-z{W;E*0)ZIG$lEqeociXQ^f8}Jv5r$3*oLljrhgwvA@$X< z_NMdPlQ~b%exMmT-;CqK0bR}B3jdyg*6WD%_V&iI?m~!c0mSy?a=-zeEH~dSsOFqh z8!{ywP>VTAc|68q@*Df?{kQxk+!)C0s(LLW!yj4V#Id+#^_uu1VT7@k{c zCKI$YS6R|8blfSr^{FMAaJ~fGJ8D1u^|x~EK1$p?x@V2^#*W~Os2o-%^X03;NUUne zkB`r0#vzCH=fmFJot?$_ZbaZEV2QZm3W=o5;QYX3lv^9j9GGE_JD9$$B9ZbJN~EN}7?75}5{AC6pUBLit1QKE+zEs8p>s$;5G zg4tsT0)qb^{Nw|iA*1L$Bi8?C(V-L23R#2bkQ(V#y0QRBO%g-vf-i-4D)^!F5 z+`w-c6L~m1d`kgFJKIqU-3d)?kU5t5`}AFG|2Xptao5#agGdkeLh#pR*SO61R8a)&&h+(8Cx0k3xz$ zi(Y}zV+Qhb{-sp@st?1A*fvQf8nq7-=m3u!u8B=F(yiZgvyVbYnT=W@-w`sExKo7P z)Ah_A$6Sb}e7Sb1~be&8ZAO3|@emjJ#A@&w5U-f$46w8;bt*xT_|C0DV ze}U*a&?S$q+dEmm1NsU@0XrQfUJrc(v|0}2$tJ4uWEB(?Tmk;zKR3C=;E>YG~@;=31$N{ zWv0+G20u1; zy<;P*w=TOxXKKYUKSy~lX6vqkAQ>Px`>)oQt$YFxEBUqy#4aHM2eRd4%&6f*Z&k6*xL&m)p9KkqpXMW?h~ITLmWpB& zioxEej{8A1jhs$bk>mxTw-=cVOh#B?D(A*pAIu`W2l8pR{yo^Hb|}RcX3VVSo?NV_ zk=KL(F@Tb*#HzV)78vYRi9j^=S(8wJJx|JYs6jr9ii(P>;3_;c84~!S$H2&FPvdj& zwM4i2RaRD`1y&ZQxfo~RJIk-voo_r6(ZA?teh#AjI)j)Ma|FFx>|d~!kL(W)2{{dN zaani-@WL!o9@*$T?Of1}w-*)?YV(}$c`n@)%Yzk5xBri5N*w{rs8EAS*D2wG<%%In zX>O=IKffE3sUXCm`X;t2mI$NkR{w09X!Gga7P{Gopj0c}kKbk%K#C;tNxZPB~{ diff --git a/dist/icons/overlay/controller_dual_joycon_dark.png b/dist/icons/overlay/controller_dual_joycon_dark.png index 63e03eb4e0f1c692864c6af98aa0d349c83dc3a5..3fba546186e15b63cae4386fc53c09658db953e2 100644 GIT binary patch literal 3107 zcmb7_c~nyS7RPbG0ZCF(F*VZ?HNlRQdEPr@ zN$xy`5+}t>1SceNhPn?0e-xdb__PP}5fK%JZ|NAH5a~T5bx{TBt=kK=3_7fTdQZ$P zZ@Ra+>g#-CzVR04%&&yJ5ez>}5N$<1Em+#6_ogRX-{8uW6ghCK!dhnfQ#cwuYBo?4 zKG6H-JuB)u&WSbF7^S#71s!7T9--fQz;mtV>R3k;C8gWIdTW z1i;!l?~#oX2S~7onRINcS;Bah0!{@ky~pn?Iji&ALjg@OloDRlVVVs{J_wZ$vJSFO z%cv6-@S2rXbeVqvjVzgJmzi>t<}^Jt#lf#^*(=+}(vORFdN`Or;=vt!Bp7}$ODfl; zBubw?NauIsl;K5v*MckVolrqjh|6(zXL>g@fNMlA(+8ae!GJ&ZlI`-Rg=)~~mmqy0 zyM+pj-K}{b@7L`K0zN58)m=lXLFZXlI|*t`6)q%Ol(}QtK<7j4GRB4Y&(6$EJ=t{m zId`eig`QAimyDj$_Z%$!WZyZ#*o(=$CKRAt-W~#^q9@MPxt=|@Ceu9%bSqVYrZCow zgRcg86sf|`%m%JYq^XnU=I1|F3!c7GNhv)WxzR=i5`Vl>;S`>Ybh5FLnlde1S7CSX zi8Ong?4{KL!=4c-7L{UunWi*wqKt2YMz$o?U~==+-IVQ(H;V}sqb-DYsA`(8S{-}cHd5O zv-N4UdJk=DSu3yquAu1?IAz$~4r}G@y7_e@8=S zSzlu9f;mH$(}M_?Opa_Cq1Azm%A12dO=b*PamI|rm+48DZLY)VeLLk$H+g|YJc+w{ zcjYhEw`Vv%0@GZc)zSrqRhqTJk?W%H??&U`I+LqY`e&O|IA>nADA~R6HnM;E%6Z+; zI=Ifm07;hLaDyI&F21gIgi===quFiNC^-ZVdS-S+a&9tELt>@D<<6fvN+5Gv#_?m} z<)MHQ4qesryXig<#4SEjND${gW;}gFq`8lu46$6h6JR49V&@7&ftRc*^EQs!3pO}0 zRdAx)AJ4?Uut2(Qv5!DgGE(@OO>p55ak1uS9=xm>+$QTHh^rqn(nQ4ewx3(q8z~{` zi!?ZR1MC^*o5hGUlw*r_4I`~ML!6r4o398>7>WWc1i~P zgH(Wa+P{P_b+$Ou>TkRa+r1&has4)n8gORIq_!B-_zskeHw@HDl@FsSeMXMmq<8!+ zy2b)zH%Ie+@)mzI<*&Tp5XBb?x<(YYXr-+MR!#wcC+bocvqyQOGqXmIF^~)uac#}z z5>mm(n*K}zkGM$CLR0LjlC!)bQ?W#zDu+J8d4k}N2F=BLb*%b6KKJ(hDg^e~^Kf3x zmxHqNaVEd{b;=!ehRDRY8jQC(g1;9pY&@oB{x|5)p{9E}z{S(*!2?Av#;5=S`!K@P z5VJ1;K-4os=2mAYWikSQ_KOM=D$f^jmKeCstZVDkDb}#eg<=dzu?N?ZX$S50=0fT! zh|Y($|8F7K2(8b=N&Z!EHOJs3A^TKZATW&`2!j0HO`?v1BPJN?(DBXj*#7#|%ibbj7K znB~&(^PpaescVa6xA^d95)~kS?V_?C!5OzMf5mC5e%4w^-MtPfYwtAIVbL&;Ug^>b zDke*_&PGOisR}6~4;h4lhx+muZ zqc@FZfWF*Nn(n166ksJ;_jHOF@ox?nR6oeasnXUT8{GX6Z$uf(H_D-@UO=ZlS`Gqq z1X{h?d5p3LcPBLO$j+T`i`t!#bLYp#C*beMu^%i;1U6D>AUmBLFBAzBMT{~l8?sNi z{XBaET=+V7#&_Em)1{KZCJNJd)IdLw!I1S4ZA;^VaOfnq9tD-cPb;nle~%Lvw|q|u zx`ktdh?WCmP8Hf{!NV5|J}adCdfXb(Z5t~ugM26)dZ@>wj8gTuv@Ubh;HEa2OWI`Z zo|2JjcyBwN$r&|h3t%wPhKaV!I8>NrB?Pa%*US;F)PC-*!Jxf> z-E&eBxPvB5EA@04BA)q@SyQ+kr!{Zt!R=`lduz5Y^Y4a_9IR!VAO#IKdHnrqRG`%@ zNXQ`tpzX$GOS(G|+quljd3G$)Fh3hvMq z{4h|Cgx1NisT)=ZmDt}M2*q>QCMGNFj+X+ZIR*ofFet{CCBy$>v~D{W_ty4zp~fw` z)f zOFG$bi91?rqatF zt@7rwlDx*4x8$cFr>Uv*n zLFF1M@T|7!AEQaOtJafS><8VaxLv>j&R`B$Z^i5^o8Jm71lTy6NY6Zd^G zWoGOI4y0T~ago8$n~vhnwIwh+3H%(e^YOv{VlP)$fvO7|d=PnCMt6@VZ_K+UKqdNk z>eM4O9ru6AWxT)9CNPovbB{iIB0)K__e}y<0>RBvbyQsc(FanA{ z2&ewcmu3GNPUv^~5akwmjxoOZwtBnnH8dqMP19jNt*JFayLU+LrQ4R!zrn8L*l6q= z{~%l^tWqpUmG9dgm^fL^q_trwIGrX(+pFbdM15V7Nc20W9WK1F!7H?Xeqx)bw5c4Z zDLrzo%i>E+DMNjDmRhv#_;doEtf1krm)M~t{v+ac<`W)qa);bw5@KUf;iYIHNF{PsZ33SA%u{R6I8103{Rb6P` z7~MP9aia4(Hw0CeHr;+0ot?1AegAH94P`pX#%zY(z}Fy zR73@Y*MOlzq>7k-`t^=?%@!KKv>cnXsJ!OWqsiYOFJ?aEIGA%-I2fxFZz}*VsnuN9AtTafMjWE< zm?NOi4~^WbPU<3(r}ow-1@ix0Y;O#9Zu?2!kLz1}kx*H>@T_S+;*00%ZYvh||3bEh zn}i@~_0TnaMhe&knuAxtTJVQRLI>}#KB}O^z-~;@7Say6#$ogFXsTUnT-~mTM{!GXqrA~oAah077VZCtYi;P1a@++ z*PL?2lhW(uLyuOQR!8O6yw}o0Rrcj~YGRCFu5*1a<{TI(hA|@m@vL{pO}1pO@(eAF zvPiCE$Fa%2XiQ8>35sN#qZsA7lTWD!7WNvtKY3XW*7NiffP9RzSQQiPdB1;xxNah{ z)fS^MWwUl4Vmy`L<}?}5hT%v9EA)D_!E{hoG43@%_mx4Rs5mA=DWBVm?8H0t11$27@DEKf>O#SC61<2^3ABYyF$g=0Kt?Yd$thFCH^Ks|7QsMON; z;rECzLm%@b^I`ZXX$z=4#ZE4QbwFQfC?oO2`=A#1h)nOzPo8t8f|e(tRLC0SNeWJx z@qkV_BXu4r#%|4hvMHayrok z#2H0orV)3Ug%LsX31qwMBtlA@El(xTV+CCv<0qQ!GDfr?%1Go-;|50xlm~7mscDkw z9Py_kkzcM=SMYfKD>+qnPm(Dxj|aOWm3(1=JZm4vF>aCOEWT~n_De%CDVsmX0%us5 zo{tO5KHj{C@+>TaIN%YYT5-v|<<3qB_WH#A;nQO>(IG2}V~;!Ho?E=&5}|uoF*s4rdVM!DmBV6uu4qA>_(W{k6sH3puK9ZdTE$tqGX#0h+Tbv z7ZI0fn2PQE)A)LwZw*;}3Ui`J{R^IZ#9*9zCjz7tMU@2R;N0 z@L`X~EIQl3U@)fvH{Xa!(Ha~*-qQl*6J^`A=rox@iX(1u3c%wXZn$WSRCKwyJd?-j z82|MD*Gn96qCPehZ%pa4i^%ZY4RU`X1%1WZ>X(&dJ9_|N5;?87{H3^4Z%632}cz;}LFS={JS<=tl>12@jxzv zXK!wr*P%bHmqX7{GXHVj;a#kp%LdLjiX{Qzq-9G#eV1`|MgrL-WaHP&%UiDYV%Z$x z&zoyfcEA>e>D$^Gk_a6Vl4$7fLx5Cpj!%y6UcEqeNska%tNG13e`9mHryuc3r#=9B zN~xjfJv$l+v=0ow6(sc~7j!1ab9iy}LJ#=L%{&;rl_3vLnv&99vZ$T~Ekdp6T_J^E(R;*}c=cAd zOOp1fz$AmR`8`wjvwiy>*m+AyLRT3jEy9Z1wD^*sl}BR=RY)=4LD%w_sZCE=ed@UN z)#YU6KJ9e9sAmXQmL5RbKhv)><_0D??^ zW^B@<1YL3$cMA_sx7*Wj$j*E&>5FH*nCJY(vY#{En;V>psD|*+cFNYM@j((eczD{5 zU7f7EBAemjU<4m-zDuPibgOHQsVs+kKc*){|2*lpou)_hRB8(k>Eerz@L!rtY2k+~19$m*e`#{d#`9 zh!nGedk;u^_qGD1#6w-=T^PQyS*3%qe=?{q#(+O99GyJ{0p= z;a?=`&Z%GJD6t$jQ#p)JlPR9WbfFN4;LXW}(r=t~@}XR76+D$~Y4kDfRX?wBKkyPF zZ1L#SZWL2%;WDjK0Ea9ql^PEyl}Sq9v|^|_cU_@!k}AZUydVIOKwJb%8vOlJ9Gyw- zNQ0KG#99?)HqQ&u`$rxNE=eKk-WydnJou&nraTJpPjT`$9rGrI1Zw5eo8=d;Fz)zY zBoOn)F7^CV>tQlXENQ;9y-VCsRP*k7_b~TJoWw6Li6JR$Kvc|G^!Qmx@C4oyiQM_A z5S03PyL{lY<)}E7<5#LiT`sR({wKMm$tZ_oH^eB&<%Z^mTU**&b!qzqstn2lA6 zEw8no_)1NkTNg#>KP{EMi~QsR`jA)TBA>NLQ^PN(rqjosrC9nI#6m;fJq~>o$mNEJ zE(M!{o3O}|7LaeOb}N#PNGZmccxU3{nx0<3t1=Ry3nD>Zu&`3sC^N70`b$HJRO;YY zhMoPDlxJBFL%ACLH@P%{;^2IR zMxEmh;$yGM4Ac+zD2$|4*G&bp-Tl3=z2(~Knm88;F>#%;WZvW)=Pr2H|76>AB;`Cd zV$)G|?SpQh){s~WYzg3&TQB%SfD#md9~m3%EzQ8|ZC%4Y0e;PWxFwF*AW9LX6!ZDK z_*##gi^sX#mL_PXvW;nYc!`Q2I@n&pWGaCbHm%6Jfb; zPX(pEJbM@T#T{yGwli!!5AEQLhZ|c-D(En+Cj2zTBEx*fyw@t!>>;M8AR-|MZEW)6 zG5H9D32Vvl2nbFvlJiLTegO8WL{FL&;4v7`(SelV5e zAZIdn5^GYvBG|nF@Aq8NxE(ng!O%m5jk~?`*F|UO(^Yy9-vbI-H zldtaEmqV$HIh{oLKm^zeWLDjzYz!wkY+&sQfiyQxObmRDpN4m2jvF&Rl-@yiz6Haa z-`5;GWJb=s2Gz(#R*C6TX~N_v_F%=f!wtR%$|#vqRnax9ZB2RVLqkDxPzStz+9>t3 zC&U#hXx)s=hLuBGbNVJFKXDeoGv9CM+-u{y*;I)|XxY<_{j%#4{k*I(J4FnEjzKd} zzDn4`YoZKQ%Gl<5IrkF3G{@4p*mrX`7?){GT6$O`Tl-{pnUL0jeB9guVmVOLpQ+1vLlmuSh)@2p?H@VQ9;qshSf8WfW%;i3rfK~kz9X(AcMT^mrh^%e&1X6{ zQ`lI8Fdkp;&Z;*bg@;2H4+lTXV95IHlb`#Zy>tBT-eT!dI-ec;wNUaEfRY8|8Fh4v#D8X2tl8ZubYTKg)0}?9afF zD!Gzx5NZKKiti7_YVE+tt!`wV=`|0umouZ)%5qfVsKS#Dwvbfdu@#m6QZX*$#~%Q4 z2w@u=fi(87j9>ouqEfzMV2|045ic4yd7hqaJ>cS$x|w})>CX6>0$DY!{;U#IK$&Z$ z!=G5-XLgek6ZwEnS^aDi85Y~It>P+2_6xT{?i_$lKBXbfGFM)=u% zO$DZ4MrqsDl^z3sJSw@zk1jq9)=&(*fmwiFe0n2fs{FDcPp^E1`RAELK$#?XAy1I6 zOQX9M%pnJ7)}Jd@w$4>t)TB);v{?1`NtUcY!#&Y^^%+8_>6q~kFKwJGQ*BHiMNnyq z9~_Rg@#moyHz;N=rXVh5st(Ls{`&jD7N1=Nv(iq^xZfyhXm3|v45sCOoS~eswehd0xYvzy+S~_(EL7DY z|H;-zM6yd=OU8&BNSknDc&h?x1;4-m3|bn5XIj1#`QPa!XntD%9F^2l4g8V`y`i_5 zy>)=>RRB3jQMq@k`0}8_Be0iKVn(MRdCqI&{m|8;8X|RBn=vHAV<`2YpIFQjc;8FS zCq9uFbgSFixTobmH)}q1d`kSjWkD%cJr(cPp&^p(zS#*JT4Np#*YbQi#GBkueB}~y z`|>gOY_&J=A_<=_b32o{oho0U1ALFgOdM#30%b$%XicE*+sO(;HM48Hbf z?j<$GKD>YbLeBl{lv(J2w6gGU&DSYniO8JaJaj;=hs~5cNRLjWyd?>h*?#2kt`f_( zdoPms;N0e}&EzE}^aSQNh2n2vbehIg^ybPlU$w8VKQ+XZt&UTQ(?Au-uAasSt*Ns^ zdLZwi7S_$8)P%t|U%!BKO3`;*!?!J{4uAA{n(UZC6#t(7Wr~s|KDo8;L+#=g6c%{c z?TgUs-*F{Gcl+;KTH-oPzyG6!(79{W;~And`m8_Z!=XSY^*WyouNC(4*mJ*3DO*LR zb9P|!33RUcsc*Tpq9vHUgetasMAKQj=bX_;LRN2R5}@=pd77_5osW(x>)k|5oaubv zr4JtRUMd-olex2KLimpeFKqLKYw^?#zM@8B!SMnkZssJbDs#qX8X@Ws!4H6rdAm2w zCt5Esn)5K5td?OblNqjzofB8rG`(CcE5xxg)h4zd!ZJ+y94m^Xs7m4LI7mk-S89iD z=Z7zY3%I|t{8WEF@ln8Kl3}WDj45EuF(7sK&%b)zx1mCaaALUjJUQb%Hs-Ae;;Td} zf9;jGB|_^OkLNZD$GP2_L{&5qVajV6Hchj$7L+|4bckh8x1L8&?+emSq%t{Yb@a#X z1@y0ub8lPAL3F2v#CVN(&rCBw)nyIZ?eFyVPw`}X2B7q-=V7uv`FEZD23cAKVNa?I zv+|5NGWr`8e9c)yx};x_=XL2&Pw)Ge=L7y2%YX!plvl$n)?1th+-s#SWp8dW(tyzL zRSv1}nFczg_{G(3C1q38m(@;ys?JhX5k`ng2CAUNLuuA_@#xYaeo*7TG&^ScCLBqT^Ao1X9$f{kieygXw;i7|#P$sp`!?K;1IUP1C9){j=31kQE@ z?g1Ct0!ZEp(<|fm-Qq^oH6e&~3#{bY-^-(O{9;*Wa<;K*ND*-&KouOmk6Y$iw4i)9 z7FarrXq;!s8VVz^0YEIN;)-2es42>M#tw_0YT~Or3+IqS6|hIa+g&~M>58t9kR)I? zT-kR=SY0!1Qr)am9z2NN(6^pvvmj53nmjg==qW3M>KsS)aZYl1IdyH7hkRl$LU(3) zNc|omsuqIa?GrXmq_;$ip5|a%;}2u`#h4_~DQUzcb6oUj1qeakMNNBx927m9{p(3f zXC{4pAniQmvWSbpYH50&PY3_Xo;U9d1Mwt3VnQk4gqnICffFdnfBzd1hDJy4eKL2( zkR(JAsTQ3zU@JN*Ih0`+|8PpBJCh{;83j%??mTGD8g`!TVQ z78stM{?=d*&}~+>%s;4ts^itfwD6wW7BCDG`nbMbA5Mp}hK!1Tf0t_aH?jJBiEfiK zwBKMAsj!sqoX5>y$JdJKLomw6Falc#e1%YaOXvTGq_+Pw&)kG3{33F)uB#y!GdpzU IS?ulq0u7?|TmS$7 diff --git a/dist/icons/overlay/controller_handheld.png b/dist/icons/overlay/controller_handheld.png index deb375011c85a5f4d615c08e33fcbb97f8532944..38c38c0da4a2167ccb0ca5df842176db3a82c232 100644 GIT binary patch delta 2174 zcmXw52{e>#8y-IfnTWBBE&EpDMOi;Yc4PDtg^5>0NO72uVKgH<6CqoqNNBZhQ(jxq zs4Qb^Wb2y}Vkmq0-v0Cb=RD^*&wW4J_1xEWKj*Gc>KD2tA=o>i$t|ZokHhKG7KR@!$5`O)nUgS>U*d zjXZnBe;xmxxU#!djoU?@cXe^(-IjHk=yDg5%0CuZmi&_ty{xkRu>JP;1Y`QS@=NAB zQdbxu#T(gz5z$Oai|rseGOwja;#dnf3arRkrP;-r-^t#$AUSfEoJtidvtxQPoC-Vh zE*fN-C8ka;v{sJmRI8S7?B5wovc8rTIs?+DyVQR<^Vk8clHZ2A)Me&R?uF$+*s)N1 z)quu>i!OePHb1*Wu0Y>{X@Yi(}h}?^X9f*+FA+>jW}iHHSuvFbz~RV z$sqjZW2M{|NQ8*gv+Nqin~(dZj!Ds0QYgyyc=u3|#jKzyVbVTAu{`xS2D$F|xUO7c zP!`?g|FxQYJM9Dp`JgC`-$W_mT^2bh4PJ3Oy@7-l$GLtjHtr_+{M0W^@*c3kP;PRg zQ51Kzin;sM-s95iSs-?c&#BHUdhv(Y-)>PaPm*V;L=0gpjo{~gwzHK*VNxSjV1VzeE=svt~!@dnu6d~w`Tst1t|G< z79Sk!pmiDR$lBim^9|Jx66Vq~aloDoGV@F4O|820>uz{3c5&LL_@a`?l%Wx{s@_LD zUhA;MnPzx$$bfUTh5#5iSU{Xv2ND+nGmz)UwaKE~)u@x_n!aYgxb}{Om=Z)6)`sev z8{5kHV^}t(n8SlaiQRn1P{5QPT_GNjNFt>~04SBJT?bC(>(`z_N)d|e9%-@d0UKm^ zM)QZt$7H_H%n4lWf62aC$wUD4uoq3Abc47GyO%$m@Rqp|z(0o&bzWID zRM}d-cW`v1H0uHkhAGQ7iffYS12O3|R3c0~EKgpFykQ62`@kW#p?#3fkOFdH9zx#- zZGGr_qZvhp4RGa_H7byzC4r*web{{|T}KLrxVk|98FM_C*~=YuNOuj5rA0krka-iK zy#$$lPlv*_(Qyv5zYo(dtM?g%iT?sI^pI1Nw#ZYHGmUtPU9>kbgQ6&lqO@2Np$bDJ z8S zxG2z85G6e}FFH1T3FXxpU~8=bXHa|snWspYp8K#SIk|D@6wwT2d{}C4{q6I-MShr2 zXbBXe3yebfY3I>R?~FoJg7A@3w8MAdsN^}Q;c`QOVOptcpJZ3`oW-4V9DwQ#)G9ke zs6WUGW5>^jX!`Z!kRHL$?>DwnQ*0YYhEt;{LV&$ZIo@=!BA-g7{cIJXh)D| z*@<6{#@HfpTJ3(Z?a;;v(dIHa@+?QSk#tQge$z>Yy2BJa-9sCD_ALnhU#OQy zuB2vhS%rd9#X=XI>7HINikCUv9H}l5{6G1bmVj3-!fJbC#FLHmWQ#c>(f%fxPpXm$ zdfNDccNHyU`8~iC*4w`d#9%E}MhHPio^xe(DZz>)p9l4_j!(WOc7qsfJnK?e83l;Z z=JEYrz2wuX_o&8N4MRfe8jouFZVqQTH>`@s!F_|c>8dm;On~>EGu&M{%bHR7v#H(YcjsdWUY=XjKbAkKneBkOq3{)LlG8vh#bIya^2slg{13l638nx5 literal 4645 zcmc&&`8!na`#&gTZIKYA8cSiwOvV~5Wb9=R5t=O7#xiBBy+4sXBgPO)Lyba=Z5o3j zDr0Gc219Q`gBgiTO!%I@zkILj`wx7c>w2#9Jm;MIJkL3=`+nW8`=mM8U6K@25CZ@} z5@~ty3IGVm{q3T{;F;8Hb|!e(6?q=%Bnq}TQUCkkd{4Ngdn5ozb^YxE`5ID(!ISb) z=5A4rVW=p~b@UAYgTX)pLxUszu7}@%hM{j3bBz@MU|%`%;yI_=k2oysL$4cmdRC^$ z1N28o%cO$`{B~O}DC}3omgW8Djrj2pqNl=&Mf8u9D6OiIieD-9V&}cau9}HK!vYrT z?^~bqaUdZa#IHbo^FPrakzvUD3R=x6&jZfC|KPv;EYW6vD&>&@K3z>SJvki>P2HHQ;?GR*q3!h9*mwY_C?aGjz*c$PK^7{V$_g}So(XpuHJ)J)wFzhp; ztJOm2Srvf1vz133X}#_5Vl^qX<7^$+G2$rHD$dE zE7?31t@p-f0M7Jj7=QH&sUf1O`=q!{om(aYPV->oYS~Jf8U1#K;*A0W0=_>8LIfis3n;jB+Hh9`?>U;6!jvB&Tc7^faLW$~;Ui<)&p~tH|n@yNz(i z(%5k}6zS~EuT3S;F5?ViV`HW8Q9&-r0(AMszNNm3D}muV$>5hfO&)l$!LQR6?|h~4 zettDrWwuyC*e8_vaA1BkTN{d#=~+>tcyj%b<3*W?L}H#G%wx!38HmiJdK>HO|K^j% zQc_Zmpn1JTC7pCTQAl;D-rgjGcT_4hta{`azSlXV_ES{{J-bhNFySDwU5{LbjJ0G; zO85!Ax6swsZ=qgIwW_Fp%0$v{-T2DwOZfa97phB!VU{Q3FH$_uRyv(h@`mDbjwmU4 z5$n{Pc7|`&1G+=|Qmzj#OBxv(ruW%Rb2AJfN}n!f+M?FG(-7%W@X)b_3k+Las95KZ zTKZB3u9Vpo2k82smh;yN*u@+Zd7H?K%IsGb7O(j%(CM@v)wI}v?-_!`8Behg2;HMe zHZ+6ua*S<+vAT>HUZeO|v-GlqANItSF%n0f)-R3tG__)3VUf@nG+c#OL;f`NrOEp= z#UGdG9JPG?iGJiX`384e6*I8##@->?b`k{I0}(p$yV#;zN>VbUU@h6ATkttk7G|U- zdbeUDZd(Uq%A~iQuFE5o+(MNVtT|)atsG6Ax9<{4UWNbrcH`;Or-|bF z4B_FR(kyx-JGLhaKQM3VO>%-dU=wJP^ro`8)eT40)_HUe5&KrBb%MVAWWcHV^4+ymQN+9}BAp}_;`;GoISQ*<1wpq44M2zty`V%^%B3zZ(M8geW9->#rRFw(kqFHiT(6z-qw$| zf?wOHrI$zJN-tO2vv@u~H{#ru3rP^45h8^2(>gnpD3B>#}-G(#= z^*>uqlJ9(10+5cAz>!*R&*#9Rw)2df1tml-f!4~D15_hJWF1M)Uk2B^vDCFYk`V3!exi5xgMmX$!Sz zo<`K(bn&D>3bh@UJ@Z0MamQaS1z*Ix=WfFod?tv4N_74>m?fbpBQ|dx8~x9!;QQ|R zN_8miTZE$bH?F&;{&0Bm9{N(s`6oY847=TA%nwoc?RxrZNlS6%AJ&WD=(L6@W6Ta( z#(WV6q4@jze{&s*mgo%6CwLuWuc^)nJ;c8m5cSi56`_6)Zay5R>a50J3{e?%|Au1 zo16?)*e$FNSMne^als(IGb3$r8GOc+N+41;&p$eCmk;-35i{qh`sO`is)|-#R8&DT zLR|+ar7o*QdL9=DkQb9kDtOWX{J+76&C|cMC$(_+5e9oC(xI5^oJ^_lgxZgV#cpl>%F*^{S~?>xt10Ki5tkGS zI6#n*gYn--g?KcFvb@w~YNB|-^@ZYQs2I!5HllYC(~sMB(U1SGO!Z_QC#_yxCs94$ z%K1<&68Ej=T*sFG*T88!?raOVV6v7+las36`r6tzkUjY-TY24LcAcPwl7h$Wpt0dV zdS+N@(z8@;&IDz(`tNckUa9fs^S!+VTGTsl?(QzjTdTBC>z!`!-R`n)Q`03k%F~4& z8#0A8{mJ-1z};QBri|dZkfr(emvU<0&RY_Y^Rj?RJTaPY->MqSwT+DQbFEW zN9O?q`LiZIV&TdFEjOJ|Z9f#1LpTVro#3x8WoKvOK^UJAFR3VjN+v_aHX611fY^tV zwN8h}NQ;;SPwekmNkIwT`ts^ncKLTxm91!#nIJ8lveRU1s|v<40+A`%AS5;0#seslK;s$!=7)H3Y`LP4P_V*`7X* zj8I%ooB2JXFy7PTVx&@X1&VL4h_HV&tv|_Nv1oSt`Im&WzH&`J9=F@O%anwrOK{o| zWb4XcO?)q+YW?jGCYoZb>)iSE>(}ShGH*XcAo~3*I>&$CvnwQH&*egb7@ak&cXMpM z>8dK4SG5I7mNeqj=l$YloEXm7nb`xAv3G!OUAYd%o^GDaGrJ{Q;54Q0mJ4iShl)w6VQr1#)3UVE>9dgiCsp*4Xs zmpzHM`hE6e+Uu^0VA?0?+j{>{0Qj;c5@hU4n-HHU#1UI7oRQdr~T_>j} z!t>W|3K$F`dBi8$?*tm`lu2Pz_PweBBe^CfCaJEA4SJx<{fjDjU}W9FK@bCFritUFCfL{sP^*L; z!1@TK5;dC-7!Dr9Hdvp#GdgEcf?38k6fWK z*k;kUv;;bQgljUtQ>Rr`^zL#CzsN2l%=8aE3c$)Bps2DI*YG0WprN&+ zkm#qkH^DXEzB6&J`KLVGT%)1% z*S+L~@bFg!8RXk196lm>{&^UbEjn!~$=101>AqR#Y|JKc)7aaN7hi9plZ9BAm?hpSOkQljammzz4B%HKf63~oD`${kQrVGELN3V!bzY^l<)ZxZDB01Xnm zh0S^$z=4f2iAxI-cq~j6LK>lr#DS2&OUmpw%zi;Lk68Ev_-rWy7?gmG%M>yW?NlOQBg5}?H99O*vw;NZlu}+BZBis zjSj@C zGtiK1y?*FW;DkIq;j&D;BcoR;$O;&HV*-j&ZHST^4b4DHB5|$Gj4)}G3ozHJhJzT` z&wzwyo-8jff9Il~hrG?z3x|M8UsNJOSNCT}*NkH*y&+`0Nu&Vk_4*Qnq%|PX;fc9H z@oWpKN(ppr(8#yK7c&o5_wF$}+)@W7sJCo|7ol1d&}1ABnz%7mVfNx3p1g|5$;rsS zf|PCexdCq}W4>IlcDgP_JE`taWbOT#-$F(IUJL0fK4^Y9s*)HCF(H5W2GG6Q^VXWs zlsHGNlHju^PU3r~r0zaeW(Q}XgnhVZo<}6b^U28c@!<$z*tI7P`kz>}57){qx`hfI z^$M9_{lYM#2^BL3hCzx*Bhb7U8t+us&5V%rrSZU`1ikE;Wzrkov@TP}P@>Zu2iE5> zl@evrB^gB@C=wEq8`Vx=T_riSQSYqIar%TqK4hv={P{r*5yq^q&>i0+@ZbA60RV&O9qEf(`&=zt5`X{Q2h!Z`V$FGEr!=KLzmxvYP>&ryb>UdY<>(uyMn-31GIPHs9ZcEvxAH62m01!t z_B6_))r~Mp%N7N{mHV!*CM*6k{uo2Nb-%OMr_-TnN%R_18o8NyZi94W-motvcPPJ1 z8W}ynYl}*!!&I6iOL6#9OkCR}y5F{jFBvXd73<`D?=*~kdv{mDo2=EYoHh zM>Xe{n>*Cig}*fT-)soCydCNPu!hzuC6KXzX_tOQ{vjNV3M`P0&O5QjUHQSZG*J1ub>t7jSVJ7)#*T7i2cE$Km3`;P1pb)P3VA z+c_f^O&#G^<3Ea{4eOCandqVO_-p)Cx8v*|x@CWD?VV*S;7vi`P?SzuK(T)}d0xY< zEy*{+PC$jRq> z|8fGlGlBPWGB^Igb*hJJuucxI^bfY}ciXL`%3jDvh@sw}4ngU8Cu58oCffDTD#*{s zr>=uDj)}CI)!65PrwJw&TOF}LRM^qq1JHk7oJFIhj1_1*!DL1YFXX#t7%Gm(pr+Pe zu%H!&21!ou&trj4@VXtB(!-Ko3D_`=zP>Gj24d8G%xhrBz%N7XmpP! z9d18(nxT`np2gd{KKq$-fX$NI6S$z04qfv2KM#4Dy z1II2Lfs7w!gBr5dz+FWe;NiJDCQ<$>hO6QQ9P5GLhRqRvYQQ> z;`{z!m90Ry{&=E+AM8bGo$JxEFZ+Zp#pqP#2ieR((c>T>CctIz9TskwmzY z=MnMeU*)emfH>JE=Dz8X_(xMCyEoZVyr5)+xcPH<>tMCgalq7PZ6s# z9;t`$iS6OHF|20ES-oO-Qeij34M;-6q*q&zblL2WC@c3dM2i{r)3qMv6K>2Q7kQg=F1W%oQMInhQNqKIsz%i9r% zvRvsh06+Zg8VUJ9>KAPOR)q}gH1kg^eS$K7=09fDn)4}oW5fY-b$YKEB{uk?K*7F` zx3(^l5uXqCicG=2tY#T&vuC+)K)#G2_v}A#+`_C-ru-4Ssb14-23V8+JCba#2%j4+ z+~pkq2{#)P+w`cT*!FX2y5|}E0ax2Zt6I2j!NHn-OEx65xm)>OK*pV$*Iks7T%*gF zV!tL?aN3M>g`yy4x=U7(8tk1Bf946ru^bz%|F3NAJk$82Y5?@rMpJ{rDYpWl Gy#E75S7q@4 literal 3745 zcmc&%XIE3}5)KIgBoqr5Py(m~I2M{j4jpO2rh3#(Nhpz?(1M{TDiA%2g}Nh%RJ}w! zZkkFH5!uIvhyeuw4J}uq5l94Sp?h<_-al~P_0F1^XFVUDHEU+AnJY)UTomPZ$fHmw zMZneRC<-N|{*TGYNRZ5&i7bheW;g)HDv#~&|8zUAViFF@sj?_; zCQfu3k#c!g)NeXFd7HBpy5$_6#1^O93Ww;ci!G7Eg7RmX0^g+e(zb!ru_sBZqH_a5 zXAJTP0Q7(RRGnkvJIp(i5iP_FQ60akLygMCC}UE5`*S7~!mXolRe%Ukh=jK|plZ!7b%kDRXWahT=)|RzvQ)F5E zFka=gf_(PmNTYxl4D` zs7mRJi9MjWDBF#)m9CdaE0h{Tm0Ptuef@0)UU|-D<*7npy7Z6ixu_2#NDx?%#CqEP zDp6GpxE8M*5xkG&OYtfRFog7kQwZnKVE8YLr6wgJS^r5T8SmHIOP8fnz?#3{8(@De zQimC%oe;917&8oBDrWR~22_}HCPN3FbZ6LA!zCFnnA5rD8%@YsSa`L_q2`uKHPlKyel$F>9EFt z4Pp-_k-(87V3i=K8g{9gP!?(Nbe(vr2sr#X7{EM;mbAXrkBS*-9g)iif zJLV)&Aq*6gA9jyW=RSMLPBU|l@jmj>MML*l&aM#U7wF1t64%H$*sV7q?qKH`S^U%j zE~}ma{hH`+nZ7Xi?J@1L?bLnvATe^xjpmy%78tcfldiQMA3S>>UTO7Gi%=U++HcEH zJVxQB@`aVr_s<32hwBUz6OwiQQWjeb6xh_JLlK;z^D+j;-e?UW-j;1PHhlf#Qk-_| z%6ju)QZ=0V$hX8E_YQ_rLl9dWHT2Fc_QFDaU0+>;$$(I$%W#CNK%z8xFscEK+gAbu5fCBU7S{-{Ic{InGanRTRF?pKm}Lx<7hvS&xg#IVb9P z2#kK3aDW0ca<(D=Xbm!Ps_+UL?KOu6nziX?AX7+K6jntr@;qrnU(5&%Eag8U$lmey zuJ007P7O_rSNZ66W=OTm3VlF3jR#}LAnS^VP#))!W+*Cdr6!xC*E%5`?rG@gP z0OP=+eV1A;eBNRn`SNjk=NA<bZ9e?ns( zVCl|K7fZ;kIRUACs9v#3e^~?_7E8}GYQ=7}W*@4Ii}WXUIF%Rj=ZMaH4dexqOl*xP zL*cV7I&=ME&@r2=2kvkNVx?)(?Ze@SNxdCp7Ux{=T04a6=xE;Zrd598=ERyI+0y#P zhYA&ps`|uD8kWw0E__QX;;R>jhBe3QTz0wY+7qyfZq{Aan?Z33Zw3Yj;jd#4_s&J8 zlLWp5AT*SjPc&0i|KA|i?(T*jU6Zp*yf1^r{A$pV*WlFPPccM|9u$9M64KEDQl+0l z@PNA$IXJ(z8fFNzsYgL&Or6XR+MXP?F1yC6NW4GsHteI@viWht9Y2Ws^S7DpJTjsj zHENft#i^C6!Fzu^%skD8Q)7b|WL$z|D9K1`M(*+c$z?TGjMAFkjDU!ZjEX($%q0Q~ zv_1zsP}z$AR1epFKOTUBX-Qz9JtM96Yo&B_B@(j<|FM#@#o%Q=vG^K1`3@%adym5; z5R61mYnxHXtf%QFX9!yirZ_AY``W*dUue&o zW?>I&G-kCU!hxgZM(RX*okdXvdOF~H2pEW&Kl5Ns7M}beAv>Dp6Yif=ZX$MLE+l^k zDRKAVxJ*-f#wU{9lcp;8mvq`kfgiLo_;b^ANLF7JGQZalWjAH&V<%eCbU*qlnMN&T zR>+9=8m}&zyuP-Q)t^*N%zaF@Ibgr_y)~!ojd`2=J1s!f+TlQ_Au;#f>d(7`t|tNM zrlOIzKq_~SLAL299oh;xD#agK7o->QZ&1x{aN!=mRrHO!r}JqWesxmaB8H^W7KWYf zhqv$S+@+APc-9UlN=7{^;?sBxB2J`)3hKIaO_XYZF(-7|p~X@?GC{94UvZx#OWhz? zJcT#n-**E|(uOCBun%jj!{gU}>nvdjp|C!Jk;lms&_RRO>X)V{4S#ek4gOutNP?Kj zX0CJb%iI%~CXkYr7beDTJ?jkVldhU7;IhnU{!h^?LIp5~K52Jal*hmO$N9#EN8?uT zYoX7vo|J)v*ZJ1P)GQsBO+LVZw|p^}U_?0u>fE>76!meUx$!2Y9_g>)7Ztf|Mrg zMr?9KsxuS|;q&-gd6TN+t|SMpwU=bSTKlXG%yv8UF8NUi+1c2`#c|$fU#SjEYabpu zrN#087%IrG?VTrKN=e`#XLW6na0C7O`)aPH#x#XJu@I?w6^8;BHgkOT||S;tM>u+tBMz-qdc{}SREDB;8NB1^9%%m*=9sm|7L#?zKn0d@^Y9A;cnReN==@;ND>23I8ugO1;TZyoo7&= z+F-2<7DiUxKBWx3f&%vaneL=l*l)vsSF`x3JhrnZ&`VrG&pd)tY2vqD*l!kyFLiP5 zIy=o;M(7KQIqUQb>lAp>zPM7sQgnMCQ1&z~ErE$OD}`Zilpxzi7wF?XhbK?HUXTgS z|K>Jp%k%DlAU2h*NKXZ8M2Qc5nR|F4JjGB~kKW#7i=CN-Y>42RN-yr4M?$A4a5;JzKvGaY5I~23^VghmL zDaMh(Tl5*ftV1Q_O68*Lus>?x{%zeZ|GxtXfY?7*(}P-}57Ju{cS_Dl6yWUTRO>)F F|6eLh>Gl8s diff --git a/dist/icons/overlay/controller_pro.png b/dist/icons/overlay/controller_pro.png index 67cf86d5c46f43eedd96beacd789af20d94a07c1..78273fe57977b2932b78ce8a229a12e1e2973334 100644 GIT binary patch literal 4531 zcmai1XHZjZw+#?N4NX7@QX)Nch#(*k2sMNbqVz6BkglL8y&9_YUIGb9Q3BGDBE-Tg zRgfYgD1wSeQ3S#7yf=3k=YBu#p2^PId#|Z0WQ2r-q@<)&RaK>>rGb(X0H%}_@OK8t3JMA+ z6bfMe-KSt+deQ_iCB($UL_|ao2n0oPA_SfRAe@vZLI42$zYT3{Y-D9+DQ=W0p#7UB zDJco~033=Ng$dvj3B~QdcbzPvjDSorp{%2*{$6zALlFXe;9S5>0;Pn6#L1KbPL2$e z02AO){7*`tqx3%k^noT&Qi3A??+iQ@5c2Z!f0HR%paWb2AO|u-Dz&7eEYY)yTG&hB zHVzFhq^>hbD8-pAh#v7Hh+o2iTDxf7Y#ALO#egbcu;C-TW5ybLsb!I982?dOOBw#>Gx6tBkU+ z!K`brpBpBn*=k%EPXWUR6Ln2*<-X2n(f$&;ih{+N_`ZrEyT{oLD@nBSmqd3ft-h^f zwi`?o&OpKxvb7s@^;Q!ss(HUGJdQCavvVB;~G{#4&jn=#jqaaf+_mE!jfS& zMNxk4!ns1YryaF4`>9d3Nv1sS{!I60^cOTz;B|w{1|1kw=9|Sf}@P{$1yK#nTvY1 zk8P0n=Nn1++?TA%snIh7!g!H$GWWyJNri31kvWwU#6nH9AwCvoj#BLDRM-Cy))~OM zzL?MLe+1u5%oMkO;vhMH_&I3*9OCPPfU|M=RK6y+j_bwcL3LPskkkH{q>R`pYnGT< zEYl)&h78hM>Oz~vVz|T<>Bq6{oHmo*fV()MP?2r26rK0*XowZF#KU zSIpIhNda3DeNN}k(|4n@K5WUsajzpA-gM>Hv*3$9Nszl$vW}l}#XB5u_v>PX#B$Fn z32kXMAVbHoAc<@&-c|6)i%tFmb7=Vr zqCBf+-dMSU!q#{8C}@0JvE(C=jt#MaEqJ=_$w(~kD{-m?k1w%J@s|#GK>p>H9-8r+ zm2JpXHT^%_81mlbk{{u>cR(#kl zJCg}|fzv3-T=jEo1WYl1{ey*sXgi@^sByM=~K zW_XtOkBA_`;%2!zC9+98)W+ToB<@^TFDVt#>j#$O(w}USQP|qfOvBg;DHj<9q>7+j zGfy*3SWN?&WG*#$?E=Nsm}Xz^2aTL^}`k=^&+Zv=B$vH z@4~HyY#ciVtk2CdJEFYZEooG|_F;cxSxKRGg0I#zp;^M_wGQIG>}{jOm*) zT83TwjEuR2d4_$JyO+T$O*k1pa6&m@US~g-W5j_QY=sYQM1G7jF~^uyIWWnMx*P>YAkEVX9ZH;jCOiZE-n11~BBn1^Huem;FJ;HavRVoPGp%*<%xt7vR& zCr3C`EP&nmOMn3$J)LJh#Q3{b-KX;L2cJy&yOkFnTOMF`^S1z zY2ViV$(6(1TJaZPjtI06x$t~6NGXFk+QDL;4qa<{^NuwwXgc7RA}(T$aTPO*%rFdbihF6P*1uW=%CI;5tcU&>0Kuv3X~_DQ9Bvp34CCyl{Wqzp@?r^-+G^}lb9R2zrV2I z>nNetG1FtK*R=Xc=iUSSug?K?Dbz4)mD?}>FV z4R{GuTxO-3)z4{+`|4np^OkN)arE*Y)zvFxKjhkJ&=sG59GfdK$Td;WI1=9g=ED>i zT4_>^rFaeDVr8J%DqeA_3#LlL{k28ocY9GHqlVp3<<=&e(SlTJs8h#J_>h~!fwPxptC5>gzx7x2pS;!|YuCF`n8<%Own6Vp zh3kx)64aii_-0(9S?S4vE5A1htoWa6U1IK3G+7LMqZrPO}zn!!FWW4L?#SO?-*Y^v0rl2mF!m#lW~? z?;*3y_dZYN>0$0ZsBSRU<6Syjf=3Qp3Js6x#tCibKGb9r_Vi(jeg0TKm7{rn6tx)R z;#RTMd7laequFJN#o9v%IS*R~zM(QBCTFW1MxlGI@Ow;?Eo4$=OrX}?ik*_;m{0EB zI z?#Ydk%vR&CNBpR`)3m8gUtp1N%r8x+l^0&CwWx(Q+<$-lz^V%NJM~T1UElX*h6c$6 z5t1psmIgy8kEK0X!~%%}?xF9AC^-&QAblwg%?y5FYOR~hYwsuV!AYZ!CsJhL!1$S< zrBV2P?n literal 9493 zcmcgyi9b}|`@i-zW^CEZkbM~j$y$u;$`-P3LzcvleMw~O$u`5sT9gW*$UZ9j2n|`r zR>UC0glzfUzJJ8;zFsr)y64_|=G^Bz=Xu`m_j6Kinj4&B=4S=~;GB^m(h>luc>jGE z>A{xd%(ZmzhZe1CWX%X3(TtuL@SN$Mp*{g%A4AscH$D^a?!e-}+e0Qrg{!_14rz@SL~r-pTRu z^S>0WPzU;`>;?!!RI(Bk+$~*ov~RfZ)PM?6xse4gN;nx>Utd30bUAPK^b6d*w&OJc9pNQh);Metr9$uYPFgo;oU43R+BEMt3zdR}oH8g|+(n z`~Pg6pPw(6JeI#@U|`_C@wLW8A}+wk$7f-GlSHg?m$pWuDKq}6$JbzyEU*(H*Og7= z?~8~)>R${a6iYzu>E0UX5I^U70g(kAg9D|wu+`dWIsmC`h_qigNXGb_ zT|mXk=M1n)S3YvDpSmfXH?t5I6B7f=Hf+zs$Wz~IsEYHbFQLCD5wDR#-ZCz3d-Z}g z=Q!kOb@n;2zG?~gFl)!{w^gI(w8b^{Oi{eX9ow;+`_`yv$gnL8y->Y#4B3|aD4oz z*!nfQM0h$bB1V?y*zk%eeL-|WeZcWkpp!MmlrORaK%pw#{T&>JEru_0xD__?Fs& zlOS}kil&e3I?=0{LK?!VLM%67XgKJ7H18QMskU=gm}bB!w?`Li2B@FNpAZsCWP8-4G`tEd+I%8XI%-e>Y{<*A&pjiPum(9lM~* zsFi^+`qtq9J5j5&dqn6EZ(A8WjP`I2MWf8VEa+yr}g4)7;lGY_^@Ejks6zkG3d$xp9K zzaijQ52$5qyCA~q3S9PVoF3W#{aaI&l$7t)QG)5DFH zsw?rN?YW)IdO!)A$dF-hrbStJWT%P-JTpZrY!Q@XDnU1OHBJm!3e}m4QAH`?o`Y*u z2?zi_Gu$)&^zEl;MI2giwKv-3ag_2(BoN4~SbK3_iIP&Xs>OMWp@0 z%U#MDC{?03yfv`F*0t6iSHIzBM?WdR9?n8Ybw~nX^>eNxP}bZW_M)n)iUz+enm+WF zXah*U;$0kWT?6=qJya`TF|$1JSHToYUl~DpZRrSThC;kPEU^53Hr%C1bR! zWXjwj7R6rn3e2xgM>*0hUy8nNlHrENC@lC1^-UWi50d$Aq`a?bZa%2itn&hFllfQ? zab^ZkiN2sMV6XM5re2P@TMTF%GY$8pTTS_p>LPSaDx{vIS3`6#{t{Ku~G zsd)wECYyA&#E>E`V8-Z{YU01YdB@=;^cAXnWJ4jK%Q@p zKzG&lVq2_N#xie+=al~>GF<{K@#=$5^M@WVmu9l=QZF22G09*p%X68J5LcvM+hNE? z#hYOGyAx2yOAs84Qhn|44591WTw!BtTgtZ>Obx%l5m6TBAHUH5;5rsP-4<3? zZ<$Y&%%UZJ{n`ZJO+uSBF2E^$4h{~lc3V_&rkzQcTS-DWcL*oULk+t#$jN`f1*gO7 zu9m#v{~DaOTvgM~x3#tAQ9GA6nNU7HV}@DmTSU1<1 zG{0^Eb##v>Ym71bz#nd?7J+aK2tM3-Z@lI$pn_V7qVl&k)}mU3FC379v&>{uB_D0K z)gI5g3)IVQz+p!+@p{Uio|zPEwEBgpzEJ5F%NfwaMF5pSWh6#KoJ7%ZtQVO0H6FH) zHoRWKkaK|fG!FGHhulO~=q1qMZkvkcd!3$#>hJpK0kN9GF7p!;l>yZ*elXV+Vo0-n z*@AcDH2nxgn_syFh+mD!YO9TUV7bl(=%2z;QaKs!dHDJHb#qhule8aVw4W)8J6Vh8 zTru-|s4hVBOKEtM?mBV_0VD~U z_e^74Q7q}0x1GtM61>DAV!@S6iuMTQdZ-H4xN=|CrSoJMxchaYN-_>iFQcQQG))1e z*lm+11IukxAk2CL(UW0D_{FgM6+Il_CQj3#bDmyqsXbOre9P}?e@g`#K>KSKK65_X z4Tz)vB^m!*v6D5H3(>`R)r4v@W99nr0@~@a%3e`TO%02|_3NKMg*!bfyCLTtoW*%! zEcH&VXjt0}nT8=t0-Jzy^3Fh9aW6;8r-sj;KX<6}6LGHQ2Qg>OturhM7*z%5_CIgU zic~jQ8cQENdh`?w!%cu46r}^>iozWlFC5hUTw9BXY-K4dEX>U6alg9(j*mZtWz5&W&vN7?K28p}4}h7Oj`d)IjZqvjH+&$kC>p9yr0` zx)>LOfi5b@AUOxK<=EgkU_vpI2Ob+&MiSS4-=>x2F29zKndcR20q{)$Bzn+3e8^|s zmOD4I!N(Vf5HBp_agW58mw1HCBl{1_=v=X`@*7`s=`xY@j$r2OuZ*7Nb{*_G43L8n~ zmdT$BTZqxOH64HAT3A%%LOzaNo@l}NG%l$rq_?^ZHSlnbHA>bKIk1Ngz?P-4m-nAe z>U+}hBe0{TsATlPKKsahi`wvmJau)?m48XkRWV>WQ((;A(^tGh=GN;sX)#t3IRniI zN>=K*-h!{j$kqd}$(7LVB z@^1r|Q;s!NDnQU~X~Nvl?vL ztO3lW8;V(+CgqME+?GSbkzDKSapGW(??!e3SCQGn$IA~fyR~v32zqY4O6Mj+=Ps;9 z)myGx0>sP@)bGptC%2z_AmPdNFJDSK6+@2@4ibrIK@d8?pj#TEtS`M#J z|GY8N;5C%P8|U7BGZvOD`P(l0p=9OO=)_l6)s5)CeFvutcYLbM^aOe&u#Ux~1Z zZ&_I(He(-2{^<;Sy{*Kh#>Z))$P`^RnliVhmf0xuO&Cf^{M?H@+~zN=c6sM(Gj`mO zY7XBCkCv+_h113>3iTOqmFEr$<+tM7Ug27>4$mIIoHc}W5>*dGVKvRKjirQo7OO!I z?a}Y65P`_)Ra0AshJ^6SRF>s3pOld_^S3GL$BO9goLuDF*Q1l4$HjoT0Q0JMToMRB zsh(<&yYI-f9|=_}E~7-EhyPIL_VK%_BYC|*#^9iTp5v|>rAQe(Iyyp@*Oh{{&SR`+ zl|}f~)V$99@}*?^Sf5=u)fg@$B%}yq-kcg}fmnQu{@KP_>`uBrZ_*0_aZ9d!+0n#s zShkPC_dSM-6}IcXe)JMkO*NGI#L0D^M2xfbPbXaqrYaCeQ_h~Na*_`EZGR^fi3E++EK~Wn zm%s$@#G?VSwgkBrOpTLwr{0v8aaV&T{**w+5<5cBud4$6b<3$U`Vg0-E&o^s7c8ON zc|L`#k-6k(P2NZ=Ue=1R)DX`+y=h#1Lx1iL6OW&~48b2dCV@TlP1gwv(f%!Q9#Crp zEjpUaP+e8E(q?h#Jm9(#N<%|q+uYpDU=GRmdiSR@4)Hr~v{dup5<(`NHmA*u_YXaQ)&%wOiF6OR{|&6`($*CjwQf@it;*_OteVZ@FhYK>V?dZ^&Us#9}hQ_cZO^llTSg9$o zFqYeoIpc+?b3=t@dI{SU&A=FuIytrOsiqELe3uzT&dTT!)qfkUcO@sz2cR7^(*k) zig+AH^-GwnGiq!Np#FBQM(9sE7=d|rs_E1LOR^DSgch?dmY3DZgjkttA9yk^+0R=i z;6_;&Q~gz=k^)E_fF&SI^Ggug1Ii=Uk$b#47fndtY$}wbD&G#5LdNJ(@@ceBbEV1U zD{xL$c!{HFrhAod|0xv!M_`OEDO=F*tE#GMlhrcwdokp@D9#O$Zbh`8NFB?`)?o3q z4uQiP-}5aK6jL!J?kiiGby`nwHE%pP9{g^peHD=7b+^%xAEo-7KF=P^;9tu65DdcI za5N&J29h@;*_*pnUS9t7B4VfUrwRwkM}}|?C@wDcXbNDbv3uD#_xMxT_WWFWJO%y$ z@qGK(FQJouL;@F)h7p!>#clt+nE*}ta=YhEZEY<*o})P6k&zVlpNHi6~5ye8C3xxstIki+y^j+W%p3(p{k7sCAf>Q1&E#?_YUd?DsZ ztn$>u-OJ*Yw-`2&jAlnvCv2b^*KQ{Dj&yJu18Ph09_Ub%GkqENx0tdH8hoAdTRmXD zs=Qe85{X1wy2k8IXAEBpiBy5!dG%EuSrTi2EV!(ydYABA-O0*|xC2JbRre?I3<)Ez`F$wWywML3?w?w@bCqk3=5W<>!UTxsjTRdV70?sqdxss=>IG8EB2*shGjd z5{q0u*iqPIXx;c+?MCYe$U2Itu6l2-6hOwBJe3Zor`b{iL0Yd3!V$W%G|bzQ?><*q zs7=dOW86Rk8P(hQJ6scCf>T0ERy#EM{rpL>67d^)%+*{@8?9SikES1Z+3z98nNHih z8-c&Hix#N)@?1+ov{?K*$*zIfmhsi7dF%OChV^!)ce9Y#zfa7cD=@Zkj!=&LNu9KK zmBOM-Dz$U?JIKV~2uwYsh4ieQ!*sscQ0dH45XS6S_Tuqmt_-a8?G$t3A(*gkgKZ>a zI!nVh`F?F|u+;YXq?A^r!`(c~2d^6XmUc2cJUk{Z;_sE;|4&%bp_1yS1+u_cV`C9B!vn*a&i4~m3o}Js2q&r_Lgol+_9WfE z5YGwQYUa?~YTep7bi_`|S%SzR*f1D3>M%HPNur-T_%_C*K)z$*Y8UIpT+zEL50=2- zTn^r9n*fH42=jCfLIb!{j6o4+g;4Tt%`@6j$lMv?xLKVhoxMu~KYYD}4Lo=Djv9 zxyWu7=bJi{`e<=%v;;a)GSO1H?v%@|KhL56_>NCdnW;EpHQ>vCOxMN3^|9D#)u~fo zuGl|i{T{;NrAE$t-57X>;0p7Ht`9f6@^noSvd!)@4EixVb^8HXO3WMZ{L(&O1b`yp_G}A<6-cAR zWNaTBaE^INJ{>SYb&gTmn+>L)2awDF&HfWD&b_P90iwEG{Uwti*SKVybh0tOsJt96AiEHqDq=CeYmSbXC&WKQCP??fWP3~ z>iX3|>`JuL1r(cR#jlty|LY?9md4d*=`RsO4bF+X8aXe-hVw*GW^kk!({*G5ZUju_ zT5bo#FJBs1@mV+V)m7hJ(LXRP*Wuy?ke(BY6RC;x(YGCBYWV}6v5r(p%_c_QnCN#g z@t9lO@uj!(D^J%>!NNf}Na-ek!F5ft5`5#LXYc6xBrje@rANHV zP>cgHzed2i=2|{+O*EoieA>BvFw=A@0xBu>u8Hza(})hnuiaL1pG}uBB7W{x zR#6dRT6{D50jg{LguBIN^`U8z>-U}a>CVbl#@v=uhN&Vc4UdOW&xLwSqte7UJ&zPA zIFKbq^;1TF%{H2xPZG$3L@rlZZxtWy;W!AsxW$P~>|saMLkLWaxRr$4=Sjivf6UNl zv~c6EI{t1duqoEfD3X9x^%l$pxw1Md{w?BzE~DZ@QFJAo?cU9VY?7!5QslaU0eh=i z;TDe(M>+)dW{qBgnBiJg{2Ho@nKmuZ@T3(@9QE3wT{`X*;{|U=b?N(ph#OWBmnttT zf~2bs3=Gt9rEF0#p*%gG2``0OBtc*=)}7k!7sC%(tOB!__YD~>-k6G`ud-$T>kGAR z{Dq3?5$;Dj*qtIu7}VK}#(H6iFW?MZcBPSNViS`|kRGgd_wwh`E1J9a5+QlZc{y{EM}Cq~uE~SxaM~ zK7tvLd0FTt?ghi>Im3j3^E6Y2$jh8#byn5mY(%OQvdZPlb-!cI{DCv-MVqQ6DI4SJ zqm(X_1RSVXao|_AMmCg$(%>=&-Mch^&KucR`ciu~DD9yTN6--uRymB|2m#j>7R`}k z+}c&-+Ca==`%J^@GXvxT6$GpjFgNQIm^xS0macp_T(2{(E&%Zt8cchUAOOWLnDtre zvYEm`;l&2XRN*ndvqp2zS2}N9MF=Cwp1Gt(XlKJWR(VK4`)+93)BvvA^BtksAOEdK zJiCCmuiY3_NM-y)Y1ghC_k(x7R9mnC#dbmVWe!tWo>vHd^ETEgD3p(`SvD|l@v%s64fGW^j)eey z#hMNArzNX%{_2bt_JVzf+VMJtnIFqmIav`G0#tcC8bB9323~BN))Rc9r$Y zBF`P?tp{!^0OhDrwAspm>`yKvhxm z&oM8)a1!P~4-qC_V^qX#w1y$j`e)8c6lZ;;h8Y8gIO^})HQq!l8^SikLK=3I?7kua zHdicwGSQp0i-}nCu&@I?U`t8Jzz%!JM7PL9e2}Ow?88-QF0~gyR7?cd%rjVp@tpm8 zFr_hoCj8`YSv_6~_$vz;@M6f@gF*jwQSmAZrQbEKR={Om!$gC1LbEl0kf4imL|CsockZRso>GSy#SZN8Z-N%HZh3&fe;6j+<=EY4Zh!cYewP$R!7=v zc^HUg09R1$GnY%6-wrkUf?~lb5Mln-x)lR#T^35F-$EMAq(E7t7rN#gH6tMwmC@9g zh*X@+x3E99oN*R8o#8nTu7oQ3j-8?ABlH}m(>u!l1PXE9e3jGhxW zsD5TDSA6%94v~0|>s$fU`eZQu*`#Yngq~!VY*qt(38USay5)3dnIl9MgsC~{r zyNDwXcQv=^m+$xFOT!|;Qa67GfDrrFod_y6?!6ngmy2pq$0+ET$oLepE^vqVfRKnA zInWFYZ{6-eMOwtg#RYGQIe9 z?l~O@AY#xnP(mVD?@Y6Zd_Fq29x)#VIu|n6ak7IWr)jZ(8n*jo?f0cdCxl>Xjf#hwQFiQ_A+p&goKdFAzgJMJ|r_FjonS12jJ9nzV$ml`~7HM|eX9_(<x0pYKelzt!8vwmdm@;y&PZ6iHIkq!L9PcALiaTznqD zW7dkI#aj{DZKQs{DOw*temn?V=Dkb} zYxF=A0ugx2cy3AUb7@&nDQ=4vQ3IxjmtfHD0n39Q|8aVY-%^Qc*!J^D|G!&vAi4yF aGluAqkh6)lJK5kCCcsGF99gUDn(%+W&hcab diff --git a/dist/icons/overlay/controller_pro_dark.png b/dist/icons/overlay/controller_pro_dark.png index 7be655b961eba94566216874cf4b14c633c1e084..8d261f1f74ed96b04b4dfd20c0d82b848efe8eab 100644 GIT binary patch literal 4531 zcmeH~_ct33_s4^X8LKE!BenNRDT)v?VpFZyt0JX#i&Dhan6>u^iPmU^+G><^_|z;l zs)`a-RijmkZ$IaJe}Ddf=XsxV&wbx}?oY3C&pqd+T3Hx_nIX&o003-iVqgOR(D3~m zjC6lHEmr(}008Z=70S-=Z+ZSd{g1$Z9|6tulhVK2e->(EVG96rGO{vBo8F4fs(JqU z)6TE+PCp)?LBaf=ADgp-jd`(HJ2h?wb)W9_lQvqunBBKkp+?tpU;X~zPj{8DT&qZ< znS7cpepy8qbtC*M}Wz&}ezTa&}>#K^nORtQxV*Ee6vGd>?oY(Kq;nO26_UikUm zK4edpayF+?ahvyLCRYnRa~)g>6nKX^`aLP#+Cfr|quE=_7&jFlY{7r{%u-<^!o$(z zvH6P`sfCBV_3mCPi7prA!$i8CtPSOh8unbN94|c%E_sI+GD|CleM=oFeDDZ)ZS`x8 zr+%DvUp$)|`##!+dN07zOpcuNO3CBeBTK)G7P-PWYJ`=-J(y-+mGI z7YiV^@|1Lu^soDWb@X-vG9gjjfEd4)NpFqrP)xcO>r2Abo6KL)i`Ub6Wdm@CiLimC z-#Uv+OAf!AySuv`BvoH6dgYM4zP$SWBl2Bt=bfG2J3CkJCnP3|Ru~!#24^cN%Ai1i zvkXR-6RzK)65G1{w6ka2g88fFPX0VH*Sng6c^U%k6X0*Hnp*rB-A>D4z1F_)y<@)#d3QPF zd#_<`2|2*F(|;`d+j)p*KAWA%+hf$|&vU9>Ysk7nW4DCla@6lq+mTj|^THU%6idGEK$hn##%tQC(E1_PLbVV+gjGdo;gxhK zm9y~Co)p4n)TA&3jF?_h+E7j+>f_x;=rUF9+F;6O$V0xtE?B&^rBban6Rtd1m8=a$ zl&9WGF^lE}C4fCC>1Fs>(-6K=&4UuH=9$+0i*mJWr(!@qPFzm+dk-(PP3gQ36mC{9 zfqdh@THL#LW)CC0-byauy(Nb5c)O94myhW60V&vYb$=ByPz*?}p zG~Ml}0DX%e4QSW`5+bI_49DLHd7yzBp+l~RkLgG<5abCF13Xx;l=Vj_kZ8d3)zKdW zV{Ib*x|q5QdFt|#2%@8bkadOROSbGbGf*jQGjoJ!rGud3*Oml19q%TW6?&nUb>LQLM? z43TVm;>s%g{9-+rT*9nq>^!}C`JmGPTyRStN)YyULvnkSN0oQ=xJ^i)F~n3VQh*o% z)Ts4Gklv+_i?72H+mTjRLbUgbPLc{-6rz7$TIXthVvt0#T!Qn@KD~J^aD-eMYPNYv zC5^8_ncLFig>f#PF^=y&q*df&w6lYIAO9>3_vK&jYS+&}oI|p}sMG7n zCEo^|Iq@Mxw?MaOs!87$QM&tS4NzyEOZO1n!+&Ja~_*d9? z=_Kq_wZqmljm~+Il*5~)Kf~|yH)v@KFe{E5%loDiDD;etEeeH`SsDh4ro!+QwT8#+ zgs=ln7IVZRw`sDK#@F}=h)Mx<-C9b#gV-SU$Y>(D`x@D)mV!x%4sccqgW5iFQG>sf zY0#u0D6+m4?XR^GWz@dytn`?s-}MGP(#HMNY;CkHJ#A2#`CHPs=GS-uxqilMX0$NW1!0ncZWp3RF9wA2;%M%)_1i=AMB4l1bLs7nSo00;Z;P<9>lRy!;s^daU3LroFMKNvoMN1#6IvWl27$` zDl0~LduhBt&FYBd=w8XO{q&S%#admGPPz`FotEHAke|lsdv9GgntkTO)rQBMu4T?d z$?KYQA2BoM5f_+_)F0jit~TT4vB1O%;)4EB9@nT7FVL0{+ywiAGq*xoR=YGw{g}hv zR1vt$t!@(WSj%o0t$h3uC7Y{3Q^ZjCi&;;4(0nN-N%xjK*vBb`A(xLn(Rx~u|BPHy zIEGs(`x3c$Csvp$xGjhpZN@5g)+|n3+34;jHOSsK-5|QI@DZ^VS`7OZ14fjm9S2yu zY#4`5xsS+C_|l0QTb-reJyT3jF><4B@J*!<>#zb5i|Ka*MpuuP*pO;xXUCe}#iHW7 zxWd5_0i1T*0mcNxOx9yK9mq%9PW&Mmt8}Z<&m>3&|HMV8h}YHBE7s4%YAQ_)TqpVG zNTOWkvu6L9*M2|MNHA-jey@%?cBX6va-PwSI~z$0;d9U)xb{9>_MU7PXj zNS1VPLz^)ZsIm`uer9ae7FUdeVU6iGRq@A%$+6Sr*XL}B6Z>D`%yJwe5|i^OFI4k`+J3J_i)>o>0mJ$RdhLJh14wSFeKUSQ-7oO0ax z_34|eW0Sa-P_4D;*vj%DdA@O1EjC|(p~x8~2Z<51te*KYxWID)8dCo>x(y;PhV8b0 z92qDrXS2sQOh)FdG}hp8?xXxxoFQrNS7q)M!L=BM28H$9gT23`?q!|Kd>k0>)u8`a zcb=z+zrX4)#2OK3Eq3koSdeNaYpj#?0t2GXBI2c|(HvMS24td5z8WIF1c31BhuRAMr6+r*o#7*@zOV#il@N`z!5aAx zjgN7oHE-89h9+Z{iM9mqoc(wKW8b; z&e>iE!{)V3{p+(|=gA2t-ToH>?agn5Txbj$8FZ#dtGdytR$~%v@_XGEu-@($x^-$J zmZ3xJu*Or>7QA+^yk@Gzl3}abwz>t%l8A*Dn4cDOAlwnsGyS z>((;$dt5cY0T4jLFPyy)fbEO~&fS=+f$c>9F!|+~FlHkMmM#Jq&_gN2B zFq*C;3YX+O=nc6d0aBawrNbjf4q<*ux0~hgT|)J~<6y4a?~Z~33#>Ci+6f|ok3en1 z;*eHhy%jz@Chi1-(f}r=!F&{T>Y(oX6ryj?ANE-Sh%fOSw#@pB zd9lC<@x;J;fGDp|8Bi%cMN}C$Jgx^XvX}Q%heOmG!xI1cxltNd%fc9ZDbCHKa<}U- z4IDyuz!r}}gNV6LTL-_xvm&PEYMjQvhwjivEK{u%a#mcR?!(Ic(vrAMPv40S+4yl9 zkO%Lx9tYfUr`l!4Q)+e04`Go7u`LH?J*xQDbAAmF!KT?`g%O!6OmGoaFl@R@nso${ zoTtBf-%B{G)XC#~QhS$ZnP)KN@?@+a*>_d~zW z>@r7u^%vK)fcZGDEq3t-{IXCE>I#)HsOpcR<|Ve|p_Ra^{~@iqo}pf7Y?oC9{rg&( M8d?}M>AS`MAI6*SVgLXD literal 7488 zcmcIpi8qx0_n$G=$k-`{8e8^#$r6=)U!tNI$s<|IGWKB2K1#y3J71A!2v z$3rg=5{Xpw3GfZ}^a%1&41DNaMA1770*UmQ8(qE@Uc5<+K;Lr0jO=X9-@I|H!U2Y z9*^nE(-x7z5wA0w6cJuezYKv$qQMI>{D@q#uQ&qU8n*w+W`8Liw}F$zjh!xxU_*od zp6wpOmn!K7_>^IvFz_Q??n)2TG87l`+vwkG2pmZfpCuSS-e}V?7q(W zDB}u64y?-%$k++)oH*9KxcefXVFvV_;d&8a_x+}2(L#(q=>qBA*Wt`btVql~uoNN4Twoxy?VGlhsGj%~UVc}8B?KJEY`0qJpmwP~l!;m8i zyTvI2ef{Kq9E&LU7-Yo~ASD57**-K6@k)GvgzSSBPm`fm;KLl|y~2w)CR`7&Z+D{A z9(A%qm_dQ#+#YYQl^TYh=54EK%sz_IKyb7W+a5E$^D=PteE(`jM5@^-;n`f=%_Mo| zHLX7c2gJR^k}`d7?pCfF40uRBZzLmEfGz2jYktqw{^Pbg_pQ00-Vb+3$z?6`AqA(cvO=%Z7;`5`BH-vu<$Sn<_pgL9@QEjYtmdq)os$wmmT~+Q4w8_|f~JKe z2`2={TmIC!Y=@=)cwdM#M4Iaz%&3#Y+<0aY7D8*z@IK-|5xbcBS;+Z10@*_%=u^>coJj*Es-g3F*fPe2tegy1l+A!WZj;h)x`8m6 ze5sH2+Z^m-OX`NVRe0gjfv|uL!@?i$>2`S&EEDt{N8k<+ca}?Dz_a`I`A*6eT zvVM(*0=$W)5!8)QFo(T2glRCZP#PEt%D=&LW(IK3g}%zgDHyL+r~k1a&aKXqyRHt( z;BE~YGKA$K;!hYjtzam#iI_p+6HA!Un=GNE2h^DOpX-e%X@~1AAZ!r?|Go~Z=o#XQDcct80%J3Rn9&=vqZL)%Re-w4ejZ*`iwEG)kP_r zKK}-H$PYDN$S@}+VK~X9s?6Dn)C;XQ>z(>XBb6z~NSv_4ZTBiuKaS!b6i6%(p z$Sh0m8IP#0OM!oZn#2NY2MqR4yL?Vvm2!ss@&3_$iqw_o5~~ zIKj8g!1i467L;{81~!WXm{Lu$T&(`W%ZD*Af2JW6bg+hw*LLV!B!wmpoe_ElZ8dO`Dj;nKOC$YuKZp!4HI{wA1xH)|P z0wuS-6z`AI+_`dpUL18g`$gt(9tIHxIlkdcyS>4q78NH44WVj*kl@Pk(=t6wI00aP z+6;=nG+pgTIpflv;Q#2$%?DH@=*?qZ2M;WU;P0h=Yt3Tut)(Bo#pZ`Jle(B@pMo~N`>L%2Os5%G=6b&9@t$O>q_}7k?q9}F-U;1Qr@ih0m>>W=F zml{Xs#E2;K&9HWRTvhQ$!t+c+9jnE-+|d_C+l_Bgr<7UB|v`0`>i z*{GDNk^d(!rz`bh&fORC!nLOKpgP5O4Fz- zy5T0+(`VzN{6_%kele`l>o;PN!3f%6>UX+We=@!Z8^|2pQ1~=Qjbuvc-w%S09Z!4^ zC!ffS=Vw0wHmm$0FG4uR%#=$O{M{{P%_Ct!Y#w|k%p{dTc$ZBO$2KoA@W2PN>4V|q zpr!m&bp=fkV+u?R@SRn+PDOWr)0W~vS`J?3o_}=yFiPuiIuHAiaZw0MWLV?M8U2`0 zbXGxwC5RfD(8VAIT4a)_^U%RX4^kH9S!r&X=Y~F;pL99}BhGp#P4ePgP<>`BkQLcS z%UEFlQ^n(MnhWX~#g`8%Q-}dHC4nS+-b`!@+iSza6}xkOw+HmI zX&n%lF+Jjdp!t=1&MzGK;-ptld=n25o9NIxccc^hEbg_#v_P&Hf_X$Pdpt%T07kfa z-zLXqSN+UnRvcr_oBv9oL8OP+WeP71Mrq$qQrU#{^o3b5Enaen$#O;$V7L#cuX^yK zYOWl-Y(&{QMHeDC)f4r;qCeGB8a+KVRZ~Q;2RgN#lL+Vu57a|2{D_k|U<~+e@{O`a zIWe3y=ya~j=I7nh7xCwinom6D`N3?e#C+-6m(F0Mn5EDy@-rSb;#h(K7zwiFU4-N& z+>VFM-wRYJpNTHTOt>uHS$5t44WixN-fZ=dmt3H@gZ0qEx{4V{F%ogeeWH&MjR4s1 zs0*kKkXimJI!9e3vXQ+2_VL$gSX<^}jz-6|&?-K5R+i%uPrz0yG9DYqQEXKERIBr` zub$0?@*Q~(h1nFGO~>_5J2kktLc-MvQ4*yxUOZtc9eeC;pjIUCvhVA{QHX6#o2we~ zWCYdXIQL5$s+QDrY_K>++_L8T2}hooeOMX|T{t>SQ;F833RkA1_0c}K{+Dy(%MaC} zY@GyuwdMp1wE8>Ekgq6l)o&YE9aJslZ)0<#{_9}64o+R$B=*uaP%F+<`SaC4K6e(f zPj!n|-%q9IQEPrftq;)YjtcuKA%qmOo|SU{e5WR%8co2sY8NOeSm{5Glb)dez zSq|9|70`(l)t3FbM}h1sx4)JYJM@f%d6auPy)ZqV!1qs5a`pXcx|6={e2~t3E3YtX z)4|xz>r)?0^Z|68V(gLbkAupHJ~fiX#_MO)bxE(EY^O)deimEOeG(LC;OAq4G$>Ve z=6PE*L53Qi-EP$o2eRt`QBbGcVRaZ43Nd)Z^^x9Oyis9~f~lV3izll)?riSA0(tt@ z0MSuyS`*t>UASqqpi%cCzyHw_f02*BS$8tOZHW)heWyT8(!QgGc7|M7+M}w%c6%>= z3IzvPgm0ScP77dMUHBdoPyf{(S+OmTP-=7%6o@`HREcDKd-F=wa4}(&y};z2>{wJU zg>9|hXvR8bv-gj2>7F^TFX1r~j?L#~*7F?IUoV_0$^Mvr9D~x_Py5eghjGON;q#02 zWk=<5WQu5a7_lQaGecrqa1J)>|CM{NqN`1hyQgnNyyImLR?uyWzmlb0_cK(8K|cLK zv=O-E8l`pWReNvmdxc4Mrc!Joxl{ap(bb`TugCTri)lgq*D@J-U&wt_(Y;=7F=;z&v&WlDh`CsMresedld>yIfwgt46u8~0BkMNdhh8(FfEkn2{id+V=$0@slr9YuHf=H}U_1Jym$ z5b}W;v(Dm0y|gdf^HJBU!op-C3+y^r+WCE)xArYVqnJM{o(~*0g6j6Cu6i_Fee^4@ ze~Th+!`w~tz>SFCQd%3Wa4L3)yqjs#mO!}iD)xE}!ejSJQoE?8S4=_3aB#C$`xwB#HvFGh_=UyaG9>k{c`~;d{QQ9v*D7vg9ItZH zuC%HE$Q5w>?H^2SkLO)MG2*g;{iLw6Tj~SF>~u_+TmT=HXVH9Y-=_#|3HTG<)yvW? za$tav90W#^(&KFY`;5cl8=KD5`H6gIhU2R{?wt)X#LhG6fRa-L=zrtUh27* z_(yzMEH2`U^P?XdzwqSsf5kFWIp`Srpc+$>M>QwlKT+f3(!?fP`)$`(S1nX2{o>`K zj%2sJCg97ykVT+b`!Ptn zW4g8wD?mpKrM{~Jl;g`+ocdJXT@?g3bYa&T%@WV_Ce5|C@YS69E1+H5_z-in5Tm!O zA$YU$yw8Fuy5!KQeJR%u^!F<6d;H9IzY~=~|0OHZnL!#N<_h%W_BVG26+vy@gHlXW ztEv_)eo62EI@Xd*-2bovXIr~K(!JVWQZESEzNn_#)cCI5z27on?2)Uv%pvbU8LOn$ zA*-hWmK$)FTJ_r!CGn~G>PUSrcbm*cfyrUOHsCcA!F)r(*VDFMw`22yTG8AG-V6zJ z9nL`Oz`kB$1;;Ycn7464IX1rXQX)InF=53;e|V564Ke_8%4^ggR2SZKO~yT1_(0aP zDw6pSK{1dx@>CHHrG6s#VRAA!ehLxg-QyZ@#9|ZtleDNBICnG}sTi`}Txksp^;WhDGF(zj{M%0fuPQ8$2}k>5IPuR_@rP z=Z08|0?yKhh2%%_L&n7`!%r?O^P%if{s%?8obMk+gg*PWdq>|9*;Z|WevQmh91hgbZnd8H&f7+f z9Q8W_Ywz&+-88)`H&HfPh1-j7G(P6ejTSr3h1#XrH$bZ z(Ej=I)TlT0jN%rpM1(^pvwZ$U?e&lSIgG!mHA^9ThpBu%cjdWO?GEs|;+;;(3v5y| zLROj=URf_@q#5n%E!(|3gG)k#2`Y9qTrU zF(IWc8WAiJUFWZz zon)_b+9hV&XzMZqdoYJ z)_ve30}Z0$tM%qg_EMm_hSM%&qcM34W}F2`S6?$wYU8WAzsqvm; z{b`tqFsdzAmPEP_pGTv2%lpl+3YiWxVmEW(!T{ftO+rPUF}6O;;?pVA#bVnJ`WmWc z<2~nWq1B43NA>@?U$1r=XKRN?+`f8HH8EA<{$%&b%d|~R(s!rZc>Dsy>A!> zMhD&FNg9-6M%nj_%d;5@a(_D}CWuN5m(V&{BA4z026elM1jALo!SKXyzt9-}g)xY0 zukl0bEBl8(sD{$g1y-etPWRc_URcXLgY`R)ack)##F6gp&saKjn5P~Vm`T(ad{el? zXJ2yvYgl1^{rsy!M6XYwwaz$ufSNd zRjU?b9a0nLOYVs*>%H`Nm4gV0lJL6kqi=ks?IF9uR!Tm9W_Z!GMdBw=OP&uFHM5wq zZ>#mpjLk$}NM>s38{t@t(4XEsuTUW&q|heZ_<}z({qy;EvApJ8$c0XDFf#Q;dRn?}inMAB>@UkIp`8k_EU+3! z6oeP=XVcD-nTObC4oHJJJ<%D#Un>31Mu}xDkM-?x;y zpWi}RriSYUHF}&#m1%f@6l3~}J``M}NYGQilhy3mJCH#zbsbA>2mji#d-LnLF0r-b zX*HcDp-;*tP}95NKN+To`g4sr*cPT+Y;B1PT1FxBhBm8YCh^sg?;oD7C&SJ?r4%AewR>qKdZ> zT70^AnNq-QrfH4Rq5a5KWVV+}QzbHkvYH~NNVY&^vIujcgLo6YbE%cx@H zT>?{LMHNKP+RUbg+)Cd`8|qN-co*;#U^9^mUr{4f)+KStn7=LlL>BmQT9O$KCwe!S zEL}_F&K5emmdSt0#*3!uCa*8T{X^52vuSR&mZg&3{yIF9=OrYCxYNCo7d~AWP#0KE?OwUCre-Asv&Pw z)Cbo0&5Rxi?MCY^o?hbufByta2mXd#KfAt}f$^Dcgddg6sJ)+LI*qS%uz(Z|=pp<$o5**Te# zA_Z;Sts1Ql=7R2f29wXr?)f^B@hRlzv5z>Zr6!sxBa3YHlnpjPAq zY=JH@;4NXcsXl0gX5co?ZBKa{P@_d`7*@4o2+#!mgb~M)$;)om%FHd43b*(VR3@~w znc~N4PNR*tL@ky8X14;Je9l(J8=vv{`&5MHECFoQn#|sRfO|p1Cl}>gk%Qe$z#)(&H6BeWZV;nkxyVv)C>>a zgbnh8@gu0_1?QFCcj>5`@7+4VwJcLxbOPz&#Rj?vdPVX0^l= zU=B07lEi~QWP_OdoC#{g{PPBjCOjl*BO*3#@SMp*Xj%!GSqK&q^y`wIQtkps1_N`L zDO?zY#=!<%TlQE8i(;t4yN|mj(9&G`uQ#l zK{e9*`*D2?POh%97RNy%H<@P$p``ClBx|Lc(TM4x9 z1i5$mavH~7V1|Ks9M$t28m8J=I5QaPeGs1f1gBBAa$$63jl%r*8O1fI zs|!QS(_zid?xu-`xF0QE#G(5A#>kZEPex%SIT_Q*80#7ukVjEf+iDrrmGilCX?T3Xr$~}fj zQF?U7U5_=hLAIWuR8j=d5}Z}_lTJe6VsJy#FFO#TtuHY8`rAcUM-a;GHMJi{Q_HSB z%ntAZ;=ndGFw=~F9rYf=T>JY-FY) z{7N}SB#Uyqj>NCN$%8q8*%Le+SEfgmk+DZqb629}KszSBHtG?xAV7FE&!FrDi7Gf+q1jQXZDg8;YioH zWw^#FMYxx?$=@d!7((5vkMyV{9^FNjxld-?+@N@O)6f=fPtB}*Xg822kFZK|Kj>zN zwhyTrTDN;?Qk2+otWx?xVsjW3CL5;$*H~N5|1p=g^>&zYe3gz&_k4fas(VLtoBEDK zBj4bNNXsJ^qYXTf8blTArX^}FWjw*sX6{Jl*YdY7^g(A~;d087w8HnK5<&~tzTyIO z!FbmDK_w=1*Wq5a%ReHvFX1SDGeL`MU!Yl?Kh2uKVab~wY-SR+JN`R`HOno#%Xc;s2;Cg6~P zFhnh_H>6hHAF2#Xz8}8P=e$7~(4_B|+-cMdqqojk7CnAy)xl$1OSv^82WowJvy;+& ziLOvX``GfGhhm#`E>juS(FJk|GtVZvnCh&;Z#+@aS#pi9ac9&#{B6#5E9}C!l5&f? zgfH#A^=(V8s;7qCM(x#!3UgA-5gNWhI}Z+Dj40dIfL}QWt_-7h>*`Ht52ctQB;sn- zG}`TWFLDA-Basw|Z{A#{lDri>4UVCry3nQ&LB=g5^2E?`{Qhh47ZZ*B>D6fYS6Z%o zzex%atElm@6`WNc~0f^5*4e zJK|3Iq*ob}$mma}e_q-V+vbe$3Yssp3_8Q@9)ejq=jKM3XZRJQG#X{d-1iOBuHOi@ zOp-`rC3Dk~4R4G#POCozn;T%4L%lF>sTzQ*Uq%Ife!>?+XY*15#4aQHiK-yRCq!>2 zh`&OagTRLqT@RaIj~!jEn@3>3q#E6QH5fJ0$^c6wg0ao(Nv|3}biR6AJ3|UJa~?sm z+pv$V%&GPlBwKH-ZMy1NC6^5_p!eR|{bgX|h(F}V>E-&%TTIDPMd;S>A#h=6*odznG8;JHNcy@F7D(Z2MnZPlYpZA%#p03%Xocl54{6+KgncUcfj|N)NDfadyh&A?15oCgsQYOw_JaXFiYsKBU z+Y&$%DuahK&`tPw;U0NhE+{BXL6>C*tGOKabM+mj_+0WCe8>iQh z=%HQ?I;mrkuq?0jsMkZ$&R#jfBP6;jRjlAlp@mKcNSSzU{i(Ow{3>Wtv(jIR>d8&W z6wNJ)J$Js5WCX&`QEygUby#>TTE-F%Qbs$6cqQeizc*Fmx`F0b|0WN<&?H;eMD{a!V+5n?-eYvvmdrb#z_phJ?EDtflQ`wg0TJUZ9X(K6GZIusl=1r)%=Cotu zkrwi(s6hKP50QC5vGj$v<|u%daSR91qlL#HeQ^fuN=#>~D)=nJH;~U0K?>>n=`3U~ zD*<8jPv6Zg8@nj#HZCdg58oT#81RrwWs0g z-R(at>qgbf%0SjVd&cEWoMYxRw*c?ctmwRnfxpQBDgUBH`01T>XFv9Sk9&Rj+Gqyc z{@k#wsy=?C4dy-Fuog_zwb-TOAK|}IlJL=>^yTP^vyelK*1525l&;r52IBB-<@S_J}gLB zl_hTzb!Su;!{6xGTD4f1Is%w%E9rXT)l?LkC_(d(*WWph1L*AZ^5RXCw+V0dmd`We z0^LSq%Qg&4!&JUFyKtsjB1u6YBMzWb);hpO{vU*;+hXo9bpPpVl@xKOjX$L#MLU%d z8U^7CC>foRU&{OZWNr@fehzQ;v7`~%!$16^p{9!lcy@yAt*Pz^&Vm*-%g5W zTz%m6xRI^wRp;|lTL{R2-TN4+@^S5{Q)r@z&Nk`qD~V4MF*?T2FH+9?eL2R6+cr!) z`7BNWHmu_b!&bJly+*N-u@(xcX&+!~8qP3-sm;(~c|nm_GB2jp&}G@S6}J%tzvq_4 zpPYG)Vmr-_R#5ZpVCnHPv(XTMFWxbEA?U4l83#1`+WRSHubHsp zXzyGSKoe(H{T=_U3e0x>p}Or>v}EUvnrcU233Gw*VrhcBe~xFb-CgpSDwyC&$@k~1 zd9RvSWG1y>u2pVkv5*(YWwZfjEAD%cr_Z&?Ngrr>f#{8}ymWUH1!|2*b)fZ7H=4zJTq*6$bv|6Cp7_MDD)gY<5S$8e9jek*=?BkZVwy z5$Cy!P31BMp(PEb2)K&w*>Mn;D&D!AuN&G%HUHd?LK%bTslc=`K^~xa%02Irya;5+ z-V+CHf}lANzEv7>x}b7%XMw7lLbsz$lWjqnfUw=ZdXmeLjZcN|?Swuly7C6FM!0ue zsy5-EYG^wrgWL)_3mx1(!9(=#pHkv~7?iZJv!E974f2a}dCSXWAA*8ZEOfMbJbsc> zUYiL<>i2)Dzbsv{2XURkWAdk;e8x2f!Gykbb69U`5fa{$s@!%QaSax2RIMu%yvpk( zHBsStYx(@lCJ%nGn%H=kt!Dc@o^du_oZu5)ob(r*b`zi8+Euef|CfQg^<@iV-ddSu zA`P4_OVNX|l1IB4!X9l)#Yqs_>buz&XF{}5dT8Tn_{mvNG4R6Cj~(+}R4T>2>+s7~ z$kUGte2%xq-ku|8={h8YcxkU>_(@yNbMCRYR|-@I2B1F#eB zSgrAoF%xWD?=LvhRt9c8DBed;S=tc&+X!E>qmf)Ck?=KJ-o+KQN{ip3Yc9h{E8wKh zD#<(W0q*>u_)?ot_ZcOa*B={PnSp=~ed&2Jr(jw0eJTjw4IfTn>LW-0N`U) z>^tEjH82K57Xg%W(g!(b@J5LUC=7(7>{S`o>jAS9#IC*F{n^lCjR0vaWpL2uJ)sY? zulv|9prO?=ia>FVAcE+ls`i|^7-V`8bHD46-2)sj38eYf>k370vTlz&0b=-wT|q5vF0k-%%i2n$zlq0#z47l5F1OYPX7~FqJk{wr1 zUw178$hB?m*sptzqXtM=6>9oILzw~Ec5G0|#w<6{Ym))~Cy&DbghM-i3Sue?4J2-e z`{BYPGGof*O$2@ovqL52DcvW11&`}`nSVFCB!^>O{LE$+u%Q*jX<%4TLN{LUvczM2&`KlDjGQ=0lj`Ro9#cE-|pP|j0@ z>KI-!Pm68#JVpL-+~aAl^k*&r^T4|lk*N^KNOk-`yJ#9v=C082lpkZ-fWD}jsbUzC z$^^Yn>X|bgY|*Qwx*imyMcYgi))0~;U9(ujp`HZ69D*PvMMO=&a&~afT z>)QAJnq{RxsY3THF&U4{*+W>Nuhq_$A{h|9XOI_+X4#d;^L4JLJsXm3@|jaj%w$@r zyWI_}`3c{HpS*detm=hAQqHQB)JMPZ{EnrL&oDZT-pds`t0~!gbw5bYC_r`M>87jZ z2Ygs!irL>j?RZ$?!NQMfrjlH^Fy9q!-1 zUAYmMo1%3;Q($}OXh7udKW*)e_gPP_dbkXT`?LDW5qf*}F(Fi;s4XP}V+Gkz8*R+> zi7s)W)v#Le&KpT8S{{9$d|0Ypo@ppYq7Z-jv&Z`dR_J&C-Ko(L9P9SBEUvl175hi5 Nt7Uk-P!sp~{{S&bvCsej literal 7489 zcmcI}XH-*9(EbfosRpDsjZzeZ2qLI7B?t;qB%zlm(tDE*L4HE$Mg*j*NDECVfl#Bg zPy{ic(jy6=AiWF7|N4GL`1cHKsxBmjL|C4r6YBq=gfcwu6P{Kk(HD2SDAO^g+GGLI!hYdrq;c73csQ5jbX&)I< z-S7K_;5F4mdaWJT<~8v+8OvnD<}XN!1P*lQoD+lwq1gAHGi?#pUai|-y)qx(k6$k* zk+*(PoP}b2)iKO~8z87L#{ex``u+Q`(th3ObYhoN`65N*sb&R!D1q$F*%3UOZf+OS zL?n*wToAbE<7mf{bJ?N$1@dAbFXj|xvo%Rw6uQ2(`rtg?>X*abFieq^BsxK8w?Uz{ zovsK^oq=&HU%mR*5HZp-SxO4_WpQ{}Ogu%p1k8t=Ryr5^U|{v<&%OGYkgZxdcX5pU zeqPTRLZV*(aG|cbylvB#SbN&*!-aXrcqj);_0@a+N*G;WKv$e!ppSeSb+lVkQMn>(SA$B82mbv@Nr*Z>|hXAez7zjAWvhS9QpLP=LmIcL@%FQaTQ&#_z(Y$7b zc5`!+dRaWihCT;SdL2ydgiG)Znd#}v*sh2677bo`)DdFVl`5+j*wT^w@_gHPumijlvZ(ZS~szNY7b2)zN74ThU9h(7Mu;P1r z)N$y$Mq@7Iw!2!|g-gFq3tr}+#Q{|TH-s)m!u(1cEmaQkSVJ5-8F)EZj_;r}X*!BX z9wt)6uuDMnO#HKh*h_F_W#vu8+p$8M(f}sJqe}&87b3O!L6w8sJSQ|6NW1!EP6^~_ z+_y?=^B>@+^Uty;VJF$qEpbHSU&No(4>8BHK|J*Qo9v)Qha8x4wZW%%DaT%(o_X_N ze@YtLz(|>?5ftZrfa6Gi%Qit+HAiXi+5KmD5zfcQcg%r)!VS*UG*VO16)Aml@J1Dh zDdvL`YCCE#og-Zyy8n$pAhfu(84|%ag0br%04==l>D`I9=JpZWq3vmBoW*3}_4W1X z_&*_w}<`^9@@lBk}Zo~{B3 z{%};6Lj2faL5H2g>5X89+FoSqe*CeP_KS<_=`CI4Xun|qua%Y6;3iz0*a_#L6@nI= z=D}GM`@?W#DZn;GT8Rm||I++DS)Bz_2ENQcXzkfll!8%O6LeNCBLfp=?mR2`9UB`P zRFm7>KWq@Y`%SzXmiZ!{d`+W^0BJcvH3FuQqqaY%BsV;uWejVVfjNyivjL9&8bii+ zDv3l1pX*t#OE1p+RA6uf26t5Fr2u!x;T7@JcIA;*uIN(e(yPv8^4-MD_^#uc(Fom$J zz*rij+`;o2U!Wb!++9;)h1&LLHAIb7)t?XqZWBedQQIlOYIbQ}zSm52b#>LmQ{#|< zP|Ok6f_Xv)qHs9t7nhfp*Hs|Aqbr<6X`B{JXJEwStVW0$j~c^pdu*W+D}8rhxL{+H z0%Xl;r7XP;gH>yC`jpLGqST1TFB%57mrJu7K;hKs@z_dy!M*c5+&4l#BVfb%cZ=qM z;E7&Z#;ZGJ$i*HbFpXHF9GAOpqfD52v%zwPIiz^>(v|? z8Ih~;Jjm4ZkMa~AAp(((mS0y9bSQMpZDQvDADWLH&|YOYxS+A1$2^eEP>9z|?V$xV z`TmX+6BBE{dT&!4u=i0igp9FKp}-w5V?26-w~L)#cD`-p+>Age{A$-e|9tq4X9-?| z4c&}wsmAj{26%DDTpCWs8WOm5p5_<9*GSKWiPe{PCSq8wO z=234-UtgarP+wlDD8dwK3Y%6!FaP6_p?c<$ro zPg|y)D_)@f)sU79eVdC0Dg1)av03`MBKF|9gzwR#i5;ae(6qUOaLx|0s@Kf*PqoC( zqL1_~p@FDt;VpIF2~%^rHkx9pK4_z>o5;W*t_R#ISFVVzot5X4(PdBaoM=1!QtQlG zJ~&xGw~%vg)+aS+@0{evVOg_^(yf0|B?`E1)sPmB7n6*(Wyp)=8PD<8WiuNcx|4bd z>5iWAzGZMQDbk$(=So?^IY`z7z)A@MWF75Akav z*-SPJ)n{@5=AoiW_PGudJGEd!r)YCp5dG%HG91-^uC?8c;yb~VUn{qi26i_;Uj7e{ z$8*AJ20k<*(YcXhm*H+BrR5NWn8wO814_#YtqZ%}>iVcC56iDkTW$C9PT^wkp(%K~ zuMM2#A|gw9>{Nja$0IwG%Bx+^l@4G~nZx0xktvqV{8NVgx+dcIM^{PL$$% z>V^_waM*@|o${MO|N01f@|)$4PJqZC@fT_p_=Q)yev1@AqTT>MZj3Q1=2(1WXr194 z%=Gc{I3MKQmtPJArAypCSpFtK;?VMIx6C zK%hzGFG;n&B|h+O?#a1I@zSlvakw?&vQ~+$fI=L}sqNk2X|b8{CF-X86AT{#wt^sD$KJ z8CE;b{mYyrQ*dU@ERBdzcv?INsGPIsF9Lahe4u3Wmxl^gbuu~0tB^D0{7__*jrD(G zNq9R4!EWVZ*R~VrLJ(&t$jT-L1q6sLT~-X)pqmuxrslIz{ZOsKq!~<(&$V8Q*3fMs zV7fQp4K`^(N&dx2BlZD6f_crP1K##<;;I29T!0h?7%m^M0Ya-o+OBL&47Gq-{x3Du zCjyEwy)=A`Z=D0+J&2qeOXx?uJxjS3;q7&-BJkxkEJJ}gwk1&eig2sU)CW&bPsi8? zs>6%XcRAP%Ton=Y9|2bCTMae{IEa65!}eAmNe{GU?1ln(tB)&PK4ugXumw5PrO*u|i2JSqe@ZOzovDqZ~86mVFw zvK$;i7v>j)`EjyyK#8%;u~?f925m&ugXpucZ|_K_aPAi9|1EY;gYRWbq_h@daHE#= z7N`SWhV@-2L3Y#D->O-p~Rl*hO(e>Ld0^$C2Kq3y&PLx+c=2Y zNow!n;$j$by9fB&m&p+?;~bg$p`{3)nQviZI5IXi7P9qoSRZIvF|k9?;X~|6_~~4| z^LF=~e-k_>rj19X8vJ3fpkQ0Ho#A<&R7b_LIK4v#l9K$Kc9icak_4oMgL-eP3J0C* zq$q=-^4TXf6j(~Q27M^0`^~auikh-8F&W+=4n?d?MbFqYc-c}vzt)y88I^_Da8h~R zF~S_lOL<}qPD(@YBu@|8&zC_6dKFl=gsYrp6BBkbmN|X*bx`fWGSJrQHj0mTbad>8 z$mv9?Piut?EGfNQ@t7}Le5QHg1r9YeHDzx_@EB_|kOn;_KpNu+Mg>5NL+f@}lBw&) zRvMYGeA3cTeP|eQ*N7W46xZ=1r{;n@wzT4{tU4SunFK;bFWMBNmo}FRdcY!q$yfXwOZFjykca$^>K z@lNCfO|hi&x|N`}(~6Z|FHV(3U{FKjB*n-w$kl z{{f!lj4v-=V0^~gCj9~J;}sONYx=WQ@~)-v=&GDu%bv2r!!g2<7!b7st{L(%DC`k(mB^-}nw z2$n*;Ex<3u6TidT=Ga497XVsLaj>i^bI^M5i*#+v3674v0A1altAO5l9sskw^sVIc z!QdAqu)Ns?m+AmW_`P+o+TyvM)uNxAW=Q9GV<yRkv^6(t+hLP=r!@ltK;E zaC&Nzu=ZZqS1lt+p7^Ac;jyuOB_PK?$=atoS!6*!+61M+t=-exNW$uwC5A*SEBNC^ z!&ztfg(G&Sf{qrQ2gDTva+5&!^cqt9`ff?etBVf@UEmuNPI{4<8AZ+{!Aka_nH%O; zGxc<9PM(MTnyh)OR)tRyZ20~ajp)n-QDxd8R)1eMhanBM#DvOw`KiT*mY`SV%btQA z*@K#+H0O0~`s~L}zx1${rjPjDM1;p%W0Bi#)ecoQM6e)u5dE6=$P_)#8uLLY%i!hp zz{P7o)eyL%0uV(Gx}?4pQgSkYtd-%qL9I9t^tAs$G>T4^mSf7)a^Zj{9)GpL$hWHJ zt<5cJZ(_0@0TBPjRU41iS*wqXj;0sgFFU|>Q~J6HER5)ZQja%U<%qT$D48%${R_gUHX@3l_y_;Kw>y-=eH{}{M+lsr$1%O z&8s!K616fMBltl9{ICOJJE|Us_dVr%JYbYGP}v-rn4N;EHO!kjcb43=iG< z>HOpal~48T@t;3`Fs|9=$s#H%A*iE@4qx_9maz}QhnLSReXSHZ2McNxi(@mbS{5!Y zTa10ANR$@j(2#~T1Gy)uw@{zxX09rs&2RD`=bBk5RWcn+Iz^Aikc@Lw}==L9|($oINg8!xum{mlo7e6LZQhg_O4E6HR{+iS_^&K z*?Hku*un-lgVU=6{b>R~ChC1GIZWtfH7y${cDZ7;Outz;{1c3_Kj@ds_JZxu2zs*C z8F+PTYs7cmJ=*%7M5?&@b=KAc$>uy$lc{3yP9kgrc}3Xv`BJo5g@tdnqa}9&`J1m_ zv*{ZeI2@_`WqSH==yit%ko3|;WUG`(i|;~u;g><>gpmn+1b;A z9%vB7UiO{sw1oM60*N(#hz#+Yyo`#Sz{?6Nbu8Hkna1(CI~FBT2)uQb9Y{QRt!GL3 zWm3}-_cK&^Y(P3$m;<+C7J0b2L4k&P zq5AVbR#zIwXWn*xLt|}dfzc-gV|}BVD#gK!+=mS0!vt7sKYHp8N35*TP|@JhQbDe( zn@h!I=l7Vync7t{bYFiQT*?iFS z!v~j17W4RG@W0{5#skjS$@lh%eB;qeu#Gl+J|~(VGe%`)5BZO|c<(nKt{Oxb_Nt;9 zuJ$Xaa$A3_SuZrzAx?}1} zEAv|qmOgbN+^um|(HP8QRw&!~$M=!r7U+ck@R64(m0)3xWu`8yJVLx!$kc;v#ClFN z2*zH1nC3G<=t(5+0tSOu452N*zbiimpD}~exN5vxahMgfNU|CXj*&OOK%23$2SQyG zG@h7cs&Rj`;6Jcxyy`C^ESwL5HE%$*=SLlO<1f!I+sqzDnL~4C#9Q?^BHOEu4*}Se z-dzT5SL4wNwk04;^v-`jndIO)S_qA2w6lYL=b>+SK?(-~&B85Df`tzwrtrXwciSQQ z0t3G3U1jB^a^{10UTHfHY6n*df(gT1Y?0gGzc46Ln^f5#z!(L9CooLWW(osyRFMvhj8cx=)Z+8%?Cr#cePY5w*kW*q=kkRU|Fkn!3Qa{`Q{1CWNg zf=$c6Rs7KbbNuW@!Sv=0NFNvyt7q=)Rbr=gE=nCzWf@p7=7)wKy~CfYC>)KgGRXwR z;*0Bf&)|9pfAWC2K{!VYbL<9}=3YV!?ru@dv}uWj52w^ImdxMo|7udmn&9E8n<0|H z$6ob;p0@(>3kyevPyC#LcRKW-NayD_@8CKXx@fFtJ+V;M|+!U{0=mtZ~0 zxr!V#YndNib{mOID$V?2D)G3kM1fjj5LDzbt$uKDuoCdvl@XX( z^uWV!6AK%MKF`#v8kd9>c(}L(gB%<=Ob9&uF5ccV(-evlJ4wo!vd*f2v*A_YpwZO) zFW6f02?F1n0++;5tv0AjtedZA*MmWPEiEi8{Fe?g)dC8hEwEBo_?`SCcwzk$k0xlT-6`oXzx0~<0T(*YQ3o|k@axgVD?e*3* zX!W}uBP4^2e(RlHI#}evHF{21BbA}?h`*h)`iTFV ccJxaTWG!3dl~5wv*h{=LDf6i)^Oh}3B1-lp z%S;o(WC_LJge2=vlr6G+)AxPnJKy>K&$-XN&%Muco^$UxcfD0ahoEQJTT&B9mPPTWZ zn%X0JC!zyKYXigQomv;Qd;>?TYX7jK>-w>cz4MEsIo}J@&QxaRya@aLsU&lAdGGAu zIZJ3Q_{hsP z%bqL#Dz@ES<~fgLdX%-l3U8Aju@R38!`dr8By8YD!IMo`gkXG$d6>SRWD2O~&Wq?t zNHEg7c-H}geJkh|3{ZG_pai3nOKR5q70}!d1w=j8T-LiVo5sFxEYix2)U1{E2y%N2 zAY-3!3tOMvDW(;r(4Q#2Gz~ z{}2H?sm8XaYH@$TXVxf=GwQ=!b0&aYX#;XP1R{AjH!yuPs3DRpi+P31>Ho1d7FtfN zMQPF>#kLHK2aB^1BTX?^>T-zE6^9Exh4QkRd!S85ZdGl!<%=y#D?sA)&%u6S3&xpk zF=*>SOjozl#g+&j)|>cDaP@s&wo_%3!{aetU{nX=dy-h*8%Uqc$AC7W;;cf-`A_^R z^L7$+_Ln1Y<=j}A-T;c)*v<>6F${c~sF}e8!M4lw;=LEhb$WBz?-0|H31%n1aKma| zw2lgFQQ4f9-0`=8#biEUI&<@9f*361j?hGRt{k_dq^^7^`i?MH6tYQa)`YoY2IjE> zH}R|46&2fsDMNJ|+apt6(Cy69qrYV`l;Pa1-fu%iq-@cpPkxU|c6o4Hme6h5H_ntu`hF@|^JeZ$$!9u^N8)F^f{~KS_r~O`l2{cb4?$+Jl+RmF5YnBPKX%6u%A zK9^-P{Nig5EzA7Grp}uhc5jU75*?6xEi=A9}vG zgKd?W7Q3GOp|Uoh;N@L{G4P`*+iXKDo`PCQT#ED{Ppy~Hg;|lLUhh6kxz;hb{EjAS z+kvdgz(ESpvLlsAEAc3lqu>a=FF&UfCBQ(?3Uni85)JQt0+9=l;~g z{XuN8e(qr*+qSo9hPVd=_0`4<&;(kYqf5*h`PIed^|?{b%izuTQw!A0npDfu-6+l^ zq$pX&x8xpHpjSkIf!vk!fOXeVC`b;Mu+3hxe=td%m*!#!Q%lDBZY?;3c+@1a3(e7J z3K)-69hq5GtoLgfqToPUajHFv&3;m5&^K{2Er079t zlS>mx&P0t1Tic=Ah34uAjKN#rd2RBwmeB+}A@|+F&usGUIS@S^OzhZsjfHc+g?eny z+#hjB$ssO2l}T53O_YPN0*B7lAy!KDo(wA1JBFAy*mb#b%dRsp!BmV09dcI5U!2DLB$EQpuJ#>-MtKNsRt^=ifswhbbs-%6E zYP4s#(Qy!sio%xWT@J`4lu>E%=BNVOS97Bu&$U>hD4-HW0(CucG(_$y(;01pKXgPJ z3HN-M%E;chi=lw#UrkgJoqCCy;>d!8nI;7Iy&v9OCcOmLN&UZx5I*y*6pp~?NbZqMWLuG58n_! z6nEarA>_w_=3W9^QQ_uvylyW-DuNT?0qOZ082dBmX=5f_3ih0)R78G5O$%}ideHxS z30*|xC7N3&dI5e1OjNzp`P!$}2k6Ttp09giUSCo|^5`e#BuL@Do06r`?Crig9idGF znC6tTUkTA#g5nU@RkQI%tbl~v`In~Uk1Lu(4&774ZFb5SMtjzf+*Of0O?<}P^$k8+ zP1Ik}hpyyBvlVwfGDnSiK-opo>x`{;ibzK>PCjJcQV!8>u?RMwjHQ|e$GtsSzkg%W z4`H+mnwR>dg`btcH;P!lq0;AEE(YZChGKIn;vk0$BR#VH1TG@ znjGxO%sFuHI8-9UsK%RrrYYi}O>qP97o}Q1LQ*4R4Ap9q?tZYE;iiDBpoa``T}W4c z(*AeQ3J(z&#jS5nS1XjfV0|6j;V3wCSOk12hl5DV&o!d&fzOG6-y`!H?e0jV;~{9% z|EUubs@5-0Ll@Q7YExri(4Pn-?pvWyM@}1YtoF?1YZf(Ml=c3}O9W0N9WRX`IKwv(La=T8`D83F8k z62Q^s|H583cz|)yDO)vu^C75F%Eh4&hpKrXR!|!I#)S&TIgLhOmRF@xcq;}>RfJjd zWC?n;h_%zxNHQ1ejsC69=i6fLhyki#5AbO4or6s8%?TT*vru}2jb5KjdqApr&FyP` zMu%BgJ~^4A%m_oKHyZq4^GC?T)lXB&{Hz1G&M^4Q**Qbnj6*Qq4?Yu~JL3e%C?UN) zW5jxECgM~{AJ-_KrljGU_R|91&Miv~IQP>)6Ejm57H6UfArF1*dxs{3bC?Q8!|%^r z?YkqP%T##Y`zePWrVC@3`~*g5b=pkKs}r=s`lq(*`ga*^SzZ@HqALf}-Qt8|CjW+R zm-X&T@9!E0{jV_&ce?PbvdIo&toUACX3|F^SIFES8d=6NkYV;#A1sUjK7(&*XPgZ~ zBKWa{wy(v4esCQ|bKs`j?Coy;=_Q8^8lmv^yC+G4kiGn1%i{Y%;f=?0_8p9-Jc+_Y zq_Jo%Gcq-2HR7Fta?J;$$tP-pbfXSm*nXZBuDH(W;6o3lmLGcL6*s7O;ModXZLEAu znhx)&!oTdr5r7E>H!S*>EMr0@#Emv0=ScyjZ2#1y&# zR)z@d>f>S}EjD+rcP;a0wfhS}yPLaJI=|}COn?=%NKg1P0>#VTSP(-U`S)oEfdDbr W`e5@R3Blx!K) z5QZ71#m-Qcp|Sj*-}~wP@c;VW$MGC<%zaw51f_gDvnn%#I5f;av9RV;f zD;SZCnGs&u|H&YktE{krC=qDd5aA5LfZ!F~fuTmTbH{f{XipKf&Pi6>J$v?SYh`6+ z`b+YMYih-iJdFEFk;#y^OQVy^B|W_e#^>zwMdpg-TUZO86ubmV`#qobF>Nuf`u;NC zMC^fL_wEI26_NA+MjkFKp~d1DtxSjcp*%*9P34x|cp_}Ue6{`)l@%?+MhCrMHNWY<6vFwE++|^9 zb=6K$SWQ}7oP2)eNhwa=1_;3pp=w>+ggW2xzyYco_4x7MS>C4~O-zi9clveCELqfv zh@oA7(Oo&uVJ7-6HwTrv46Vj2EiEy-A>p(A{iZhP_EPg6z>(epT@=h5mXeDJiL;KXt52LOv14 zT9}ac9NJoiL8Y9(FoN$Kar1v|t@MJs01w`ca3IzQf@ZYH?tQJkh0Q(P1HlV$|L7MANoY$`HD~pknb4Ssw^4ebJ)4YxO;0Y&QJx z9Lns^l4)%qBl#obio(hjIjPkA5Bqu>JEbQwI`0x5^xXUXsrp35uOUYuRyM@N0=v@m zUz7tgJ%v$blQS@vO=vcK_xj~lbIw9QtCz8{@%)2=1UdJ2(A*JlSb28BmJC!k0N4VV z`r#XyP%j|DX4Z=G{N$~QqgyH>f}eG`VZF&f%B^#RE62i1uMV-QnOHq#l#+)Lhl1}F z*9=ReTA6is#STNqLrwSZSR*JAf;6i5ob16`(F+yF%tz@Su!$yj$T~~NknFLgu-ZHC z5g+QSvz|TDa|LWgAF>6kEv z#X4cOo`DEdMrqTfhOG+MHfCQmE*eT)Js_Z?m-qt2$3{Ck+y>u9nDXGSnDvpl+8NJo z2<*+PFs-8MZ>vZz(sOQ3XFyf)BSfO4RZSf>s8uz`w8sMXOr<)#WU>GE@85pjeu2z@ z*6`WhR0%6BTx~^u9{t>Q0)ZOn;Se49^QLfteWl&_+9KgrmHp7K%{?5ZsQj5Ih#nc> zR43=if`UBWDXYyT|1m2IaC_aCV^*egddM;;ndj-pL#5uVCu{_XddurJj7B79N_u)N zSUN>7YmF<0_K#;z<}Wsa*MAnXp7&`EQC;)wRbZAN$=u-MnQz~~)vkW~d<{J-gw_cB z^!I1OJy^gWLZ^67TC^^PGrBLDe=H=CBJt?t#yQ04*ObSOLUG??fX7KyO^I+^C40wX zmr@UZO_5=otxPcOJ}v0s$kL)zc5Vw(0<(TecbGVndrfD0GfCJD zb{&jhJikX}&qpHt)NvO^ZoxssRdFb@3CnMbAH8KJKd6sD9@UW5?}|wqx2*&}oS0$- zBPyP_X>XjNA)sEBrnzJnWXLHL$A)+Qz;-1%?(QSg*PQ8TX&lI%I=K@sdxn+zve?+h zoJ7veF~)|S^gWO>^!vyVO%z|Fri7=9fS81|^h`xY?8G~lunc@fM^y!ng&>!yug_WV z%w6Qfr^}Vt8um0+>GZ%E{jG=SE;CS4x0E%JDmD+kKQS>aRA|q;!{C0!U9_<>y(Hn+7ke2Mlsa-A)7Ww8+K$F z*5&yZ3MiK>NI9^fMZkKOMCg$h-e2L0348l(8Ha4@C#F?29T3yT$!RQU>d+F8afbB5 zpFjkWx*o`wWj8>98k=j6g!PbL7@3NYW2n2}&EfeY@H$weRJitp&qW+VGNAvA!^j`| ztp~BOv9-j7&73x-HPQc4F4;9_a89G~>3)ri&c%ycCI&*`0=G{E!k1)W@H_AaNU!6$ z3?t$yH|>Pyr)6u2fL6-Qxq#57NHvg!rR8B8TrHk{NEZ8f$f4a1S4;o>;|KU!nN4dH z0&zOoUXWw}gi~aN7?$es`rF>g`AFEP;(}}E{)};2!6m;LCb9bar8f)K{4t0};ouXTROH7ex08IkZ_**ymw3;b#O0L}^Z6D=1O{f*SzR{>FdO{w=o0|I*^yy1Kf! zqYm>j(sTULy4Vjh(Ih+}ypQ-+eT1A~NVgAeDR)*I?_F4-DPPUR9=Gmi{|3p z3TtmxL6h>Z4;vqwC*PRX>83Pz+Mf59YH2KJAr<^kgB)F$-#fL9F!qN+_4E$zC*LUY z&@9LMb<*2bB4a+Q>$YqBOGiGr zAm;n7xW4{N$GpR0vG&ep(leHnaQ)ebl70wV<${tD-+TnB3W1dwTtQm)7Hr#YGPqUB>Uy8+?}+P>yKE z>8|znVftJ#$Wpar9CNhxH5ouRpaW|8Eck2?Zlr%p}btrg)u zNPoK57H2D*-UQ*BZR=o-i;owraltpO(ieKg9NH`>7j);N4QN&BBf2?fm(~w!!djV+ z#NyDP!Pkm^pJIK$J2hSZbfmCpjaM~4-n}ChcU2u1AZ*0oFnHg3$W$=CX(xv_idqta zwf_@=!AEryk;ikxw2q$H!hf6E)g7f8YQyP!!{qug1-72YFo$+`cOfXpq)?b3>@8uH6~07=HT~KbMtQ_+ zGU^q%Vjj4;xe02bss;J@+9#-fW>?NHU54%8^tgBL9jIAVQx2}v+FKUZMCp;iS*oIM zTj6TSy|bjbOyArCHQF@&FjMQB4+}y6RZzHCrrc%L_oNGFyZqj1^Q3x#r z?YCPwN^W5q5Ckzt;^y`d-j5%*m2tSxXV9q~rFxz|d;UVGEWA|vNrLmKq_qTxWZ-oD3O1}n=R!}0k;A&{+y4#X7GwR*l-JI2TI@(HJ@RHtE zi-eXzSC{C6-4M7J(v6^pY^rpsQPaSk06_8;b}c_Rlhk2{U_;d5FL0pHL_XS{tC3Tg zx!f^W3QtOcoH4tx;qlktv$BadF~>4Ky&)u+-m^m(%Bd?uWHZH1iv73QD2yFxJIHQ_whd{kw*2bO)7qh6~buJ_I0Ut+-j}>IAG}J{DD`c-QV;z zDLVjS$F%TWg1ckDrm)-;cH!Db>#Y3dSVDM;<}Nr%HdFoBJoB*;N<|}4uk4idMpq{R zl#P!L?1t>eU?XbCdc9>=%MP+UZYKZSN!Lt1gt{Tum2w8%n{D7tTKf8teMBPhJ92z^ z?%h^K122&z(pMnnC2~-MUwpAb?i); zP!38;f108{FNgfZ5>)A(V^r7o5wGxh_%8OA#3|LAo(4i}MbZEa{Bx>KL=C>{N&9~B z9t@p`BWO3;&KVbh^n_caA5BsR>4%|cnb$kAzXUOtG_|el(j_#peiy3dH}|YcPp&4N ze>ce&ey-*bNpDIOGD;AQevJu5HgQ7Io%~zaCMg6-Cd(O=fOTn|Giih+2ORHrf}uzY z+-`sQ)hiEjmit%g9W#(&xmt%^XyE^Tl;T>rLJHMxFj&9uzM(PXXCHDjUC2{F#vhb2$7 z3TWV%7&i>lIR#e}zwEyd9!wz8OU9RrziM*%U8=J@&Sr4#Lj3ZJM(?U`;J`a4R7DHH zGvDsz`y7;EYy2Q|H$p4_@U(4?>2_Dxa*1Nn3TNV0f;|s$Brn2(Li8>_UB_y{@(Fg*P|p0aGq+v?7N_Mz;D22rpEn&7{a;;1ZVfxhNhQ{YQ(oI^kx5{U^q(;&y^AD~hKg}S^xj_hVYTp2EdD4s>36Al z!`~AN9<+z(BAmyVvl`qood2~Y@MqtxkorUw-_>dK@nmM=qy7!5`Qp8kJWtWqVR|=y zu}@ttlj4Y6rhjj8KS`HvVcO7;wAGN~`8_Y}1r;Vn(wfrp0vWcLX$<<4Wb^duNwyEn zECCH*%o6pV2T3kjuF;iILXCRKg+YwzXnuk;K>~yH&DJ-mnfrJhF^tP~)(AZqX@Rs$h#lHti{v~yF|3DiSp!e1HK}O|{?USfKoSuh?H};G{F}9x7E#Bqh zr0)>uG?}=gjw`4)M? zev2|6eA=an(``Ovg*tm77qTp>;v={Iz}1@HS}X3u^0NT)!5f|4bGc%xP=$y1n!t_J zS~$Cp+&NFvCM~UyyqYUrfrbr=%lilkcgF{&0<}v~n8yLnG+FAv`yIytH#s7AOwR&R zK^DPPK3{OF@6u;q!UWL$Sta%S@E;NYgafQ--1p0)qoXUt)sf@~ z)qYrwW~mKkOn`H@yT6r`%CBkWBJl@)UmsS*886|@s#vK@{-+7Jh5Lug`*_vLf%q9)np-v7h6F&lpz4U)hjlr=PmO>OksK6*X*gv0s-xiP5D*ICi)bHz|7S^fMc_v@MJJ6B)Nl4cUa-9 z-a!oA%0~Dkt9HX-RYFj!3NKLv&)VmnA1e}Sbo|x1q5m$T5Hi7plEv<8)6_M?5JUFzW{Ql_ z*UD_O2ULJ~N=SrpX+!zCSE4Uu?c0(_eL-(2x`jz4i#~xTb81r4(_Ojq^H7$g1v|v! zKIZxC&j&+%VlpyaF;j;&vWbgZ=A|Zpykz|H$vSs9hsOiKSH4l?-ybj-j9-Js7oz?u z-7i`9%(2CN@TL;9>f+<~_$aYmrtkxKRMd#o$q0 zeJyS6$+0wkKmy@^f3Y~+5O~mjbD*1Se0sgOc>6gZ@u+PKJPFLypI8!{(#&(MiYoGr z%0wqSV97&w#qLNNeU;BPnFOjaGjY#h3jPm?Me`p^HrR4W&h3n)GrjtyhQRyyXOeYF)iL*(z0if3g^ zw#BP9I-`C#%WG4jAPJmh3G1Tzgn%3J?}rIiFm3if6R;V96GmUV9T!fo@xJNcQy|?W zuFg~kKHI4zn3`xnBV2Z(K_2c_1$T-ya|LW`?AM2NI6Y@yfJf<_I#f$9NWoldLCDn7 z4j>Qhup>oSt`6%Q%c$8ewNpMY8HbrNo7$D%D-X))M5|@!45rsxUi(p3Mo9TOo5Ovu zk6za{5UTFi;Y>6EA&`PB&G++@xd^qry}b>Gq_a(td4HFQ^+J__3_oN+>?ijX@TLzU zp2`e+K910dUDymgc(rn7L{tnKCfTCg4X-cN%pKmK&JE_QaL-8B`*c@YmH255Bw!=1 ztVqh;xu6I^hKO!-qy!9Ps%6wSH^J3rp9LR3UQ2dD-r+LzRLE2>XndfOR1#%)B&fGw z#NJD^AMNr|&rH3QZB~{ESaz*x9&~~xpCe^S&9XFBK?Q=W^(j2$xP0zZD*u6*4EZmCv55Nv?>It#k$Y_s@X}N`xt5*5Q)E0 z@bN>fznwaKzOyUMWy-tAmJx~im-Y2Kl>zn8QI|sgN<2KdYs1reAC_aPq=A#z+}odZ zFNl|`{aWL8#QD%n{`VsUKwBLv?MP`Q5wD8DZb)0g65+-`d6qcf&FwX|K;!b|JwrgZ z)H$UW`++FUn#L||p^~?{cw8xT1f-BXI2TqA=$)e|3=0J3%BlcPm&;1qdgD$xCPkoB*;2KPOdQLjl~12O)d1@1l2;o6&|4x1-?uDrC>L4zpYH%6 z;K?xjuY)-|2Z30~W(j_mpenH>PCujqKyk^+(o+9fuv%up3tql4x*C@3NbvO7)MBAT z;P6FJi^cJUHplyLb!7|n|+!zn{ z+H4D=*6T zG9WYA4EXQlMfYsxn!e@HQo*(Q9sWOIk^JknXIfHUvb|PL%73(wC)4iurkXS#%O2?$ z3e0)mmCBNLuJWga;^I%-xhiEfH?5bqgcx~q)B02$&1_9#=Kc27sVVAD3-RNQr=@1E zP+DAP8fsR{IibY#r{G~DCk2HZHC{8*BGW2Gr#H3PK5=&lI&s?#*X&y^|7sQW|Fy5M zdpxfd6myl?qJQpL&7%HkyGQgT)uPYaE&83Hm~YG$m44Nt=j;}pRxLVJDOx#oD>tLc zWhx&iiivDf-9lqPHR&-l={PCXJ*c{o$9akxO*#5SqO5x?$ z`BZFTd{?POeS4HNG|VJt+YPuuIR;=bhJ|!2Q@Bq~d zuYKj^*frm{Ze?;MTR6Ztg)qi`nipI$y&0?M>xnIySj9t(QViqtvF5@>lRIAgNt@Ww z5B6LcZC3k@DvblktnKkc8FZ=_$Qv+4I=`{&(LnJfF2C+b45g*h~s zLkznRT+A?9jB>eA7lPG%g(hFH%A^ay(`fU!K^KB%#?WSzMsqF%2MEDlV=e^Mj1z(} zs!h2NJV+?+Gvz|?DWUk-kPAU0^MqoadNVErJ%5Cx(~JwjK|-?Mj0?fLgybzV%PM{+ zB)^(jKhjS~&Y4*#GfhaQtSACyQ?!gu-QUXNW>iLx7yAQ)jtfCIA?YyVLQrpZsMmHO z_=He=Xv&4)0YY)FDHnoj#%#VyQFkHOPYCu{aUsIK?i``PpbNoMX!BU4g0c(2D!xFI z&wsU661pyg{*vjkNDXxt0<$~1Jlx_hADLg=sd@6vY<^j(N72qVO0 zn9cg`fsPBY09?WsG4He&>$?YfF2n+`l7|>6TBur`%I`4H$Uep>gfaHgkm~O+P|f{( z#C(oB?1$V()e`v*19fz=pSSsmex|eO=XodIWu=()AG1Xy zaE8OcVc;-u7_xB~I1C&H4nrUg1BZdbz+rHVycZ4whk?VuVc;-u7&r{czH%5++k uhoQSYD7SYQvW>R~?WWsePG0L128*@!3zv*}W{C!@p=XqYA_v?8+m-pxOdfuNjch?gTuqGG;0zoiN z=rht7vuTR*QjaZw?w1C+Xa@{dQ94M9mnqV@QiRiQ(IAk@t4)*1MW|>=!QC;(y<$AV z17e7NQT`wzk!TWp1s{#`i|{uIj|wcDv(c0mvN33TY;4hNad;>|+}&lzv1-c;?frX4 z4$G{7kc%>x8(qGn{}`sW-=&415us!KaWDlma!H85wf6gmH^MgDxk>t+hb^K`oFYaZ zQhf34B+AcWvhFoCJITXHu{^79xu{EH`mT@4>}*?8)AWiq*X^Y;_Zmmy(!N$~*DkRc z2cz^`ynA(Zb=!|_>j2=7wO3{96jy^j8yLVB4gor61`M8ybVI_z;t{2s)Hy6lKGL`> zyP>IRd?IdapTH0B@&3qjtC=wwUwL-z+BG*0`T5>v!)-lNQOcd2oh4iM$OwDH5#_X} z$ZZ|E(Dc_Ez1}{LE5`bkmzPt~pYE|{A-0QFszakZ-bzUF?V~jngMADJgDn<|Er9qK zi!YQn>B`W9{KxU@%k@K#`VX854my8+#Q3?WU7lo2y%R2HpM`*?d+wxpqDt^GOzyxy zE{5-%s6ZMQai*e>ZMGciYGh1oY^>JX0;?CEzn(aeFjscPWtFk@y@N&JEOsE!XFwxc z-PqVT+0oHa)XX*2scs<$JQ-S}Su2vb^*Ivz9SvUb6__4FPoT7;Eg#gh{(R#WUezP} zruy)^clCR>b^OUD^_QK#C2!y7eGux_0^PY^9f1W0`ib z4Ba4eI!-I9rKqLwq=dZZ=+&=#eADnckyye!pU;otSx@Gn%%l&&Fn3O&jEc@VYD>t! zmo&X(lEG|xpLKILGqKuz0Ls&p=3xPimO*!!=wY_jU#VdR(3T!%qGhmYOs1w&GSz^w z6)U)dmx*L+8No%*{>=jyMHu@Up)bE-k}ZW0NQbx%5ApCjT^Q#WKW_KwTnEj;zb)+vSB*49=95UTJx{$Kg+2^+h4@O=jAWLb70N~DmO()lRben;M{ z;mHhYio--JB{Y)mh_*O4yP7O-o>o0mm9(C8jS_I!ZacdwbdQ>)!gn2*+z-X?uIL{= zczUSCNIC5`%7I9K7(z@)*a*;I)>KxH9iq*U%GCHE=arq`)G8B*vKP0O9MJjb#yZ8i zqH5)tD|qn01&70x$_~CaY%{?wHEA5WKr(t;u;eZQ0|URX`dK)ZF@zhrn=jL<2k##X z%^~N4Sz919JDv4pJujjM1W_IGD^M!;18jr&$d+qXj|E#&X>q_mPL-lSH!WoL0g1?Qxm$+x-q%xBHR5t@qKXyH{#bDSH--k+P%y zRIR$4wYu&~dBA6Ue7rqya$^dyxXwqZ-S-gnGmht!#3u;PrBXuic>J@*>d6<(jODJP zYFcsEse)oZbBW)jl&f5R+t85ktZ!S*q>hfxDT)Q;k$AIR1PUA-QI@+r4vj|v{3u~^ zPUli@)*W=tkzHPU;TxF8LBSd`rLc6R9oa}17njndM4*D>OfgHa`h0&7scUn;F}oou zoV%4CXd8v?vIqx7(_}vbQI6(id|M@yJT7925ej`gk8$SHEs}}gX?a$M-a!0^Vsp*B!~?ULoZ3#*bOQLhl@-QZVj))M!T$;l2^p5#Yl}7qgdGI(%{Q<*=&GKf zpSh{54VD{3cA)N%v+q-C&GsGo#}CHPfx%$6Hw0u}Abi2(G$%MKu`Wa@(KUUo!51N+ za#`UTg0J7)$+AB1g_@A|tjiDVwR!p5gJEySv9lSe>CVY-Bl6eWU4l9_1*pB_bMrEF z_UH?xbIB%1h|vUGLLK%h47gvM?{9vScCRVzQrKAWTrvX*q3OS6r_zoJ0j3fkz4*xb ze9aPAY$GnBU`G+mJHfsgzu%vLs`lKu`mWihvKx)^<#;{`_BHHhX%aXMIYMN+lFhbJ zpRW2{v15pDd~h@gQ3n7TJ0ZtD zoLZ!at$by8T{S9OHU!^d2c9qo&9j1i7u?LGDUoU$d zLL-D&PtyZ5y$sY54dvxH42-VK48p6!;SZObxBQfl_rE_#`l%5&=l?GvJ6Z9f&niR@LN4ev zi!j#9jB}(1YBoc6bhT1p9J2_z{=dlJCNhYGc;hL!|Lt@9)!~&!y>a!5_b7s;CYHF+XlU6XAjF z6$k_u@0$2LRg753!~)M&W=B@8rw}Hie@bnhlb4ch0>t_uMmc?wmR2Gv_3lnHX@fUt|XWfD3s~ z7X<(i*gu<v2SO#v&S?5r9a`0*L^KlyogTn$y?y9ddG?xek-GY&D3r)vz?sdCI^%tbhy+7BE z7B4-ep_J!^Mn)S1kb6rTLZ7BivRh2N^uERFN{%>er8&*(4~xrZSZw!sGm3xYS@n90 zPx~EZy(M<38d=ALhk=t;rxi+;1AM5P*=^s9IQV>{YqkU0+nzG%csl>g@Y%fA!$Xot zZefy1)(ffhMvY%Gl*C7h(muyrV`Y00@T^(KH8B@JP*30aD@ffLcILv)K<|7;zupe3 z>Q)Hqyv@=l>kC;*T~qO}lIc-jvU3*_Mn4S5qvoZBA(gJ^+xbF@EtgkhSjls>O|9~Z z;G$QY5?uEAfV8b00B^LuFg%ahYi$A+9P!GLBn3imkAe-(eZ1rXue5a{2O?^^M?GdB zWtb5WrLH(PuOtYp!{Tp=oWKjKCg;MVzP>0?3Ue@F4Fiw4#NoA>~HFt ztYpsgFu?{S?jwyFU0l>ao1pJcvob6CZp}lDc{MXLGLQ6JG(dr|YQ8{mXA?nTbW%Ar zF@xV7AimChT~su*qpbZ2eYCrIbUU!A9q0HJJC255UdpLfMSkvy&Y>7I=1tp=IJ9{i z>2GEWAe3Zr7qxj7K%ZQEHW zp*UhF&Z$zmFYnk-yKjWJ!3V$>*)Vac3D7s8$#_ND<+d30N*&cX^$qK*a)5Eub2~R5 zcCnI|_-nKvq)d5T#KE-SiT7I$VRWsBB3IilisnZ?y>SfLhbvvBZhFV^H9Xjc>wMQeaH|dg<*}Cb@oiAxt-qLUiNSIcc(_?x z?6b>sBR8xo6>1!%_Cc_&M$ZndR{2=F%d_csUKvi1cuH0)4V;G-{*)T}-gt)IYiQ@c z4Iy+I+uV??O#l^Q-t5=qRQVAJ5hoQcuSAIPEAk8hJT;o7ZkJk03Y_0cSIka*27Z0t9s zAsH{%C?>SVk&Q>qCC8V(TUTj;$Pa05qeDV=0I5)WZ!$WJuqNx%81<%hz^1a28CI?( z)9>%R=RKTBsa65-k=j9PsN7;u$#E_=z)1pW}*e z%#WOf?o$Yf#aO4qccPatAk<_8C4P1-QyI{XiqZyPIC6(Tr1&`-C%}N1dYRTH@Mh%U zi3U{;f{*MNo>+;&nucF3!Tw6MV!($(n9%@TiVp!!@P7z}5r$(MhL3!iQfy;QuY2d| z+|n0@`$12LzlyVK=YJPrM9>%z&gM^0CN0%hWgu`zz*Vw8ceDy1h~j9&%L$SVs)!rd zOOfQl=G9p!17hM(>_^7046f0Bhq%aWiiF*R5r>yUK07CGI{}K+qDY-jE^q_s*g})e zRCzg$+Rsb8Kz9}%p3-C_3XBAHzv)v6xqE7lK?C}Od#dw^a@?-%Y6K{zTaQM53j!4L zMUfImWQ;p1)EpGE`zyqNff>EDU$OH!>9G|ura3`JS7D_iNEBq;E?-S*E%1$KFDmHX zdy`gbn=D!gi3vi&+b+4hiGZv=9Ij?|wXQb#t57MKozp=p!s)5sz{)|Bi)$#Y=)I#> z*(0*_6D3M_cNB!^TEN4coLN%jke@l{aBOc>h%tm3n{TpX1Ot&~2;he4TBzqIGo*!e z{`9@A^r?B~EiwO&I^UM)EB14qaGn8;R5vyCQv)=$R?9CQZ3npCJGj_yICPb!2ov5l zKj6;H5*Pq&I_>T~{c+BD4LmeOuQXI}vMO5_uN!6(^2_@Y4o#3AMz!2QOhj$|hysJD zS_Dx-{m>IBHo{ss@g};a!yHQV>ASI%3`J9AhWgxmqo%^rrNKN@0z3Hmw|w70MS!&I zUS=oAPh8>>zO?>5k?Adf_YdIREL#0DZMWND_KRj{QZ_|sMwk*@g5PxQ zZUb<_{=XET$Rj(mQv5?J*6&odQBpTAx{k*^&#)XN-+A}7^GoO|)2<0R7~ba^NrP+O zwCK;huC*FCmV+TEpJ}iP;e0>x?>}$I2hlBzFHinB*U1!gb6S=0&O-->#mij@*yFYB z1z@vhUpM}&I(*`JU7cLwG_I)n;k5oPrlo5`ZkDvObNxf0fO4)<7ejYc=U!am&TAOf zgcRJCm3>{gcVU7=-hE8rQ0`n`IloTCwlY0S+M4&3#nr5MHkb4=?}fHZ^od8*VVxNX zUwq4wpz%t0PrlnAvB3D)s`sI3`*U&Qxs=ftP(4S|#|QKzdEk1hKhf5NxgqNB!;!<^ zucmSRIp6+35yL>Vzv6?MA*ppSZpVi_+WuSaNI*jL8_E_(n_N!t>E(n6JC2n+waI!| z+!}JDJttnj6()(M2ri2F-w8wiGEJG?C6qa6_k9^#*Pv#wgNGBUM2&@wWFGcibE!5(YZR)a@^gZAx>%Xs9+tRmxsJ@$2S4ah!M!<+NP$x-?3x#lkv4<8S9Z-Y<1LA<-*pIylzUKrvXRC&nOBzV_eBk>s zt!EnD)pW!6Tcd;hF#}VZg*O6Pr!|ubQ<(+K@cXqplvUWf$mi3awJbbp$VzD~^zdPXZln{vxTLp&X}^>d)_4GDgdYFD5&Qq3{r7qH zXL>WhZ=0I|xblIz<776#ISy)!5zl5sI3|kT{}B#5wTFUz1enw7vIu!L8HxhHZ@I6@ z1}uoNAkz2W%iv23A-RmW(rbU};}PirgG?AN@|(}&Li}xIZ$L(R=;g#bA-bnWs;sgdnq-DqZIaI7blK;D#>>y?7`P;lS^nvkGa%>FE zaO?t~hIfvCE#72B&qv0w$zD#%$-LujvqiD2XKc>E!Kw>u&HYrt#fu{8X}qp^E%hOR zI!Oh$oPP#6WYf-_5hC7Z@zx5u_X3a#OB)LN$~%!y4PSun;f5uathC1Y3Wd;qIk?<5 zu^RL4W}L(9%{n(hJwAt2x46Ymlw=Rq$dB88rs1X2kE(OQZ_*v_8CanIjAzj&mDaFV e|7B1E0GvXsuHkM{Tv6aZ&qzHJ-6|b)^#1`J2D5ko literal 7497 zcmcIpXIK+WwBAsq6Qnma@&Sep^3g#d2qK*>`Q{=M@&`y`pn?3p=p_MG>9H{RUTfQ?y@82|vbtA;2`0D$rT zy-qPeJr5r*Btai^0lHVwr=S*b>NXzw&E#un8vp?8AOBvkG%0o=s8b+N|9YU6k85D? zO@9|4I5=3=!`m~!`KGUntdGB2_KKz;00?(nMO{XReM6jRacsJLA3PF z+wYFLROjj&bc#xJlA{buvyv|*)t@}EUEPnp|KQqFQhASkOWyqC`Z?Ry?Up+m{Wn_Q zMXhq2Mf`uXSN@172>`%iv8&1*5omCLGlc=nWu6EDIsyJw|2-2zn75VHSMQAJ+t~gb zwV0Ka6(5*89jEVwUTyfWGj&H49;y%4l`N*Ww6vH|iP)u}3*ulVQs*9lAZ2?T+?7efoiEOazqVSoCU$Zf+-p_|5llfTAeg6<62EeU@KWo~0rH&9cqQ@Xec z{6VG}9l>$ScV*EpG@I|mcTN|VF6sgSu$(EE-xl^d2Xn;=3JJ*(v4=xZ^9mCn zP==bBL?PB0ffvy<<_rEu%ta*9u<;Ac{X1}yEhu*-WhC9=yD6|{3m$pN^N-v~@_2_m zj6ox}AB@LatOrz(S-I1D^k>)sw|bt3P3IAi$>;=%jdV{#YeU0<#?nyL_++pnx+zIW z;!)?cjd7NuO98%|A@Q1;Aj`zH!`;=BDvbPI1x8-={p$S^Rka(KbU7#O3!=$&Ws9c) zX^8<7eUEbd2ogb+?m76AqZTj{Hl_bhcZ%KX$B!SA$C|@WDq34lq<{`pKU3s|PMW>T z!NI%5MJCxh!zS7PR_tn=L$cF5y?;w2zQ{2xjxHmN1BurPHK_cXb1C;kM107ElBL=vhGW*(wt82)R99Z0~2&-L@tAJ-M1A7 z9_r=?{OOPTle#HvY+ztuqMjRd32gHgL%GZz5q=6V$IVG_#q&f-=#BL(Fc%dRID0`O zxPU|!mojTG!!~{P;0q&C+mIwR_7Y zEsM_dSu-lCNxFeSgI^Z5l415Mm$lKyE39$4D*L%~q0Zm_uCSGLB2{!oH|;WdTf3*m z^)UAo$>$@@>BM$fhEKmukm$=aK>DUu)`p5#jE9ep4n5gy!qGsP;I%q%TQQj~_Pt>1 z>2tfRb{H>e3m+N95s=>D(CXb_BKXZ(4ND|Go=u12uqJT##^mcFR4uuzmdAJH54Yy3Sa zv&R1o`P<-NAFXdrGKtfKBTkp^Fqckk9us3RXttkq3J$y(Fb^%I!SP608~Pbr7(9t#$>~lwq!>&Ln`(?#srzRxTz{jddUpztd2)3NBk^)9`R0y zEYo#+qf^!YWTCOhEHCOtbo5CiVrmxK6ArVdo4Cs1{PY@wPfF#wGRsDrXGdmjYS)Y+ zYc1)hr^|j(nb#-+bLbn49nV3KssjRG&DG;f5?Ry>tI-Hsq z5S>x}rO4r&lGqCcv(WosCu9O%^SPl|RNnGB?dCbhyRWiu+kWp7?Kq#o4<0=kzxt#O zt^8ss^R^=C3K(v0=%#vRN1m7T5l=z4rk?fL-rcS0bIVZ>ukzbBAM|i+N9^)biDsJ` zOdHRdLpSGCHUvrt8abw}stErEn`(*{i12@9R*eM)5-BW%10+ra5XV#2fufSdhm%Jd z{AuJIzYc+E6|Wy}G?J@V12nTI{q&;+N@#qWIu(Y1>n+hI$9XdL^=t1Y+3$FG2sqi; z1ZNQ(IVSG-K1y&_%<8oTqH;Sw)#miO(4AR>7W$f*(a~!|*AiTHzjNnCR^R(XUYRf! zavLD(?c-C2-u5J@caCyP#bT)^v}(MW=8_lnz~-DP=f>}f7~orjupVp#9*+{Nm63hO z^02MTP)w1J%m(bJK6jw!N(p_BSxXXr06FThCzFUnZs$6Mo`LZOZ^NtuJWJ@&6(2sh z%+~GC?pyiB;sK&K8xcyyQ+m`)^@9fwPS>dYs43T&JgR&lE$>1mH=f_-$?cTr|cg=`X_Txn{DluAwmEq(pBAKx03akREwkX-L*65RM;PJYe z0rI9dPo@(Jk)q+Ct^%ADd~{q?QL*d|-*3wHGorWx$q9yaSZX+6%$1Vpi#g1K&kv(! zG=cf}nIMI1U@a5x1*K;Zcvd-qFOd?>=%iE<;?2!(2l@m|HW`#uR2+>*Mn+ly)6ICB z*LM>s^IY*C^;G@Atw_jm+|IqDgs`+WzhGc*9ttmJM)8e@4I2EcQRb)>M&I~0zta2l ze&N-xN=rRrS?0Aqu&Io^u>xD?^~(|O^icg*#kdTX=4k-KLQ6SuuJ|(j8ArzH7U-!~ zR?0HgesDgq2Qz|YV`F$DkMgs*SgtIWi>vTN0Lw!WI3 zXyVFk%#+U+5Qe;eyvcWaOKVeUpC#bFz1BqQS4yYmfVn$3I2cMi%MN^)Tw5X>7<89}X+Elzhnk@8G;%F@IC zExCjsH@dEvW&98E=+A2x+mmN)>sL@MOP5AUTx=f=i$+Yw@ma(6i2peyl0M)?+k8(q z`FXa{c|=W3b@lo;FLt8zlFnqW=OGAKo8+o1XLFpcg&Y@--agO%5!!fQj9PGe0?!N6 zIb{_UAs{1Mp!RynVmuLha)FyU@>adGbdUO2P5Hx#-z`^8%pDw#pn`iwXa(2N(Q!53 z+?@xf&kp%-dh1r_B`2rxG~lPO_Uao`wb^I27Q;t&$yS4%GCemKCoDh{0p~A7Y@T}( z$q=lH)eu_BrJKk|cd4L%V+5)sQncY}UBu;KBG!qPoN+Z8De7Hb)bg&jHek5iw)Vh5 zbr+tb;Ixe}Wy*wM9^fy53VQ?66_C%!;_zV=`HZ_vI5W%^!jBynz+k7v+96IeazVa$ zdu!_=EQA~va0bDfn9H%^Y>yv8Zx^AF?+zr#?SB6Fp$ge-^uyRNzRDqeVKYS?w=I)0 z+Y|YQX!c^V6}m^pq10#a;DRiGgA#tm)2^9!z{;k)9P+&PyKK$;y1L*{H@9L>8MV$w zLc5WGTSL&XqFdQw(CFwWwN}+yNKnulw)U2K)nebTBCi5(-w=7Y!^rSrLlr5SJkBuT zT0CHPS+u)w_Ct4?lu`%3j$pJ^d_d#PyMf)$E^yS<)wOoUrv?QE(*6ATv!$3Y?mHF1 zBRwx0;NajeP_k&P@-%;AR^Q;*>?iTa-qX`l)+bjqzqz^jT(0SfHR`z7Djr0_yUH%x zv`m-ylm0dE&t!1!{aSt9OBqLf_1PUsjj5=pkPveAuET0DucS@WX`~p=)UIm*GlPhG z$P62L*H0i&nb3Y~00MC_mnQgtr<1;WnBCyIcjW+YJ3fMX_UX;Vcg_xykm z<&{cWFbFa3mv0~9fMJL#_K|(9=Td&1nKcv&8Xqq&O%3Wa;|*EW&x4RICUdJGZu0q|Ow%^~iiG2F>Nq)Gdd=S$^-<|4|+JqJs zrwRF1Q~n6C!H@F>O%f?$U^72C4OskRl42yl!U`h?g1Opz8Nxp`IUnKc3{f+Jy!;4% zHWpSm=>h!%ynP#u9;)(Y%sIr+8!~5#=XDI8qAV@{zlRlNG8_L~rNdKfqj)ou@NWr= z{|573sw0!$ zTLOhzS!*DB;ja{w1qg7mKa6IY5XjibZ@oslCxv4J9O<8|tB4>O9S7c*BQf^h9o8;L zX@J{qh>iRBKq%v@Jd6tX%Xlj6>W-Zuz%)xMG?B}A_tIfbwq^<>YP#5R)_adO1nTLv34;!k z-_%MqLme=(PDHO^#sGmg5&Iig5&mg*Od!tAs`T4w)4V{;<02+T$0RXzWEG2Xka#Mo zs-WO}8QV&BcD4z$cP_x$zQ4@=NG!{^fmj>}flP?UsQ@7X-(pJ<`|B4mchz&fmw){# z5mS~(}}%j3=fKQh|YOlf?TaoK=zwSeED+p>al8DHYh@YFEz+~_ zdI=x2&&|0Ck@H}GLWgTyc3l;-I$^3img#t}h1J!QkN4(@)>Q|24y{+)LD!;~MCYXy+XuPguMbQ;j`D>r7d$ch576SAAz9sH^4q>Fx2< z&EO3S&iFBoMI+QD^VBk%Zb$AIuN2&JqH>(W5_w~(Zc>Cw3#ddshj`kW~{2&4! zM6L7jVU+KQuP-!2dFQDIEh;#EWRBC#V^}*Ucgw<~>+q>HSOrzCqX0WQ>fl2s@S=)J zc8F%wRV;S-3>b8C(nA!X={-{K231X2Pj7E+u@i`zU(yt9)kVFaUGsQ2o&J29P*&DD zara3;znWVTVQliQFw!KB=;_Xx0zwqtMw&OAm&^{F26ED-3%)oChp=p22hKa@va0H# zq7+=ZpA9kI(E81l5)Ta)$HkM5uRW`Vl#MWCmhtiD3ShNqv!BC)lxXC^ZMq#BdZs4R zSgThXxaLK5{5Sge&pcymec-~E1*oJd0mt+Ob=?s<4C-TP!QuN_c1O^Bhkh3t9&WS`R2c-<57so zZ&&s5KUEM8Op5yrX1kI*6RLpvJ@uo>pF*mOe+PcZ|xBZ@ameQUuWC>3rWrMm>t{&+>u zNK!ZigOUiQyeU_x#N&$?dzTUAr_`nv z{q9og$;(S;pZJtLC`VI-SySdUW#ke5M%vR})XMsF7St?xNh&irRP!-DVkRhtB+Z*R zyEQd(DUreEFdMxGP4H}zm4^Y>YOvd%hKFrkl0rEzS$EDS2FOD-c3QtNx#;Uc-dhN$ zxxJ2s^ns48tu1K-`a1iw$e@;TeH!0Kgv!MBTTUy>%MQ)%pI^nAWvFXtl!b+bRT>t5 z9p_B((4I9wv9FbpFM-80A+L$VyTO{G(V}EoF!v3F6_xaF-*XCurvyYu$?G-QfN~p3 zFw+e#Qmo1I`2U4EOcZ+GKhf9$_R{cPG;5Es)XqeTF))uiBRvD>&D^N9L$$QH4ZJvn z>^}aH(U_lq(;rfG)N%J3sqjY+=!=@XlsfD`4-UR_si$pdY^HY zHKAqC_>?Sm8X#r$pi(@GsG%&sE~%*|KVyRL>sH#OigCqf5{^GqW3+1#DdDj(+`uR77A$)HPyID z=9fLC_V@?L9U8siuCh1mmV*wa|L9)iW-%5pAKc{$VfHrBt;ED!;P}eI?+M(`F}m+A z3Hii#JC4sWbLf7gv-30wF!fbEg8-!EXQyTy8t?WExV^+uodVPaUR{Pn1YNLI3H$SH z&RwrJ(o0S-`zcE!6x4k?2meQ!o7qJ^&knMi zVo|pf2*(N#l?sBwzf*V_l0pI-Tx=oPfQ$PiF&#aXaKLB`R@pU$`29||^Ii~H{_$55 zZCq^$kIed(pqm5^YCy@#hlL?Fw%!+M9rKhgcXf5S($Hdd>iwpVc@x_m zg_p^J>XEPTYaW#G!FWpN!%8LOI$wGz9Zz@+jJ=6&ZZQ#4*n-BDT<6&qW`(Ey`+p!? z1>oS1>Ahc)8;hR|+S`GU&Q&q|<6Ft&Oy-kG__b&;o`Sj~QyjA!@OwvtsWq+V#%uo% z94#9d^mgkX(+@rosdbV^eXDdOzE@B+Kbf9ToU{KM?nQNt$$wQq_(PJW?clefyij+E z36O6$K>fR)OUu*WwDy5T$YP>Jz3_2>ynb;ogewOiL6oN-%I#F(cp?~q(kq9KX4$bd z_2maPS)S|DZd_5*GM1nBbH|yUD6s-tySq9MXv4W}#KsUjeBhBz)cmD>dEjr+_hwv6 z8X$4P$WB(J-1AP|AKwptBOE-*i|2&l2**EGg(+%`ERfC_!cQ(4J?w$cV-g++I`3V! zxx&ty>8tuj9mk_RHzm~no|_SjR7^2kV;fWSH*CA$-0M&>(dD=?8VqeQC&(~{>0#Sd zpmpm4-}oADsic>2p=B;4y7sHTaw)=(Fpzi7{r#gWnsA`Jwn+LdY=J?fP{Po^>{<6+ zA^q~k@?UAS;NK(O6g${!t^PPzUTe~aAH1H*S#MtjOK6#w;#$x`;TZY5j+d_gJFd)|~HER|SI z3KkIZn|@bRRMZD}Z%h8SD869m(N_K|5eo*nb-G_{;3vn5&P)l7AwpMn2?s8aD7owM zF32k~@~9POQc4k!+uGiC$S^9bFL0-)5Xil`8g+7^lq`+;zZ$w5yj`FMdfPesU+h6DWMbD}RvzGt zM-dY!#?P+S`i>z;;vPx+r>g9cHjdC-$kmk)n7s!3m~-u6cN|{R*3RzmGsNoo{)+!r zAZ3CSFuwzHzvJn7NW#AbRKI7vExEr;yEceI`0*jjml;JatV(6(Lj=TH(7B&8WnA4) z8)+*9ZvVcmxbN_$o>LepPmYl=hBmGlhG=jwno)kNi{E=f%xCNXkcOwsp%50I53|31hTfzw$lQ1< z$u`js!8B{<-aderCUfSBw8R>-xLZ}(9lNuf^~I1z^-pVR{?x2djKD<*VTb@L5G4CE zmnhc~Qi!D{qGoQ@O+S_L62AU=uV8F!tdoePTBn4&TX<{k>*qlsqJx9vp?6RnlSu8d z7em+p^}^(KL}z&ry*VAALY6$$D4nQ5W?)OEEDlDI_{pH*$BjC z;4-`#9h88FG%-J5Ok=IH&n`3YET<$vKZl6jow#H+6UNsji5n#xq~n{|Ca{1O;^|v# sc&dVUnxU8@pmB5>{{PZG<4!ml(c)=r6Z+QBYfs>+zA36w7jy4_091bf?EnA( diff --git a/dist/icons/overlay/controller_single_joycon_right_dark.png b/dist/icons/overlay/controller_single_joycon_right_dark.png index afa80e6ef0f54acd62711c65abab9b9e7ad204a9..81cb94a1dcbb2ead1ad8c5049cdc08e24e564621 100644 GIT binary patch literal 3406 zcmaJ^cTkf{*AEgPv_L{J^eSE?>VqgEAc!;r0hA^P$`h5KMA`+U2|}a@QdEjh5QqrG zFEkO*Borlta=~2SiZqR2!WE>bAXQX+<6qx=^Ui!bXLry3&hE~c**(8AC-bDMqpXyw z6buHF#htKohrvWJzdHghMD}LrNCRE`I(cf?K#X$fv?Y9^ z+pVlKJ+*hxP~l1EiX}_ej_ON_MM=b@$fE5BCjv)tl3eo>73Y}nnhF~q^QWf8ZB|tp zQugm|M0}A}&!wl0WbNY4B_Q=1;nR~qN#9e++@4`ODpjE(JlIQ;Xe ztqo8*f2%Ons~ULDvMJYw_T}`9cJ?#z!DU|Z`0gw-Pany9nNCc3e4L}?_gSK%RjoEA zffu-4&E{2UL)FpXVH6=gn+%>G$zA&^P$OhFG`_a>Bay;ed=p>;NXE-f-+D1;D@DeQ z;{+r=QK;}RSbZyHJk1)F?Xf>L?t#bPN%`yxZ!^P`Xrl!9G>>sf`Ne@=Lj*1TnLM@M zrS>O+Mw{H2kH#}w$zbr6v&*{^bHU)x4$D=%X7d3O5Fo%egGTD9j(~E#?AMv2Q{g+) z7@#9*l!FR!%8I>&ot|qy$75@Q!7j`hC zn&iMqPcyeuQ?D#I(yN%r`pJinKaPh!<=`*-E)bckZE|2zc-PmG&+1(5T}m_r1K0?5 zJS@^wA6PfYe>H2BtRhJMpyzS>FYd$mAO`&!8zM4jyfGKI7|(r_xF9r` zIBm~CNXEl-1npucN8viQ9uk2n+i(7SBI3<1VoKSjG>RmtFsr3<*!MnL<%UXXh!Yav=1RyjlK!eFAAY(bFqFr0S$z>d zFPMl8%Sk)k8%n-~dPD_+gg=sZ40zI0yT_F>_Y_irkhMz7y!@Rf>#kyVMr`sm$aE`F zDHj3syi5g>(AUMzCOPa}K;zd~r%Fs5gTxK?-{E_V-}=EgE}EnZ?YWREKiipOB5KlP zmrer1&@Z3HDBz!7ri*YpDJhg=x*U~;Lrz%!pJe(0BIk_el{+dBC3u`*P*2Fvfrf5I zvRDU16;52-M68cCXuWT~p#!ydvA_v#2hR{iklc{V0iz$NV#r5)OQabCONmgS9ulxj z3(nyP+;t94N0aI1Ghz|0j|XIAh7=-kzGey$TjD0CA6z|h3>bNI^l;ruq;5hGLi}UK zN%J(7a6d9fZsDH4g^?x$bfk6s#{XGG}lsJ-6{A(pfDMCWXU6HIhAnTv3+& zL(S~^)1+4F)Z{T>JE}3&oDaB?AN9tTkX&2Vos&x ziJ`{PdkuC#Qj+LM9UA5B;0w0}>}!R7@x918kkij>mOu;C#*js}^a2`t)MGJ1{mGhD zuC2&@Fou_T3dWYtwiTW+v9ruawy-KOgy+1_<6Il}2 z&1sQ^wwcH{{K#B_+Dvastn#$GxGm7avLkkeiV(Gty>=+0zf+7(;uU0KprJ;=);k#2 z3p!p)P`Os3i6KwCZ4?YdBZ=zkMHs?)-hDKN{Lgo+(d4-?@Xeiks9cg!DVh%Uh_5(U zWu3ox_QnxAbn5c*4ktZKZ(QS$=Q628%lox4^j4n$iG^HF*1&noIA7B>#o_OM zA-F37jPkKs#EeB#YJnn`WR=_ZVFeU1?88;B$V?4!p*0ib&61Gv3JZ$$VK0~ zjk(FX!aMDoQa!N{Yks#z?<(ZChd479L1|A{$;|9FT+FIQ$#mY6o<42f0cze{X?Wq3 zp*4ENbdb1dCDXt#OBHXs;j`>0y78~U=lEouwgaE}894^G@18Mu?WaT`&t>$fkG@ki z*28>!RGmh9A?H|E6SnKy;@yd*$Gh#6i@CI|JGvKuV>S9R3Vc$5J@8EBL0+-iYI#FU z*OxqG!otU!QwwTVS3-vzo3HmhCQ1;o-bHqNrveAyvqJ8xm9Ne(zfg8@eV+}tMeh** zcq?w#RWr;9X&Lsl(3yL8qcm3{-rW#M>N$@#QcuYhvOIp!CFo8g_uy9a;Z0!s$1mnAsaJiKsCCBjJ#LI-^pfTUApJ8bD=d z9pd2uc>epE{v|j&Di_?R=zPg;KY31_{o^L_rB{uJkh`ir5n1zw?Ilr;Nq9;%)9qfFk*8y<{SCO12r7zGZX@Mrw4xkU@tGQ?gDiyD;x0u1Lr1Auo z>Ul4h_WA2U1&^rD&ziPx(nZL8lnYr0^MB-90PO<8;OU!0n8|I>opf6ce7+lcy~UOQ zz8JZyCRuZPHIt1u{>;GD^;*Id${YZCbeRO$oF?2WC)WXYUOt2muKO$kjXH!(BlDdq zjPGpTj!OpJ=Z-8_EFGJ?>rVIj@rQNB)$j~l`G>B1t6pR}>v14F-MfL_xu*hyV@@*r0q8drKk+bo;NV^ofp?$YJ@ zcWW4U?+34;2x-e}_GJ1S2IM`i^&#lhmpJD}Q-rkS_eZ?NEsYrHFYya;getL>VydJ8 z`K456zsbtN;kI3cBYCaP7m2EgxfXbw7GWP@^o8+RN8tJfh9>X5*JH?(%>F~2>_9O7 zHL#jJ3r_Z;JJGMft0U*io_`GCurDbWHS@aoeDm=p4Wi7x4$n@1BYaTqFx7H`uqBA% z?x|h(Fe(41H=6eggGUV#jPTmn_ED-ncTQ<$_SeK-y7lL-PzX2vqHusRWhXbRwq~Cg z@M7dy7d1>3#l4S);(#4}^T*Vlb0lIV5A(zS1rDpG8c%IiX_(3XE^g%i4Qua&iTzid bBLq5>mpCCPtNrJre_L_(u6FguLGph9G1w%t literal 6729 zcmcJUc{r49)WB!PzC=YP70MFXYh>Rl%GgOlgDGQViLs6>G32!sA(^qHvXq2mH$#rmIrnnzxzBmd`Q2yI9qlje6Fwpg0)h6y ztt^~DAn@V8w;(?tNzGci2RwMg&%<2=fh$fBl?J>EVXWN4K_IdBe{XQUn%Gf5C>>$x z7I8TQ9f7?b<_p4Nv6}wDf#E*aF}|81VSYs{<0C+yKDfm>m*`?9E&icrWc<%HHnx;- z*CHqJc)q}a3O707v;)_A;1I>e{`+4H?H7d2O1jJ^P7No!E)<#L=9^5|l9!ZHSo%Va1hg zv0_^~7{LcNrzjU^vG=JLNGA8GCFtKl)~9_*qI?;FK0ZE|Kx&jAAxs+a5-#laeS(be z&T+>b!ZC21zT?V$)~*jDL5>c-a}yo<1W``*^*{=zyzL=zrnxS&zV3Z9(pd2;H~kv4 zkt?2wb4NzbXp;rDaJ=l6;8$27%mHWK472Oz2YwsX-(`gP*Y=Q^M{k8aZJJ#AJ zXx+jHG}_^=olVTSfEJ~>xw(-qU%vEzZ3+wtXI~+uzi8cPPsVlg$YR*kR*u(GiCoqr zYuqVG;gD;@D5fE-8j@OZ;6hT&iA{rNdl?P(L3WFG z%Z(!9GIexyn-gkWIgbhP2M-mT!zyAjiP-{iO!IcDOWnLF=dZUBY9iS_dDd1jGy5^J zt1?)+Uc{M$u2Xy`OR1$y)&!(*Hji*je453&lwxo3*8jFxrJMRt^{0S>HqMg2qGJ3H zB@&y)?L%x@9#l>25)b%tJ>i8g!W+e8k!tw>GnzoZ9ci(I8L<9;WBhq0MaWJ=o zDQsKsHk};v^GeRE_*WuDbvCKZYT8X6OrCM#!*mqW9BBz?a=#`*>F@79kKHrxN1XcA_N(76aB->PsTEGMxY;BGC5Il&>Dw19cEIGc6Dzv`-E?!FH z9;yFp< zR@liVm_FF7eXOb-e_Xz^?fw@U-Mz}C*6vq%g|ok>W`U8ITj}@P0>d~`4a;=dPytIH zpw&T4A9{LR^CuRY4v)`_GcoLNwfv7RM;5%A0zOf3Sf_-8O3W$fRxOdf$nqm{;H*21 z#O`-AEuK5z4j3kD^xde(Ic|-0Mc5td1^y=p&AmX=(r!V0qd|R>@4?7J80e}IBl~vt z^DZt0u^vR$F=cL@(iWN)*-^%THa|`5?dwEqua2r{pT-0WFT_7yT3YHJ{1N8om{8~A zcnf3-LYTaJxtK}g`jRY3H{XWJ@$HJL5GJ$M1EFo4b0j5Fx)3>EHn&e>^vaL0wyNh6 zC545BF``=tsrvTuGLjQo_p%ujL|M6NF1zKN6q>+u>^eFRCRv~n`cuKSS^$WM(8DlJ z*yuu#{LJy8UVTg7 zGR)?O|CNHk=Re-oj4R_{_eFNpHm6jjwbP{z!-%jC>u1%n=FXn@mp>}JXwI&5LI?hId|CP1rOvj7j%SCJ-u%das{$6LYb}tRePTy=>o#g%Z@i$g# z<9@8*mMtW-LO{s_`Aj7MYhrVWva+>5lm~`@G-K*@Wqtij z7paBDy)-WJfkx`1^-}tVVscgp;p5C6W!|73-5g_dbI%oiw2^+B9 zw)W6~c^3}+s6}){hkD4Yin|RgNkyflXiAE?>TE78o@PEMONM^_W59x=_k{N2KG$+J`o5$Ji?hOK^5ux36jH zfc^fC^KYx8-Q7s^?t*V`T z3Q~Kk^3ywA4QS(xl}9B6*800*ChYp3Ac0m6*!)lv;(=~T;rD#jYvtD;X%kL#(yk_> zM)r4E7mh3V+NO1*e4>gZNP2od+v-_FrlNe66YfCH_zO~+Re%f9Lh-DswtVi9Lx&H* z?+f2cE5FEh(Zab5qVT`FwO5ghWemXw;I91;*uJ+%l#`uwmQ__BD_DzN6_r~Bj{;ec}32f>dTGuYVffI`pIl7NT z<=cJ8is?Mp?pE2Y0+6`T+;sfs1i{hVI#8W!dFI}fPxd|H7L4|`X52;p z)^X&mwPqBLmcHpqGVqOv*m&Z~ukPIyXNJ_Nh~w`uKkLO_&Fz>c1B%vhvL7f zWN2ziW!l}sn3Q@&QY`NyA}Gb8yuw2WBVb$fljh}_p_&l{8~ zHw8f9wTU|KggoM&f7;IWdo%oG$eRql9m6uW(F?h8;(fS%q|8KgFwQFkO$V4?yq!}+ zih)fH(CAiy4)<@&oAjqq;%(tC|ID z{gv16<^g-3oLy&ih1N>6a=OfA-IiEJ4gmjcKmz>}>gvADZjB zC16jys-11?;2_u1@Gr!%#`WjXBS#FSTI&Y&jbe^x&CNx*B0xZnm2r}k;n6uOa2?HU z7CSNiucOJEN6f}6$Z0Njuo;2Jp25DRHz>ejLz+C|%LzWp%F1$4?1n;vX7P|5 zSushyDy@S{5==>asjF)KBQJ^4;^GdWEw7{X&0*{!>k4O^g%>WWbl%`)#o%n=E65Gg zdqK}jAY&|)SKDA{N!CxD;JVPan=n^#=}CJdzWEtlA7;c1N|QAcJZ(VUoIG|9+znOv z9gO?ex*(tpaLdXlNN=d|g2HmihaX{8z07w8ct^f@l6h!_O;b4EnCtRgfOBv9A)xP> zmZ`iMoXY=N5J<`^+Gin|CIp6kJT!LP>NA)YAL4>U2`~{o#cnE+Vz+!rfCrh9GIVd; z3JmJaIHbYMaRKD~j|L+VrGq^n^+ zCX-tRx`CtxxYCqv&=-u_ruX)_X6R~9V{$78GCTM8yBhy+gM7V2yJFNi-3i(6L=4D z4^E!Xl!^6r<|4gB)u-NIonYNiR@Vqc^Zv8Nj1M0^OttLX_JRDNv38*pn(Dd|Ix6G0 zZQ&WW#giA6 zNC8-HG}_FLQO>#}wpvv_q|80_-lsP;py{g+>`=k2{DB|dAhHu(%iv1<7Cz<@2cp-; zi8VCIA^x0aUF9jA|HHJmY?gRND!xsNoG|%9!2yawVVt~13C26}IBfFJRv~e(pOzgM z`I<-#XXlSkJnU3hTPfG_^+X1rDj3jfJoCgwl}nsi|TxWx|ON@7AxHjO|)v3ampxib=n>XbB;yha_1yI}wd*EaGY1p>Te;oGSfX+zs>8F8)t2feTV*gdQHGaB0H z-*0ru`)&;I!DNrm5w4UAT6BpHhmZf z%>rWMq^o!)R7*BvZcEMlGP%VjTw8ugaP1lG8nA_Wk2F2ElS*V2JZ@CJkoY76s4a^89_RPTcfz!n@0gu zVvb)kT>*D=t#!+f&YhRSYe<2~skY{M9r8<#dFcP0*~j*UYv*Rs!E`W-cQ-GlC`BQ$ zY#5q}vf!)S3j&#f5kY9`RKXNY{N_%_94ksbd_>PZp-o(Wl%k2RY6amZdC*aYn~*$) zYUBaWo{enfMBhP{EE0XSowI+Hx>Tu@YwR-(-?mPI~hV09bKLpci(k-%|51gN((P0j(y$MiaSFBfg^ z`SJ#O>{eE%bhIG8=}(Ilv3*B^9FN<)@r+OL7T6SsKhQv#dQH{nHh37Qh_+nTbHgsQM#`H4|w) zPJ4(Nj*P_l`*=OAxt(SjorldV;4zC}XSZ@{VKTF^)+Sth(Zl47unuPGUqD2cJbz2POgB+dsbD4O>d9JlP!aD8KVetIKphVKU*ay}VB6 zhpGHK0RJr^B*|+gPxJN@dT9TRL{~x-Xja_?@y9dfTA3!1+bQ$N_R*66+EV52Y#AET zuRee7FIKs1EHZt>P-qFE1E5OLWH(SS4f z+=eC+e)8X0EX|;E~X(BzeuGj?Xb^WOGBgbqfT4yL5{2_9a_20(%L|0LD zyzI$gA*(k)(xRNC4wD}(>K@bS(K_$i*wnN*`}@w z+ctT4@`!#%+~aNdN_hzs>%I?u*&w=Bm+%;ugle9_-w_{?AozoJ0W#vJ9EH2csHhwCK)Tj>c@F#t+mji7=?cFf zTI`PwVA04??Q36OUa4jQXTUesCnag}e4#i(jabRu^1z`I8K9g4rL3sAD@tSp=bXVR ztWE&xH_kRit_%+kdxHY3mEc=SuisJ{%N9X5Uy^KXZG$qNE$YnT9 z1X$(GhzkykFD{OYj)~cJW5D*}ErI4}Vf_yjmT*ZL>iH6cNBb|D>t_|gV^KKV_E<$&TU;fflMah|lg<)C!!DDMi1D=INejGghvLjLibZe*?^sJk34E$W|skK6!hp zS!fHgY7OHQ6F!|+R-`EbKb$l^K0cOj{bT3bw{O6)Vm*;wen9zDdy@ZT%xoMZ$K)@d;Q;E5C zj9ka(CD3s!+G(!g#Tpz&eLkme+PL;l*i{VsDr8Sg-4-Ug*_-MB?!FbrdcGSrT|%NJ ldcWMtNc(@e`la^d6613Y`I`V4 diff --git a/dist/icons/overlay/osk_button_backspace.png b/dist/icons/overlay/osk_button_backspace.png index 4ad284720ded558efda48766f900bc4426e3cd63..b7dc33228250bc2af38d43d24d05c2c25cd73a2b 100644 GIT binary patch delta 1263 zcmVCEmkaxCG}RWF%OnckANy6U%_W+heyPsJ_)Bnp(R%b9}bm&zm{ zQ6Re|=lFTdPv!AIqCj?A&h_)~YYqnz1>#MJnGdJ;1ri0)JbWYT z4kQWWO7<+pV}GA3cLI_GawT)V;^9-jbwFS$ox>JYT^j_(EYEUf zy@lp!$-DWie39YPipS9jqWD0rwY^vInCo4ONIXsu2_8Wd zALy6%d_wW?BG)Vyk025}f+#+a>B5&J&nZAG9zi5{1bjLV(mdfnu4K-Y#edKI-Vq}8K%{xXfn0N5C5s;vy9D(^ zqn0ATkO)n88U3ncm_{cp^1qWXQ?4M_86{=dkPgqMHo0Hpax@t?So zJbymg?lk}L2>e}3h!_Uy!`uxJl(IDZzy3OfNnvfDmDWg^RBM}A`fNOt28 zuuF?=@71vYknH#iV0I82K|h!wh!V3r zt9cGk{n9?K39|#lYx8-nPfkSPBY2ue1L^Q&LG&KG=0p@e#Ho40fn14~sHKo!+UG(P zJ_f9LG?0#KCci=-cdg?Qg$cmjZAv3Woc-RYXgBMLi;AbH{g0pGXfvwk9bwSOzc zBMLjrB6;Ei0p(77(@(^c5sdFK7jTdMEcZVkl9LcmJ z`HPomKDS632r!dr{F~Kw?WBQ#Y-!f~OCgozk_G~@b;YPKIW1`*z-}!@9)8QlBn<>` zFY%NAof(`o5MYlLM4#1qCk+J1*=X2zOqZmAT#1)^fr^{Fx#qmH08%%sc(?!(caOHu z7eM0Ln{O9D;y%Fqq5$%yGKk+5K;D$c^N#|^8@o8O0>~S#%$Ws{H)eg#Er2``r#~9m Z<4=bU9t_=IpXdMp002ovPDHLkV1my#P;~$R literal 2919 zcmV-t3z+nYP))5!Bj4mi_(@$p)C|!MDRj_Ml>qkTFcU~XXc%A zwzhlD%;SfiBMWpd^S{hhuOh?%QW%GChQv7My^sMrpWh8;V0yd_DJwE)hrouy z9LG-pc(?5=CHtnZm#`oR4kw~b0FJPorDSpnc^4K%(a``l1327rl#*#EoIpVkya&LO z01maCrDS>vbqNc@@O=Ot2T-@2rDSFtU^_sMMx(J1z{3DsjWR7}KGXO8&4yy_cZ6Zc zX@0*D0tG>E5)rKfaIj7pB3j~k-gBmMl}wNI+xRGo&LE;kp(oEPNs@d-2vN$DGgbwX zQeK!O$ewso0$isl=se=Gv~3LJ9p}QXJ(Yl1nM@w-^t$)cwM<<~Ys+c}mok=RN>u16Wk8Rv#&fM41T0%m+qMbU%ReH0#_Y zgt(Z9+J>SjF+rZ8?vJW@N%RJJdK5+?;{uJ0j0{OBA7SP*HA}7ZJnvE>>KF;8$Ufxh zk-0>Z$kU?`5*ZaJ2m)_#aBw4llQm1-;CbHFM3k7wrOFiY^vF!2Y2@i)T_R%wHJi0wnOBLcNrt@+*N_xEa)VP^h9pUKn1m_#Y% z>0wPGZGnOyIIPp@Yz1(vMj0leYkc4Tu5KP(6y)ilEs>%;J*-HiB~TPaM-kCh07q(+ ziJAFwA;c{@SxiAhS0TMTJ*tl5+%KhElJ-Dmu6FNSRpjY${NUhVt|L&&0yUe>W0-j} zfCU=sj{{il`~E788b%R9TmoQ?Mu`KNc}fd()*SothzG}m>1rBNd{vXW@F$OFt!pfC(CVCFRdbXo~Uk|a4% z2=N<@8o6UyqS+)5FoVfdDdh?S{mcI%qEmzrf77U86;pqBB6^;gPtd86r95c|3#U8- zg<<%q?&O4yJb#Jfcws-w1DHD#6h5u*OQ#>sJUtv{z8gTSQKKEl@l8aeQ=_B{bE*@7bsXRKA0(p7 z0jM^x4>*pqDT<<_Oa|*GdY3Q*lPDmf1M;3b)4>4%A;g_T^f`?>^O7W4KQ=bD-|eiL z!RH?!$GJ0I{(l=UgtuhXdYj&{4fK}uOOkgG4qNS+>jA<^r;Nc4UG8_cZJn|v%Y zTOW4IgN)_rQFw{o^eDsgylVj5p;7aMYPGtSnU(iFEIK*K)1%N5y-6S_1sYDbDAyxp`pjw!j&x)exc4jWrZumM_lBY)@C7QHV zgNVl4?e_TqeyduXnJ)>$@MhKHi-f6$G62tN9A7wzrtI|@9v*Hwj_VMZjbmst{rkavB-RgK3+^T-SXyj^on+{6lpSX1*>60^Kd{tYEr_Iq$i%teH`_ z;oP}%{{!Gu03)gk5z$RS5L~W$ybZEyfS*kg&A46F^SqamBsmR0Lvbx4q8|o9aIWI< zxsvtt#B7l$RljqsR{H}Hoen^Cv&#w*-4z7EiHgVPLSCacvq7TN4f8^XX91i8V2|RO z2NBV_FbvF4Q6w48|6YOGsz9OwQhiaw-KBQ?q+JHGEf$jny)P;FqD8^`g+AP5du&5#Amyb|h{ z=<947I7 ze+x6Ke9qHuL)E;TZP~qh_tN?E=Z~qbuY&d#cfRl6481X+BRieWhA<3$-7Kb1kjPjM zv!X;+>*OP47m%6#-@ z8i}mS0~p?^o`~A5R%;o6EgEIcjiTsBMQQ`theWx}ZA~IkA>;w{Hcyz8(a}-2QmJeP zaGXY&>pjo=im6N{=EX?e`yxD9H$8o#`z|k6IuJt_c zMpKzg&}cM-t+{_@u5RDHy|Q4zg8ycw^u5arvV(MSb_n3+Vi~I48|&% z0;4;W=~A!Pw-eE+0A5inK>${UVYu92tP+D{{?1e(#9x{DqX0t15)ObnrIbqz#wsyH zE^lr2eg6f=aZbnH4aQX%VCDxJjmAQgxk~iO?Y;4?>ptT+&V>L}cknpKah!+3Fx0uT zD=|!N0s(;Qx(^f4Wl-P9K>&CxiXxpmyAp%sDi8n&A?^X7^BVBsBuO>|L7;kPS7MOd z1p)xi^Hvhk*ELEk=(d3zVk}gN5>^BP03pOT0eoAd#5+5k&f`H4c&0Lyq{r&>CT1on zMiYXN-Bc9xQKSo;h#5h5DjwQJXT zMD(=nEG6kE#2KhZqtOtK<2(hu_s^H)NTCD*fU&W$L)-24RscuX&QdZxg}8)GWN2vU zxQQKKcrlxS0u!%@1KfuhJp!cHRk_;*28K{R4 z;@8+ar@q^EmXdu_Xn_F0^SmdSc{$WKawy4=!V3fdzVELiqAQ@@22zp{{{!w-(+1_- RA&CG0002ovPDHLkV1hb2VV(d0 diff --git a/dist/icons/overlay/osk_button_backspace_dark.png b/dist/icons/overlay/osk_button_backspace_dark.png index 19ac8847e42b79bc89d6219359f942926b248980..542038bef3b1444d403506f69fc640786b6725f6 100644 GIT binary patch delta 1253 zcmV+o+skG;>4K%zk0lK+Z#&+FMBAW;+Y_b z6pto~3zT)VdnC_ksE@@Xi8PNUiU(Bf#>W(o9RWn-(SJmmM-#;X%KGs68t`Z$&7+CJ zfvg{27Cisij?sBEk>=4v;Xsw4d|UCjZKCsNBF&?T!hzbx@FT(VkFAdg;pL*|54Yt1 zUw+sP>e-wt2Ow75r=s{zTNwcn?*aKP3V3EXP&O04R-yWVIpcVO#xA-sTfP{en zuIs-zbf3BrK(Ze(0C3xg>US&sfkeNgr^XHv)h}BQAkm-ssgXw#9?v=gXb;>;+$C3_Z6M$CSKAKIDej^@z>)l1?8a{hkwS0#0M7AS|BgIYf=Hn^4uNbAF~jql zn;#;D-g*YyBK*T!{G*#0I>kM_0KF9QHGeD6DemzXz}6QVL7!BIAxhkQ{5j79R9VMd zfBG7vDnH(wX;qTQfVl#>?( P00000NkvXXu0mjf)KO;VU>uUpy=TW+3)YCd zWks|lc6Mg(eRh3; zW+?6eLFnn}Ib?8f@O}VS#hv9~r{x3^LbManS^yWtoh1dZ;tt@4LZNUN5p6&!Phx19 z8Au3mI1#M}aCY2TQvOd%2xQy#kpMO^^BHkxN%sm$C5;Dordq)blYW*{NNd;re@ zI4`0&0hp;#W-l{esFd1jD%Zh=l#>1Y z|3ktG;s+tb*`DXE2XMGXnSXkox3s^%|7BCTQVvY)U*n~e7ZA}#=*jaofSy#I=!sPz zA;jeX9tY5&QRYq0^Uif$w`?d@iVtEFNJ{w`B3c8WEocp9-VI<;e}BI+7A(aVu?Qrk zydJ;<(EAMbr%WcZ$aURU4aG_cK;#11wtX{zyEVt|A08e)r(7<-ZYWktKq3{$w(Xml z`3IV1N{vQiaivmu(@3ZkEu#7wPeO=0nE7iuMVNVetya5W*REavHWe&Ik0@p!5<>ip zh_vJZuob|vxaBDn3bW!GTL{s#J26l%)Al6QAG4uloAyRg|=F)_9%dr02UL`(@Bvi6oIR4Ua?bt66Hb>$*1p z@J!?i$~f|jWr&$CQA%wHW9$j!8Ot_iUT$(}_q&;DbO!3~?moivygprdnE5u>b-#>c z%L9Zs6HP%LAOSOxF@f^={G3Lku@%5VojS~Xn^NjqhGGR~3i32DF3}X_X%eeMMg$T< z9P4@B769EEWtf@2-eU4JF(#2ud78u~k+wj&T<#Y#5{pDy0?nU4|CCH7vmHQ}Mwww|zSeP^yL7Ucf|;*}e|eg;6H%{i+e`gED1>O= zw{KrBg2~h53?hp32vlH!q?9?&^R@ywQDgieX1+`*^?*hVqhzz$s{uTrQ-YbF@G}!N ze}#%-4``#_$H7*`F#F#jC_SWUSsSUGZ&RoztN~+oKmS&&t|iiLQkS8 zFK3#|R+CVFPM8ee0tq2L;d$P30A_29`@Usa%au~=HEM)LR1&qiJb+JuY}-DIh&BPx z*-(6sh?bPgFG(tHNqx|Se_<{ zm1x|})ZX6SVP?J(Bd=Ks1QD$m92~q43A)T&mzd;fl1ParY}HUo4b|)QO91>)qyE)W z$~!flPYebd%3jiVK5-IF+UrxTR`(1I4J`%GuQA4rQp)dYJRdtITUxW(>|#W8WGD&_ z*eo_MFff?OWX=Qd7mabgCxrN_#`CdZx&c1wb7x^QrEbHWJ9qvaz!Cs&1s#Zper(&e z?uKoogmrpiI!QF;c2(DPUnQdR0K6M;EMn%LODR_cd_GdbK6*1^P2NS=Mp@ zL2ty$0C>>0?Xv%B?@%n z!f~A60=NRf+dyVA^JXb!F5vT_QLEK{1wDCcM6}XRKToMtsx$Lt%>1Orxa~yLE2aFn zMh)KtKCr_Q{PO6%&TBPloauSqx~{IStf5TWOhKa1PESlxqFCht%w9C^ zjovS~QKQC^j*gCpip64L-e*6FM4=zOIe6N5*C0pL0%t_5>%Pd$S778ah9Df~dESQ8PCIRZ$y_OVL?#dbD5W+K(KQHuk;5#@vYyH3^YaYG zO3@%vfdIg9oQIhCMvW3jd!E-Pg!rhjP$_|lTp$2YO5IIFw`i0&5kQ}m@)%Q@Qi2hS zKmg!4&aD7`6!du_It9Sy?(Xg*%%w{4OWgZSB&EC;z!zf9krI&DW}s0xm&;uT;PIGq zqy!}H83@409*<>NPcrkT;?9!dkGN+b03!|M?d|Os19&0sEGhm-#0=D=P$V zM3)0-#GNH&e-d;4YBJt==bh@DIdgUcSb>Buy9XQp1zs>)iA2UVssI2007*qoM6N<$ Ef|*-odH?_b diff --git a/dist/qt_themes/colorful/icons/48x48/bad_folder.png b/dist/qt_themes/colorful/icons/48x48/bad_folder.png index a7ab7a1f635d6c2aaee7ce3490ecbf0082d34bba..34069c6b230de579d436fa9f0c21ae37af905b2e 100644 GIT binary patch delta 503 zcmVMLN`2`L5i1E*bZ0xZA+EWiRRzyd75 z0+!u4eEZa7(PBUW_oJiOcj^3O-H(qLOCa)pFZSO!HzS~4t7FCya6CA!z)2mZY~eTSYnn^Nw}$ z>v}KiDEQb+U3CvQ9-g=)N*xLEKM|z&|4-Fq68}Xh(|h^CH3}8`9|*OzB>g7(FJHJ? tG64xtEsHs}s+0m{xeJ{8Cpa7q$popE`M?eo;|+1` literal 15494 zcmeI3e{2+G8po%F0G9AWW_E!ci(Q` zG*RO}?`C&)pZEJd&olFR-{;x+XP#(WS~sQQ)(VQErZm+1n&3BUd?rnR#}(y+kHfDU z;`J*vikkYa@hRJW@6=fo<+`G@1oVJ^i6E&_izurhU`a*ea5hD`=cVGJ6b8B}1lpBY zt@-NPN6jWht~IZ4_*sA43p$kgo&;#_S=u7?ge8}3p697>rvw-v3Utwwibi6Zkg7GO z;|lP%G0d1v=_z`+*1W(-XbSinOp-L?nG`vc!N0h=Q1|K|ijfU%W%rRjorEC1qY7H8EThzdxV5%FW~VA-M|nVCoozbY$j-6djJYXojsjG~!n3|K$1sEcEYQaraPjw}gn zY9cD?9wjQa11278cQb{-qM7+!C3w|{nt-PTJT^B|YPiTNz0-t-n5K&{2{iaTaKNG{ zvLHLe5HE!!TCzzlT9oY^?X&}UkQ^e*+hnVYv!@~Y{l)GhLi<#y)2J30I@4HWRf6FQ zr)34K!)lY-Xcx=1(HsYyvOKNEGh8yeot82<$kpQ0Q!-qRfQhk%3qI#+*YU5)R zHTWI;zD!Y_@Tt-MAVmo~C~Btq?3(xPr>OE74ZZ~}ssCI#yDWSre#^vb7k6Ksf4C(% z{lix!_M=6WCpY)qwC&u$k>h=j4pd#5b$b4Q=~VB{T{|xSE$0QTRZ!` zci(lx=O4W?V{?Eyc&Xy^|KCNw$eVA!wr%yd{=v7VH&F9Vl{;16(Bju_zBXijy{WwQ z#Kx-jTUXPzgLfbKV%Ld-y=5;xNG+-U>eCzR>q2uMJXAR?P<>(lx{V#nuDm-gaqZvF zPhwBZp7wWT&{S@s^6&3v9ozOs&6@Z7*4@TVo^*@*P}!>G!F?4Iwj7u_`IA|_mrgp$ z=d1h6&hMBz_1OBx$^Y2$DIb21N*&l63@;iV3|Ac9^XH*)&;OyPap%Nw*VorLlfUdA zoG=veKi+ljeDag~&U?d0A2>5OKEC(Ka}U{GezWc!%jQFCyy3UMZa?-y|79xj!8i8z zKf9;z6n~gmwRPZs(sugn(-U7?zIu;~ud9s3g4YL|rDf1R^*r*x+E z7qd64+j4v$`S9V-_;7IFo6{B_dhW_sJ#};5s5%;4+|^vUYwf>gbluU{)Vuxx_klZZ z^vU5T*EW4HPO6;vw=bQ)-&(zU-^sq)4xTuEa!TsItE8*GWxL+#tvuGcecIj)`X5*R s^QP0D`*wfPI<#j7RW|A3*VLll%v>_i@YJ7-CxsgpF7@sIb@0*e0i}o=LjV8( diff --git a/dist/qt_themes/default/icons/256x256/plus_folder.png b/dist/qt_themes/default/icons/256x256/plus_folder.png index 3a49669a3ae3f0ea46ebd5ddd0e51f2b31b37d47..f44c80c3ae25ce0c12c83d13a1ae84efc81d4018 100644 GIT binary patch literal 1948 zcma)-eKZsLAIHD5nN5aU-9;WUY$P#{Ym?Uzu!5(^E>B#&inOwpZ8zye?IvXKW~)!Ms)xH zinPlk0077;LI47`YG2aBX8=%(Bzd?~Q{*$RpiC?YcqtZz=o>c=z5GXg`t?63-4fe! zbFgeWf%WwJvdQ=(-W<2)YQ8C{o& zg%KN&u{8KkWTW87Pi~m!JOZ~0vCs!|#E3H1>6G~+t=_=;aI0LIErC@esf7Lra1lT< zUL{8U2vAuo{Vtqwgu!s|aBrP{@QutN5^`F0_o6&8;gQ@@I?pWGjd-lrrD{pw+McyA z<{Ydy^Jk#r2UV2syE+EOj$OiLBbl@NQp?+UMdpXKjail`u+IF!iDa{DftUI1h+kZE z0GS_^&O^mf2*W1qiNGM7b_(}mY%2?y`xRqy99f!HVTYN)XAGU`*d|ya^VQ;K8|kM- z3jerlBsC(&R(~b-X`bGWviEq7Ot&ga$msHIAeU8&^~lmUa?B{)?D9VH$U6Glq-j0I zVG~O986~Y8NtT8=+mA}G-v6l5Xy=%v5EiO@dM;7SYKk|(E>9S;k=Kh^VA*K0504J-a*hl;+5T^(q55GJg8L zGrVw>)<4d=*HGgH&Kl3&7W3+0{OI$?Hl(;#17=+B&dzr*eq8Ocx&Cu7$Vloc3+V!b zP5W29IX^(D)6G2y-le$U*necf?0F{^?!L6QC%M~=`WTkR0F&k&tS2iKEB;v@=;>aU z-V+rDpKb4vQb{)wg-;N~kLg`4QvOemgBk}HJ|)X2qj0LNQ2wtP0Y>E&w! zprWFIzR*eh{r&s!Yj?tGiji5!4`ydL~0$sKS0scSF>V;8?|7YEM!LB`Z zmR?&w2CJplJim6mDjn2JvbJo64=~OI&6E3Cl|#V!5_WX9Vhaa7s!LFjwY@$Fl+UoH z!XufdDz`gmVpLBCenlBaSoUaK2;YacVe4EgDcFNDl4e|pph5F&ey*#OgNQ;e3okpi z4wTdlpm|Ewy7@s6ac7qW&7Q5pDlTwYt+!~HszTdNn=fl7+iVt9T5A~fapw5%(-^z@ z`jK-=F<0UZ9SfCUs4p-fZ=17S4zl3uEJTv&55+ds3MzUm`s!cN=rO8R;iUfWc$X<9 zsHeJb(GnUqf6lYkc$cu%veQrhJ|p)lL@?r(Hy^ASWmYcejvVWma3e}yRmhqczNHRO z{Qt4szjrOkIM=WYFDgk~|MV3Im{s>|PXL=!^J)zp*`=;MF@^q%nbPwfpN7S=&TLA! z8pY?XtLtm?407y*TQt>JhtO6}*dj29k5*mfg&F@?*%z~H2o=l8TDBV4$XGz}1A^vH zzEO3f|0uK+_ChkgZRnUc5Z^Bf11ISl0O5G96Z=%#_uHYp5KA)D=r+*I%`|aKfKlB{ zJVPF|PtDbeloVc9See_AZ0NV*!?2-@vmgV5Y)oyzM`0m??Zw;f;=2m$5-iYGG0_Ua4T1^8k)8-V#duMR9 zXIl;?XR=ymU5;TUze(1MNnXErYbC>*;oPc336Ad{ZFR^bC;vJVxp7IVgjGB}tzbzn z|4S0x3?Yj*F|neAqv$MK)#FI3Q>ci5<$JoYif+Gx6oLfX?61r1Xu?Ra9W4v{9TJU1 z7g$?YVl`fyh@!qiB4@4tI$$Khwz>EEyCjhGB-mC*$NZF^>vz)^DdIR-#gN}6ekRh2 zXW-4fceM7Yf2c})q?U2LpH}v@xm!)PT~#?UM8l2rg3XhB#r7I^Sa2I^bnl6af#l9= zSa97AF_~Graq9C%$({)7W-ABh(CV_(f)Stg4S(jqR|HN@)z*jx=9Dzo(vvF)-y8In zN;B_{gg0;}oOt89<$}b7@)%a%IE|*ryp<|@O!;-otCVvUqLYVTaL0Al{MS!3CGi%} Yh89WFHBF|yt6vw8JpDWxh~e3P1CC-@r2qf` literal 3521 zcmcIn`8(9xAOFlSc8SJPQ^t~-#a412SGn9)BW(6c;x@zY3(fN#^;bhX} z-;XL2dv@-copp=8Thv%TXkJ%nASc|*KN!b?DWUyoX`CL8EB@xQbA2|R9-e1B{U#O! zFnc`&6%~gfxf1C<^p_8TLN9M)jImpTXM)ST@}1XjbvTw%yqjNhXVFvyT@Mj*SQ!CB zVPe8iZ*pL{yt}t!?yz!<5E$rSkcCaddC4S@gJ7-xSsnOTs68L0wN$FPC1ndx=3ATY zWtiR8$wUTdHW!ns10>HW>LjP8dI9$6@J*G} zJd9OYJ+S$`@!5td$=|S6R6)N9GPgI40*|(IQUl?dz<6EJ^Ztak zN9Loe6P_!qrv-EiF@<*Ou~Knqvsc0nKyvAyt`?LjhS# z1UTV!`JSXv|BI#wx>2x+05uTE+m(SaH+FuMv|K~gU7uka7F4Zv9U5MAOK3LXDvzs0 zq@z6$Fl8s}DA46Gcm%Wd{(}S1WCC-^pqdv=?V)ep*_%%+IgcD zZ^4&I$8A|YqAS>KVQ=yI$maOq9vzS}Pz(uf!+nHKFLtntg`G{(1uLVXO%8nnx5%DI z5Ox&a%e-6f<@`sW)<45A0$Qs$^g1K0KW0)PNYfq=>!n|gC zC*&FeSPO_#lbb{z&V6p+0xWdQU{v#vGYgSoQo!c}U2T7HvbIGLz>}M})aGP~kbd_y ztntQCDc}%OV(JkyQsUu+3O;v|hyecNutZph^Ko+26E8e=A%dKsz-hsg1=V)(TLTcq z_fhAL>tF%>8yk^xg?Mwr%NYye?IQI|0U}FJfMjyU{odqa&uBV}cjXFLMy`XEt3O{l z<-BP8F3n%$9Fzih%@I&)$MOS(e&2vmuhJG>fG0~OO*C&0RhxZ-*LI13BEIbvhE-?g z)FUH`h|3Vm#Kh}hAchNLGdjYbYdxXvSc8D|R@;`5-{av;vgT?IAmHk2mWb+dxomsI z02HA45!`!%!~zy0UP&VX410hcPoo-Y(*a;>@lEKyFCD@V5}_{^ri2B^+w;|Z5j5Z; zr{q{7;?dX2_@dkS8-e)aln8T(8hjF2e3qce*~6L6$iIJUY|CKB!lHv}8`rSl9I=6H}yBv~jC=e$#4DV9U%(ocrqAo{o4kRaW0ykZN z_jPGer3)(ok5nx_u_Q7xv<{|&v&$eEdgzFNwpevzzxgnYBT(|OYHVKKbIM%U+VR^e zQkbg);X9{{zaZG72-co)b)n(+rLDo;?9sa%&xhB**?DP>mt~mOnH!bPLIW9`trST+ zVu54-(G5A{C#{&egeN#X;?scJ1c*6wrnJJG7l82_t z(I$R;Q{yXXEzKS->74v~@`N~@MEc1}N45f@1Lws2mzwt`w_&nJ^xnJ`EkkzBP z;9C_RPONPcT3%A#W#6#_sZe&L&R1%q@_2_B(TqeduqF-28aw;AT)D$ z)`PyGlO(Z*{VL)6AvD$ETP8IHwsHiw?<^k?bahcv&NRn!sYLyamvm(pI_Mp}EjRIvr%WbT|9-UM_Iga` zWYIQYhXJX4mE_20BtEq@eOc4g(=>AgEQK*R)L7lzaL($qZ|9z`HGwuz@~BVs)I)7t z4b~USlVLb9a13uoAS3Kp%gHbCKIDZXfu0BM)m@sF299|A@iq~vcb-m<5P`}MxjILxP>IsjKF})q! z9;sx?a0ea_HaZ5wLQU)wlxfvC)BLkI;CWb>cfxzfx4)vy8xK?>$$iu3oC6aFApUq` z_*c^^p`os>8v12>#W8q?HnBRsfK~)j5?~E}k`dOUU4B$}x8;0w>I=%B3D|pA;JkZ|8@3!?I#l^-3s3sxAJUG?SkIeG^Tm5p^e zUic|%GV7{~3NjsW@2bn7V%HV$f=eP;nw&=nxebh-3$K4iK!pfTn66% z_>kp|YClkn%O9B8YJaN$Oqf{Td-*kS{p5N+Dh}CfvK!-7mAY`uZMMMrs(J7Y~Vl)dTFbW{qH*mRbhD2EJnc_%Crd!Bl z<3(UT{?nlHz}xW+&0es6amtRBv9y18lDp&|n+6pS|80_m$DoHcNX32(DW3P-;H^6l zF2cWxuL_WCIWeD>WQuHlWf&}c@uk}5rsM==Y5^m!?6>YBMU05~Z>E{eF+6d3v(vpl zy<~vKF&NRjtiVtEZ9t7+WQfL8#BiH2J4r70a9r=Un64|i!+~Iyq7$$_ei7eu@{;Tq zO>P?J1E;xNfo#6kI85Ij4gU=GpK1MZ0Z&Q`1Y-m*xwFKRXApVUvE{&@rqioc!x}Ax zDVVrJJ-vm#+Oi?N>~G$PG1Pu#pkENw4r9*_Ju1B9xDI@LSHTeXUnbnz z$>*qZthX)CJI}PS*v}O?MB$n$19w)+bMiAwi>_E2pPMgC#U-k1rDiEb&Rv?JwkOB= z1x%sE891vQ6|1g+sFJ7(VPhL9p~Eien{sope=!#2fNqZc3aOfz*gS;P6?LiYE0{~R zg2U%*Z8i|5h`@IRAdN*a;+=C1D_5*Z9=y8r*YDP# zpWw`2c{AUP4cOoQGgEx}`MH;q-J2%nhd+GlFASKtI&tCo+Zp#XB@M-C>!+^Ogyfhm zezd0F)TPr2vX=1oXWpVl_gGHAIC;OF+ z;|?qRaI?q0(M5`TtQe;WaQ%~p(tmZlEEyJR3F5$tHI)UM#il%IC}vJb6Ezm&hTbyF z6L%D+H*|&qzNnpW}a)D60y@A3MW=L!@P(YY}2|}^`pN|s8uEA(^ zYeZli3m2|=cSq+KyM6*fxm1+cDzOsmas_wqFj(B_z>!Tq&*8AR&S({q7IxJDdhAvIKi2oM%+hPDtItv0jVEva)A;e^EQ~ zt3iYzTj~>?AEx*1DPq7s#cXLB$=uO7Uryl;H9}xAV`}eN%m7AT3!Xi-FVPMHLVBPU z^c0+YmhC6S_B#o|5A}Zfx7ObkBmgG`o4#kmi0aL(Bn(`^fjNYX@mlHJXmy4bf&&6V zznIjlEo5*(WHCPCCJLGoXIc*;E^{f{RHLu<{jG-|DsZ`tC}>tY#->h0V!Z)_Gk!Rq z4=tOrrK`!{n4$y_`pA&3Q!~3l87+RUFf9Hd{mnMt6h0Sbc5dhOf|x)0tULvXHhV4p z^`3e9M>Sv@1FQE!@(g5U%BM7HIB%cC^@1Iu$hQnQm`^N8n>2`N zwxGIYxs@#Isf0u8k#S~t+DgCEt@~%~^dKF6(}cxfhUf9_v{`<;-N7q^)|NCELW+&s zA6xI4n0t`Rh`s#tP5hFD=NOlYb(8PB>}|xRz11K<`?LTzAF%Be#PCE{Z?;XJsm1+5 zu~^_Xm^EzdGRTlm6;ZDS9Z!L)uNax8E`zuZ<4G^!)gb3D?mY_E|MPSmlcP^)$#Dh# zVms>)Y{cnFN0kp35DxnX8ft)O;%1xaOj`1i6ttk?L@)1nzqz1XhsvG z>q7S++a3`51uu3fy)1ZTlMV4tKqWRdoQY_*b$>KxV}YWF9|MUkr++MqUUWO;8x50ErY3Y1C^nvlbS{baxtM!0WByOYmeeV6 z_fa8?BR&A1kt#fr|Cb|EKTQ>$pgxCq-(Vne9&4UG)BKk|7PM(0jtWnHRLV7AZN8NS zmTDwhQ2KCxvoV>GZQ-pfYal4_Vemg}IslcKORT<02*{GZXeDI6s`RbyfGx$>DxHVB zTM4~b6<()415r*VOlM{=;+`_(sXAp6w5Cb6w_~dh`$a%oiq|C&a^i_InKvyx07#f; ziI#sq%tdLrv(IRq;DO^!tRe5XOmM6QxB_7yQNF*enNPjY35McAP;R6lU4_p2nnqT2 z0IOZ{;8SSdv1!36+EV9KjQKGWWK~_At6<*E%j~8coypJp`vQ+TD7GKE5z%-WS}f}$ ztkm>C_fl{hQp!M-I$qovoQ7XpDFjLFlCQpjAdA(;oJ^;=U7vt+O2z967;xyzP*Qe2 z-5DflB<~hNrP=x|q-#8kks!I5?gMKlp)1dD)_yg~IpdXAi9EL7e^mMDG1I0eT z8b(!*JbdeMsvY-t=meGQv4#bSv4BhM4Y1~2ckyEBnO%j7*%r30Fe63V4LB=~_u{&q zv14FN{VlT9>xucAT(L_AZ$j6p!vA%Hu+E3ZoE6tpxY}xIyCh%<+S6M9jY?vKjK+Gw zZ?xocER=1~@1k6c&QFN~iL|Y_RFD9jR*qZnXhjWfuHB%Yg^sGrG#byClJJqzFr@S( zfdreD8%fht;lIS96m6*GiKvP+BRWxKFxF57Lwa)ke(rbd^HV}VxmKzQw{{1`CqRZs z|Ktz@FjeerAMFg8snYGQSyC>kf=!U3WLn3gN(79X+wR# zepFIwB&%&gfN7Cpu0b+NP{|>p<&)t@Hn`etAuajgGo*LQr>pLMyqQ#pcWWfJ1&0y) z;Z;S7*IPwc2H@yXJGoxwEfegcpPx3U=H|W>cV9+DQ8G-7WnLq3xvhvwLs7fj3n#kj zikg_Rtt9yTqaAqq1AOs8w`+ny;9{QZfyt1qpey z1=@9LvSPkt=G@!+Jiwo6d=b@7Cc|yc{>md7Iu2QaxrXTzNrzcTd!dICg9W(T3nE)5 z-O&;IP|xYAtFPt2SbVkk)Z$wx=KM+6WP%$p(j-Kd`yRojzP9bEE3Gw#uyiec`E%p} zR#v!us4W%GT-uI*to}zQ!JI;3N$v||B$y4sueA8{+`Akb_>jTIT{&1U-%6cl#)i3} z&x=b3isyrqhAB+W@ZCHJ(EUMouW*Et_fUdC!7dWISez~dTWL#qF)Yl`48k~9v2pV% z9G0P#n%RE>##Yt`5@dqUkDNxd+leq=wAIk;T*Vy1WtA8BI#ndXHp4D0b$U)qrsc7Y zpv@|2%fA|sMLkm$zNRT_CMxU+bwMjS5yBsghwfC6vo0X=tjXX9Y4A0NMcjb*N>$;_ zE#N0Hw|swGFJd8n#H6e4Pj7q3!Wpe)_EBiksnNQtZpsD=D2joN-3qB*=czbj4GWo= z;kLbG&UW=T5XZa+hVEo$mY+p&i$p{cDl%yjEqT6s<#M)k{Ocm^>Ov1xXw+NSA&H8c z5UAsqAFzcox^AV;dgzW;VMg!CFo}xX*;DEXd*mAc>YoNc3L6Wn)ZFdLH82x0VI?fw z^ZlrnHzkyU5%%18;1-1Cm<-*KHgSQ6J34B}g%}C%MfX`r?{C%CZYeZ^jof6Y@4mbcoxxk;DZHldTCDC4s&}SBQY6M>!(S2l zwi#-IrJY^vJ4pp$y60^_^+sXj$eH$18BqftLb0m!d3r9@X4ZZf9s%>+Z)H{U;7e!V zMMwzZD=ZK;(-T1S1+)B`ySg$K%z68p{Y^Iz#mhTogA#1C)MN%!VbueXf|=NEi_X!0 zPAb+D56V56Pi;_HVIIl>ad&`52qsMh$B-6QX~fZgMbPV*b2Un+qQBFeJAdML8HaF) zHhdGD1rt4@w)d+v*)U{OtW-d6*bER1lddVFJ6*ev+yaRt}3!7&pZ)}W)B^#xrJ zY#_y4wHYMPHWlIKAA}vsM<&st6xcROWw|M4^mYkufyE^lzS?=F2EWAiT!6)zCrW)%3Vd}I-rlwlo=!SkY1-z6S6?yoS#K)1R`D$U z#syA*JJ@~6fTxR;cbHHbH7h$LgkQ{Azu=3jw&VO|k9{v{9a>KUr$-T_bSK?m1IlU* zWY*D2V#%x@(O~vxt-Yu^Y48z;+J)=*JRNxmZpMPpFCt~>nhqCRCmoGWv9<@;Ab>50V{X}U~nxxppn z-NykXbTb+JqcZ9^?!cxeZ49EWOB>IFh<99SEY;}fzIpne>)^1W<_}QL!!|9i(R?#p zl&day2r2}G6b(wk9Hu9{k6b|@e2Wt6G|r3R5Gy-k9<}B1@t1Hq!ZPv()rT&2ve}eV z6xM6irit%iyK9|8AgtPLpzvg)Nn#%7qf%7su7A{re6H=0rwWmyeP*(hCIs!{MbW?{0TK9r3o=8VHo^36WjFU|2F_z!@&u6 zh_3{%J280&8sEgWNm{F7MB3M+Ewm_%0XsU^%4ajFC}wxtbR{>)l&KpZtbYHT$i!2(>lkd{xN7YbLlgpu6sXI^2Y6s tKTd1St6%SOTRYg0|G9cv<<;Nq_gMQ|kEfdP;lD^gWLQk-xnNn|{{X?lcliJS literal 6751 zcmXw8c{o)6_rJ3-wlMa6kL-J7U$SH`MKu^}$~KgpamSWjB9ts8Bz>%fEE!}8p%f$A zNT`e@!ekl0>H9ptzwUj`dA(ofocH_voafxvO|`c*XJO=L1OR}=(!%s206=K35CBO> zd!R#}`v3s{X-iWh$B3`%#o>il`gR8l+U4cFUGhgiIVvL^y5qV00F8crE|q*4hm2JI zax+W+hIjXoyjF?Cr$j(QhMI_gvqRX89AN$YoN$j`WNRz=ZI{8Qh9;7{G17T^^YB=g zmGynDvfAdX+Jf5d<9M{9d4R8Gq_+5#M=&99&+vMgpbTW_X;%%HPbeZRU;XzG^zgpmk6zApIWHTEtb^*_oZ(sMD;G(=~prYaGkForZaZ1^Axacy(IJ0j( zKJJ7Sb0~ku)ZIqI@50dFgFNM}hYcBg(Put8nk0yq8Rm;aEcB!ZwuF7W5I&HgNtoP~ z*+F2Y9HWoXS!l(9uiIJnmp?qaZnspX&1$rSk_qrUsk-*H25tytgDw+F4Baq1m>U?T zfzT0Hm{rqMoj-6B(DD;Im|Fo+pc4sQ;Y7ycs@NRpwi zP476CDJy7u7I+DF>z1?2JVTm*%3`w^bEluod6u-YNFAHMQb2cu&8x~dkyfP4KRmfH7yN6W_5z+38f8F zp*_8%Qq>r?;~NIgKIk(T+D{xGf>#agVsT>$CvSqs8}4czN&-e$=6X&oqtZEr1^uN` zRk9Wfjs|s&L;o>9yRXYk-F*CeEY3QC77c!k50}_f(q+LF|F!~*ztdqDxy`I7f=6SQ ze`L`7%(K+|;0)$1xD>F{;f!c~sNEbi9=YB?(So z*$1ZRzBcode)?KAy8Kz?$2_9zx(9sDzDT|}_8!t4pda?7RSgR$>uyusKB?OX70ZD` z-0Rvgdy@SN5s&F;#}(WL)M2KgOcs}X%Yx|tKmz-}G5BMxE8ZX01za;*%Pvqk7HqFw21`<~JlpD5UsMui{@Z(bUGsQu_u_Iv`jkU9;Q+91uHtCTp{ zt_B{1^MnOZsnJLDWS2TTzw9)okKv2TE4G5(W21*O!JG~3dOu;;S#@*0VSnrPOLu{a zVQ!NNRhR7?K(YtGTgFN0y3X)EWOR z{q!oe@;JbwHe9iu3u~?~kn}IcP2tSSh2ZUI467#Yw%xp&1>iy2@I4UXsj1&NlJh#X zO+|rTZ+Nw&`VUG5LV(eGX9f?gfcwi9q+h&i?HoiQiiMBprV_*=Kal8h&`MFjKGN^{ zN6aiN`u5(OG;HeU#PDIdG8_Z1zs|h-vbUBqd-6uXK94(+@PzP&cKUA#jwZXFl8tXL9MFs-$$Y5KWA-zxO`|){0C{`K!c=enjbYr6G!T;PECBt1@lyd#G z%BwT2dD~J!!J{wfmye}@24&7JOPwm%ji0%Jr?2NJdu{ZpyR|)Up6}w>f{|VO-V(A1 z&BS@zhI3b-^5pe22-4vK(lI<G63eogO%6sk478RUjp&p28{q8&O;&r5DNvXaP_{Pv`>rnHE6+D_U$l8_!gwxM7@{?GdwTN+#|lCLG$I~nEjy1BJ!yUL5f*_FAtCL&?ZoGi z#^Qo$Z2gufI?S0iP`dH0|)T`Y|#E;)PiJfrva^O{8Anq^eVyvnIlWkAn zX?;5Xs*MzL00;G7d@}oshS{>o1_3M2DQL6m9u6A_? z4+#6c^DRjC!h+Q7Z!0qLw?6}TJ!}8-sDM{fkBSXg1jc#T6-fcR&k#68sxITm{Z2-^ zI|C(}3o!Kuz~^F#t=o2oSEiq*;By=N`pqqYU;Qe=U(dn{PtS0wIPiba8dyAwJHG-i zV`_%{c;wJ%nF9=9su1)RfM&J`ltrjzU4b0X%4Rt(`zUr^6hc(j?>I^LY;cglw!0jI zQ4H(746SFc5F|YbV12)5DoRf7Qza`wD2j(m)GJ>kNg|EyBe}gj;BnTLTSUIc$5h4p zKuj)9`0H*;znQB&*!jDaJNo5>8Y=N;j#lDeD4`ey)?s7QZwV$Ai%IVz2FB=+KKqiA44vdHY{p`^z&XK|D!M}1XdZz@CV5_R7I9@q4f3j*YYTcN z*51(#LYxbZE-`uXwr@oRW_7wzp!sH@q>Y5iSLsrCS<~I>F||fN2dfqLqU7lFC))2D z{#3{;CjtK1hj}(?-eXU*MpAuX=AJqD+%nN?JsSnA(E5a>=9gLdfbr+M=J*iOi^N|_ zJ@agTKC}U?B~&FqKmVA~9G$%WY0TOeZL>#`OhAFcSu?6`nsS0Wr|Olkr+*vB`=z-E z&8wzE?b8kunV7uldN?E+3c1Dx&_V6Kr|t7KAVkLf#V+n)+a&XR_kl^LesoYc#&{B5 zR5?1 z6NSHSA!ZAFATr&@420-Lqa5XZkM4jP&*wEtcvFGebl}yVGR#@*kOQJD#s8RQUyC7O z7yw5|=@9MajPwa}^#xl~rhIiPE&vxAG7X(#Or4*UBsyofh*7gwqt4}GH-_7ghAMmH zk6fki#y=W=&UfDo#E+$IiCjF3V;WYQNITqCH(;0hxk^N7ODoQl~BTmq9ct z9io|2E3}GmI{kvMiR&?+>%40;+kq3emYN<2Rl)Cs%l?2aTE?{3;de(*{ ztY*!OJJYQF>ZE<)VFM208Ak}Vn5oQkPx9g*R}LHH+f#c4VYk60$KUcY-S?>m=8JX4SVn*CF8dftG6&T@2)7Iq)m07@e?uh%24?jGA)%U!ES*D zV$jiKxgvJHr;HKfCU5AIMS;3+dk**DhF&>$b@QfKiQ+dQ6y}KO->fq{Nen#G9%9OK z!4Ln#-Z}HUzT|`&Ibh%~;DJn#G#$yOpG$}u@ZR8w2S#95bg1rk z2VG73ouSsj=#?z3i1XLoAQVEcY8h)Ar(~lx6ZXS~B@;kkW;GzoOs5a>+C|FT&SrxX z23U2>{*if*5HvDD8d*;)c(3Fu6C2;V-C#^mnaw9Cfa?)07L8`-^UPn`e&kVvPh}!V zvKDKw5wB@VsJ`22wUe5ZfI}h}_uYHa>V2OZdnGLsxuOclsCeKdLU?D~-JmyC(?jh3 zyInKVq!z>l}W-Og=wYIJvWAZOp_zt2S+X&uCl>_Bi$$> z#LkjE(E|3D7`cu#e#R+A?wGya@C`|<=S1rsh% zgK$}RSOh?eMWfF2?hxK%@dVNm{T2;NYt#6wE zfG+H~q1!@LKwMsCDi|y@BVND_0a`_q&*2h^Pa)a)S%SNqUd!4*w@_Fd^DY{H9)%K@ z90Ixjw!ta)7Y)zTYzVY$5eMYs=x=6>MrekLeIeGIxqF_8vT}iRYq}VjIGHtk2v1kJ zcMcN4V|Bts&*K>kPqcV+Dr@+MQK zF*;)Yue zgWe7Rkd?KExKFVNS~~gJIH<%}&mmKA)TDpXs*ETO1GZv}L*0bV_!~^uVhLfb3qXw2 z8gvVR72UdKyc>?Fgy!nT=;pYQ7oUt2G${fYOU{GSGEjgX5`-~NUiTV)dWyW>+Oglj z6!4G-sNWyef!sy~4pb8>DrO|%Pq{fE23Yh;hi~#@9w=bXx6WW~)hdJ_j7cDbS^Sj_ zxr;DvWlc+}Jd-V84``e-I)(^f)&gx+RqW3P5j- zQJr1my$PX&=Elh18mB_h^Rj|oGqTo^Rn+7N52nfF5OU;>AMy8cq(9}NgRQ4{5OJbc zHCU6IL8cA?$TyR+pH>+8lGEnoV+3aE-v|=r4i+YT(cf7HB4l=MI{6XhwVS2{DjqYTiRHF;xa3A}yZRZp*%{jBW2Iee_T=5~BMb-n` z2;uky43qd~E8IZm!7#=e9`??bxw2a4olm_QwWRXoi&ME2a8ktQ{O5@)LGQB~ z_0gcZQ>5&^5Q!!%G+$#sQ2p^0(

  • JPa8m5Yr$xgK)@(1w5gf%#jdc)j!Uh#5W{-0HM=WA9{1)v82HbQ}Zd7D7an zFFNNvRcHXqZkP`z6(H5_RTFhL2#Qn+DKz_#5d|hb??GwLq7-<-(oEe;efzPO)T1Mk z+WHh_uJ8=>YqZ+!?1t9%Y*4KL)zJ&2Y2PuD(?_b268W`9ki6WsC>i%xM50cOKSD4) zQ5RMyvVPV-?9q>SGz+KEHjD)$??TCU74LPA3HKjsVCz~R??yuPOE+|cz0VHO0pP&&- z=FEw7EM?FBK_C6tJg(P@QG(P#raZ#;P5grlzQtKfLf8cOj?QP&l+5SVwlibZ-96Y& zgl;Bc=!XzC+AZ5hdZN+84h67KpRI2KWqyobqYTD5ob#oM?gPGC={C1d@0A$bzEML% z;2MJG(>kbwFR6wMAYB(xxdk$V5x8#wm>!8pJN33b^^nzhy=uWiZKVJ+%O~euiG^>& z%S0+GA8CAe)b#eUTvoAuN!NmwMZ$tX3#@o>T#+V6RV2iP?r47J=TiGw*`3jOlTH2B zi|)8caXFggoNLd^({C{ORurNVSY=ddUG28-(=R2w?YI*Yi z`g6JMgi7gk*?KmY==1+Xy`IADxY(wko&Xcxs44q2e$Ja)Q>7QkUjyyr)hY6!sc_NO zUFuoQ!xcToxhTV6H5O`H6H-gSH$iVU(#Ay`f(-98s535#-Cc!|xG`rj?LQ3auC&m2 zi?&xWp*5$}{lWkC%}-7GxXn?JtpZxi?Tyiz-08H)(30&If2;D>C+#9(<` q24WP=5QAdb3a7{(wID43sz~k{y7DmLP6+MC5ny@V*0k0b6aRl+8gWDb diff --git a/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png b/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png index 002101114d150e304765a3466be9bf267ae96382..14c90fea54ea60d0e2eb024cf5bcca81a366a93f 100644 GIT binary patch literal 1924 zcmb`Hc{JPU8pq!sGLl>bO%ZJc4Wi=K76~$%(ummCcGBuJ*0$4DscJG+Gof}VN~*E6 zwl1SFmP;$0nn6@>Ek%tgVk~oODasf|tGLM@_s{#+z285c&-eMh=e*B3?>X<2?&C#Q zMd~5}0ICevV}1ZYG7AB?k_;PhtR(=H-5JM@25?3frHcGeh7Nu8@V%NEv$%8f5_!W6`ML-MdQ+D4C0#gVnC2wcWNh)*pa!N_g0 zY2G2D7EXh#QnMTwcQsk=IH{6O(T?j+h$tvGQo(}`W00V~A071Mnr>>ztQSpYDciTN z?qTgMBqqHeXXaVk{&bSx(z>5tSx0iHHR!geeEJ1xvZjYfaOty3!^{~*maH1i);rSH z==-oU?=2&2QEsK}-!zFKk4Q%W>Ws@=2 zLUSP>G<73~@t>xv^DND~`Cxp!-xjLyYm?^Z$Gc7X4>#_%6^q_`w>p(7O;TLycEzJ2 zhi-A1(*01UE%wEP7esTM+hqKsy6@YnMMcBna;576=?+GDDzc&F184ben8NFkBUAWd zW?BQ9^8wokBiomO20OCbD0Ge%<+T9e6V?aSsAh@Y0>Tn#K#NS*0l($YdF9f*tq#BM zZ7JC^hchq2bB_e;e#*%ub(wOHly!djAnX8ag~aEi28=%jJCI%e6ybg_JaGDWxF1aY zn+JPp_2d3fCl=P7nS|ihR}^D!{H%Tyi2tb~_P(=f2&W3N95p^oDMaaiy=opM0&G1dx)O(q;pYoA57 zC}K4-v(a4TNCT}#JU}fv5w0BvdIG`a^wvg?D$o>VXDJ$|m~zuUCiTZl!c3J**Ah<6 zU5)Z5C*~X|GfOO#I0nFdljE;?vfOLQmo*DX%%BsnMirPF#pRBAz_&b2*lDeaD`;lA z)Q*>TkFH#8PjUR0#kI3W8N<3rYGaD&|MT@AJU1>n*4snIeNnN_aym!Hy2@!3{WMHY z%69ZXNz=DJpsq?uji`*&2Rs_-*21ficT`9z;nyDq(sYFdiZFE*we;|yWNdre(%T0;FQF?&0WBHLAn2qGp}aP#phG0Aod^Jc0>yiTAMbw$zk zxyL95em))E9AZ>RAa!cQa_5xydNj()2g| z`OW;U#aALFM#smE6Y|CCTu{06fvnAFo7Xcz<8-$%9XpsJgVnMA9aXzX`t-HTZEZ;f zR;_vZs7wcO=VRv&8Lcn0J&ec%U6Stky2*D{R9TJ7Eys1$jDePbL&_0HS-QC-_I_EKiO$qU{vF zlPMgK6gH)WjlI9fn7EEwi&49ov-nYPftgUe?&uN`+czD%(3y5(me|J%ba^&)3&Fe- zFG{0ximo|6O8N5_UF_?=B1#W!vGyP-jD19K4e&=u3bIj!KKN2AL3sCM=O*T%R NFlb)Ks-0Mg{|0pPAie+q literal 3931 zcmcIn`8O0?AHOrmt}sa$Bx#H+St>-6Z4hN-%}!)5TV%;dg|SxViJoHYYX*sIV?>q>P=B$vSwa_b+&VdG9&jb3fnp+;i`_pZmQDX4lcYTq0Zm0A3?QJ#zpcOb7uS zN0^g+V6n#^Eyx_L11fsN7MKd_ovWr-0jNslX1K61bW zjhc-M3tJcOZqb8@e^&lK42@#`@iDc(yOW2Pi%qO#WCii2=^^PM_}}=7{iwX5M#>k8 z8)Xf@l&+s_kXIrkOsM56Xv{n#@5JetE<6&9Ig|1}6B$1e*@U|h z(33tT!1J^hB1rW?)xhRD3!MjtD!ip}guwd=K1Dn*6zpJCR++I52ss$_o@aQuNvCD< zCxe=+VsEL{N^v|&0{?vS=EiEYr2d*JD7QR^Qec)8t_8&#du^K0=CueR$&0XIRKfEfwLG$VR3xEw?NA$+BE;By?S*)6(z;Al!WaXG8TKG>d$X>2)6F zCs#4Or&|prAz#oSTq^2RZ!}Vjp(`iMndG|9jU|q*T|OaW z5)qR~PKEiSlvVHc9$GqLvaH21{gGCF*1r{jb*cKfuHi+$S?}2nitAh0g{@uF(V?&9)NM+j|u3HriJ!ekJIjotwua}5`c0RbV?!L)* z6o%hzA9S=SAVDi*MVyA~uFoOg(X;9M%Vq-llD4ZSxQ|Ek9BTX)JQ0_&6mf#QwOBUj zyB~KwZ=>$UQXl)&CZ^jTjw>z|Gi!zVF42PgYVLG+1p|G_2NKp) z*`uW#%TDJO(nt>2v%`^HgU2_a9z(i8+C386+$OHt)WTuNQ$<4)e#vs5+H>RSZ%0)YKc!U6M zr%c57+7qOdro*8yKwsetlb%lXIc<6~UAOHg7Yx8$`M+LUe}5{ia8QlQW}!)f3;dPX z$f7AXLArdov?@99UIq^W*d*Y{iIh8P>beh#ZQ|3#;Nws>5Npm)b1!0aDocodFtjb- z9$2^kCnR{>sSzT#dQgz5Zah0fWU0>UZpra2Ue4cMZnDFN;{DanA9y7JY^j&(u!W=9 z0l~g}n?ibV+)0Wks+dA5bdG_7;20d)4Y#%yM)+l4RjIA%2{Ze~mc+C(kEPrD{>{b6 zz2UR0rW>qmU|#t+B=li+vhAE9;e)6!(*UXB5Gxr9F>yY7s0#t%df(s~5>(_|*%1uD zimRYYeqJY+*|d!M`kOGl@dl98Qd<}mc`*SB=vhu3wceu4xXB-b6-!E4Op|!DY9k*E zE5;<%DvF-_i13ZnWG90y`9#0r&lsSb!$+$4WjbrsO)9*c8& zVq@Z_4Hs%=D0AQY(7{x@z`k_3i08iMCB=;qhSrUpJB)V+&;fmjVTMK}kv%#tFvdJ` z+wC!<`5;qs#!V6f!|Ij%@UNgMxTm^aRX-SQ4=?9*5h}?6(uVN+O(NM=V+kosI)QGH zCelx#X@&JT#Y_FN56@kZ5zvJgCOMX9`DUM_YIu7@`bs~BrezJ{nUBEasfS&IAEnK& zLJZ>_b1qt`%xg_+{6y`in9isQpdf~cL`Cb_pv8`gYBIqChOJpXHjW%#*EAF4Lfxv3 z_ro)4B?Xwd*Wh!s)I|9|8Q0*wXU9!+!jxl@m>c(h6pivx-wWwplqZyC$_7Q#c$U6Y z^rR)%{GesHb*XEqXQ^*#prsBso^>Y~+IMvfRa9ond{e}T%Z0whNtiwnq6$%ys7t&? z94rWc=jkErM?1bC^0Y zqBVwsc#%z8bF z6NVM2Y28^V9(Cc*7wVq$^pu#U8aakKZ}fP#1o^MM^Lk#)++tAX?58cCahq> zuV-pKA#8-#Z(S27t3rJHpvca%{2ZDC=lsVT#Au~X+ z6|kf8WxWtUfLVq@?L55hlaM?Cqz5H`48Tr)pPLy|O0Y5m#-6Go+DDWWyK>mO5_>ss z=|K={JhOPE1ch+R??ETeV1O}>cnKY+9^8LETp=rA(378yz&l10D*irP6ydR?J{=DH z2%yGDbb8eLy4j?x5P{EeYxl|7NI`1#0s&4-?T87@?uXKWL? zdy~TB{_I!9qaIoVUE_Ddgm#5bF!rwNdGt!AORYxuKy^b&?7wA-R`q$xn00o`&$5<# zrPU8I%DZk-hFz-7iLzhP(RlWnou>IG^?jQ^M4K&E&T*l1==QtRla^EPc|I-t-X|}M z{0b;p0i>Ph`hN=Q)?|_&elq^QH{i>)s<#7Qs`Q}<;AJZ^jtpS``Pd8$1X}x+1&8`R z1I(+lYMI6R0#9?>3zg)DVIQlC@J^wl{WHowecRSd-_5v5o&Q4-ZCQEL;DR^Lu+f|J zZ5zX5Y?3ZjKg9FX)n%r$)#H1zFEA-+rZ1{G@Tv0iI*xs>-2sqG`hs&$cZ{P@ zqCyhV5G&oNK)4z)uHN{~vIDkZa%@z(7Cg1Kc6uLBUC6? z=j03FAy$y8yV#~Xl^p7I-`{;__4*yqmBT{H&>AJfyLj_yzIX-1j-vQ6@=#^`qZ`nSZwHK9F?MFr7Df6k(li_#SM1@L!E%; zJKJ~Zx!;+MHNLn~1g-27WHxh7QQ78^`QoTo3@e6Ez);IfI0!JDcHzVZJM@q2_!acd z`@kwoM6yMi?pFbn@uay-F}N(~VO=kkMw%{bwdbb-X=>5FW()5J<5UBP_L*&dcVA{C z*Q8ag6=WNo!F!}>rSL9If$Z+Whmuloe7`Y{<;Qx#%;w?*fW?=_&4Xi;Sf6N~@R?*% z|5|Id7!@Q1cJThYz$?ZM({XWc6)3{sdYS22U_#ckORRVbnj$O);{#n z$fj2}+r+Tq8&R|QO6tn;$za!0`d-p238eh-V*pRnJ~RrCAxUIBZu)Em+GH_(7w-BO{*{lyOJali+h+b%nc(3+&L~%3W$YA;gtYwXZ z5Wea|DmWBh34Z9~!`hc^z(SfFwV%Z@iy^<2MB2d*Tb0JMQsAEkCFsSkZpg*S9*sDO zO|dQQGAj%z-LUI00Q+95(;t7SyVw3QQe-is2+Tiz1arasj@hxTRTT2s7JEbN=L zm)*hB+0%O$5!!#RT?k=NuD21J03CBGEL%iE;;YlB^{7?J$`*@O7yKKMUME}EU|B?* zWeIaw!Xi0DOV(FgZ@04_J1a|4+||Gl4S#NMU+U+OrJdP|x|%}zd*X7@c19+#dEtlq oR#{sMLFj+@N-{%|Tz$&dqmf1)&@uP)=YMjH^snnx=r}+64+>Wf+5i9m From 282cd3e5fe1ffe01d1ab008ef6b2a15266708d24 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 17 Oct 2022 00:01:50 -0400 Subject: [PATCH 148/245] kernel: fix slab heap ABA --- src/core/hle/kernel/k_slab_heap.h | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 2b303537e6..a8c77a7d48 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/spin_lock.h" namespace Kernel { @@ -36,28 +37,34 @@ public: } void* Allocate() { - Node* ret = m_head.load(); + // KScopedInterruptDisable di; - do { - if (ret == nullptr) { - break; - } - } while (!m_head.compare_exchange_weak(ret, ret->next)); + m_lock.lock(); + Node* ret = m_head; + if (ret != nullptr) [[likely]] { + m_head = ret->next; + } + + m_lock.unlock(); return ret; } void Free(void* obj) { - Node* node = static_cast(obj); + // KScopedInterruptDisable di; - Node* cur_head = m_head.load(); - do { - node->next = cur_head; - } while (!m_head.compare_exchange_weak(cur_head, node)); + m_lock.lock(); + + Node* node = static_cast(obj); + node->next = m_head; + m_head = node; + + m_lock.unlock(); } private: std::atomic m_head{}; + Common::SpinLock m_lock; }; } // namespace impl From 5000d814afeefd7eb7f250888b76d6723790f762 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 12:54:53 -0400 Subject: [PATCH 149/245] fixed_point: Use variable templates and concepts where applicable Makes a few things a little less noisy and removes the need for SFINAE in quite a few functions. --- src/common/concepts.h | 8 +++ src/common/fixed_point.h | 120 ++++++++++++++++----------------------- 2 files changed, 56 insertions(+), 72 deletions(-) diff --git a/src/common/concepts.h b/src/common/concepts.h index a97555f6af..e8ce30dfed 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h @@ -34,4 +34,12 @@ concept DerivedFrom = requires { template concept ConvertibleTo = std::is_convertible_v; +// No equivalents in the stdlib + +template +concept IsArithmetic = std::is_arithmetic_v; + +template +concept IsIntegral = std::is_integral_v; + } // namespace Common diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 6eb6afe2f5..b2e4898416 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -12,6 +12,8 @@ #include #include +#include + namespace Common { template @@ -50,8 +52,8 @@ struct type_from_size<64> { static constexpr size_t size = 64; using value_type = int64_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<128>; }; @@ -61,8 +63,8 @@ struct type_from_size<32> { static constexpr size_t size = 32; using value_type = int32_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<64>; }; @@ -72,8 +74,8 @@ struct type_from_size<16> { static constexpr size_t size = 16; using value_type = int16_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<32>; }; @@ -83,8 +85,8 @@ struct type_from_size<8> { static constexpr size_t size = 8; using value_type = int8_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<16>; }; @@ -101,7 +103,7 @@ struct divide_by_zero : std::exception {}; template constexpr FixedPoint divide( FixedPoint numerator, FixedPoint denominator, FixedPoint& remainder, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using next_type = typename FixedPoint::next_type; using base_type = typename FixedPoint::base_type; @@ -121,7 +123,7 @@ constexpr FixedPoint divide( template constexpr FixedPoint divide( FixedPoint numerator, FixedPoint denominator, FixedPoint& remainder, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using unsigned_type = typename FixedPoint::unsigned_type; @@ -191,7 +193,7 @@ constexpr FixedPoint divide( template constexpr FixedPoint multiply( FixedPoint lhs, FixedPoint rhs, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using next_type = typename FixedPoint::next_type; using base_type = typename FixedPoint::base_type; @@ -210,7 +212,7 @@ constexpr FixedPoint multiply( template constexpr FixedPoint multiply( FixedPoint lhs, FixedPoint rhs, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using base_type = typename FixedPoint::base_type; @@ -270,10 +272,8 @@ public: // constructors FixedPoint(FixedPoint&&) = default; FixedPoint& operator=(const FixedPoint&) = default; - template - constexpr FixedPoint( - Number n, typename std::enable_if::value>::type* = nullptr) - : data_(static_cast(n * one)) {} + template + constexpr FixedPoint(Number n) : data_(static_cast(n * one)) {} public: // conversion template @@ -411,15 +411,13 @@ public: // binary math operators, effects underlying bit pattern since these return *this; } - template ::value>::type> + template constexpr FixedPoint& operator>>=(Integer n) { data_ >>= n; return *this; } - template ::value>::type> + template constexpr FixedPoint& operator<<=(Integer n) { data_ <<= n; return *this; @@ -490,10 +488,10 @@ public: // if we have the same fractional portion, but differing integer portions, we trivially upgrade the // smaller type template -constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator+( +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator+( FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -501,10 +499,10 @@ constexpr typename std::conditional= I2, FixedPoint, FixedPoint -constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator-( +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator-( FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -512,10 +510,10 @@ constexpr typename std::conditional= I2, FixedPoint, FixedPoint -constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator*( +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator*( FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -523,10 +521,10 @@ constexpr typename std::conditional= I2, FixedPoint, FixedPoint -constexpr typename std::conditional= I2, FixedPoint, FixedPoint>::type operator/( +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator/( FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -561,54 +559,46 @@ constexpr FixedPoint operator/(FixedPoint lhs, FixedPoint rhs) return lhs; } -template ::value>::type> +template constexpr FixedPoint operator+(FixedPoint lhs, Number rhs) { lhs += FixedPoint(rhs); return lhs; } -template ::value>::type> +template constexpr FixedPoint operator-(FixedPoint lhs, Number rhs) { lhs -= FixedPoint(rhs); return lhs; } -template ::value>::type> +template constexpr FixedPoint operator*(FixedPoint lhs, Number rhs) { lhs *= FixedPoint(rhs); return lhs; } -template ::value>::type> +template constexpr FixedPoint operator/(FixedPoint lhs, Number rhs) { lhs /= FixedPoint(rhs); return lhs; } -template ::value>::type> +template constexpr FixedPoint operator+(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp += rhs; return tmp; } -template ::value>::type> +template constexpr FixedPoint operator-(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp -= rhs; return tmp; } -template ::value>::type> +template constexpr FixedPoint operator*(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp *= rhs; return tmp; } -template ::value>::type> +template constexpr FixedPoint operator/(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp /= rhs; @@ -616,78 +606,64 @@ constexpr FixedPoint operator/(Number lhs, FixedPoint rhs) { } // shift operators -template ::value>::type> +template constexpr FixedPoint operator<<(FixedPoint lhs, Integer rhs) { lhs <<= rhs; return lhs; } -template ::value>::type> +template constexpr FixedPoint operator>>(FixedPoint lhs, Integer rhs) { lhs >>= rhs; return lhs; } // comparison operators -template ::value>::type> +template constexpr bool operator>(FixedPoint lhs, Number rhs) { return lhs > FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator<(FixedPoint lhs, Number rhs) { return lhs < FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator>=(FixedPoint lhs, Number rhs) { return lhs >= FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator<=(FixedPoint lhs, Number rhs) { return lhs <= FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator==(FixedPoint lhs, Number rhs) { return lhs == FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator!=(FixedPoint lhs, Number rhs) { return lhs != FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator>(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) > rhs; } -template ::value>::type> +template constexpr bool operator<(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) < rhs; } -template ::value>::type> +template constexpr bool operator>=(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) >= rhs; } -template ::value>::type> +template constexpr bool operator<=(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) <= rhs; } -template ::value>::type> +template constexpr bool operator==(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) == rhs; } -template ::value>::type> +template constexpr bool operator!=(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) != rhs; } From 9393f90ccfd3131a021a1c5f3e42f20a287a6560 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 13:01:36 -0400 Subject: [PATCH 150/245] fixed_point: Use defaulted comparisons Collapses all of the comparison functions down to a single line. --- src/common/fixed_point.h | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index b2e4898416..7f00dd7b59 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -301,29 +301,7 @@ public: } public: // comparison operators - constexpr bool operator==(FixedPoint rhs) const { - return data_ == rhs.data_; - } - - constexpr bool operator!=(FixedPoint rhs) const { - return data_ != rhs.data_; - } - - constexpr bool operator<(FixedPoint rhs) const { - return data_ < rhs.data_; - } - - constexpr bool operator>(FixedPoint rhs) const { - return data_ > rhs.data_; - } - - constexpr bool operator<=(FixedPoint rhs) const { - return data_ <= rhs.data_; - } - - constexpr bool operator>=(FixedPoint rhs) const { - return data_ >= rhs.data_; - } + friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default; public: // unary operators constexpr bool operator!() const { From 0101ef9fb115e59f1b9b7a28890104b115eb184d Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 13:05:08 -0400 Subject: [PATCH 151/245] fixed_point: Make to_uint() non-const This calls round_up(), which is a non-const member function, so if a fixed-point instantiation ever calls to_uint(), it'll result in a compiler error. This allows the member function to work. While we're at it, we can actually mark to_long_floor() as const, since it's not modifying any member state. --- src/common/fixed_point.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 7f00dd7b59..116dfbb33c 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -411,7 +411,7 @@ public: // conversion to basic types return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr unsigned int to_uint() const { + constexpr unsigned int to_uint() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } @@ -425,7 +425,7 @@ public: // conversion to basic types return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int64_t to_long_floor() { + constexpr int64_t to_long_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } From 2cc9d94060bbf4a6395ea1ebaa06ce65bb8fc07d Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 13:10:33 -0400 Subject: [PATCH 152/245] fixed_point: Mark relevant member function [[nodiscard]] Marks member functions as discard, where ignoring the return value would be indicative of a bug or dead code. --- src/common/fixed_point.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 116dfbb33c..cbe76fbf1f 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -304,11 +304,11 @@ public: // comparison operators friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default; public: // unary operators - constexpr bool operator!() const { + [[nodiscard]] constexpr bool operator!() const { return !data_; } - constexpr FixedPoint operator~() const { + [[nodiscard]] constexpr FixedPoint operator~() const { // NOTE(eteran): this will often appear to "just negate" the value // that is not an error, it is because -x == (~x+1) // and that "+1" is adding an infinitesimally small fraction to the @@ -316,11 +316,11 @@ public: // unary operators return FixedPoint::from_base(~data_); } - constexpr FixedPoint operator-() const { + [[nodiscard]] constexpr FixedPoint operator-() const { return FixedPoint::from_base(-data_); } - constexpr FixedPoint operator+() const { + [[nodiscard]] constexpr FixedPoint operator+() const { return FixedPoint::from_base(+data_); } @@ -406,42 +406,42 @@ public: // conversion to basic types data_ += (data_ & fractional_mask) >> 1; } - constexpr int to_int() { + [[nodiscard]] constexpr int to_int() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr unsigned int to_uint() { + [[nodiscard]] constexpr unsigned int to_uint() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int64_t to_long() { + [[nodiscard]] constexpr int64_t to_long() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int to_int_floor() const { + [[nodiscard]] constexpr int to_int_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int64_t to_long_floor() const { + [[nodiscard]] constexpr int64_t to_long_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr unsigned int to_uint_floor() const { + [[nodiscard]] constexpr unsigned int to_uint_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr float to_float() const { + [[nodiscard]] constexpr float to_float() const { return static_cast(data_) / FixedPoint::one; } - constexpr double to_double() const { + [[nodiscard]] constexpr double to_double() const { return static_cast(data_) / FixedPoint::one; } - constexpr base_type to_raw() const { + [[nodiscard]] constexpr base_type to_raw() const { return data_; } @@ -449,7 +449,7 @@ public: // conversion to basic types data_ &= fractional_mask; } - constexpr base_type get_frac() const { + [[nodiscard]] constexpr base_type get_frac() const { return data_ & fractional_mask; } From 0cfd90004bf058a278ff1db3442b523e2bb1b0fa Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 13:13:26 -0400 Subject: [PATCH 153/245] fixed_point: Mark std::swap and move constructor as noexcept These shouldn't throw and can influence how some standard algorithms will work. --- src/common/fixed_point.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index cbe76fbf1f..ad0d71e502 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -269,7 +269,7 @@ public: public: // constructors FixedPoint() = default; FixedPoint(const FixedPoint&) = default; - FixedPoint(FixedPoint&&) = default; + FixedPoint(FixedPoint&&) noexcept = default; FixedPoint& operator=(const FixedPoint&) = default; template @@ -454,7 +454,7 @@ public: // conversion to basic types } public: - constexpr void swap(FixedPoint& rhs) { + constexpr void swap(FixedPoint& rhs) noexcept { using std::swap; swap(data_, rhs.data_); } From b6119a55f9bbc306593afccc0820165d74c2fadc Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 15:33:47 -0400 Subject: [PATCH 154/245] fixed_point: Mark copy/move assignment operators and constructors as constexpr Given these are just moving a raw value around, these can sensibly be made constexpr to make the interface more useful. --- src/common/fixed_point.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index ad0d71e502..5aafa273b0 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -268,9 +268,12 @@ public: public: // constructors FixedPoint() = default; - FixedPoint(const FixedPoint&) = default; - FixedPoint(FixedPoint&&) noexcept = default; - FixedPoint& operator=(const FixedPoint&) = default; + + constexpr FixedPoint(const FixedPoint&) = default; + constexpr FixedPoint& operator=(const FixedPoint&) = default; + + constexpr FixedPoint(FixedPoint&&) noexcept = default; + constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default; template constexpr FixedPoint(Number n) : data_(static_cast(n * one)) {} From 6e1c6297a3f8fcd243ec6b31d3832d84c7df474c Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Oct 2022 15:37:37 -0400 Subject: [PATCH 155/245] fixed_point: Mark default constructor as constexpr Ensures that a fixed-point value is always initialized This likely also fixes several cases of uninitialized values being operated on, since we have multiple areas in the codebase where the default constructor is being used like: Common::FixedPoint<50, 14> current_sample{}; and is then followed up with an arithmetic operation like += or something else, which operates directly on FixedPoint's internal data member, which would previously be uninitialized. --- src/common/fixed_point.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 5aafa273b0..f899b0d540 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -267,7 +267,7 @@ public: static constexpr base_type one = base_type(1) << fractional_bits; public: // constructors - FixedPoint() = default; + constexpr FixedPoint() = default; constexpr FixedPoint(const FixedPoint&) = default; constexpr FixedPoint& operator=(const FixedPoint&) = default; @@ -463,7 +463,7 @@ public: } public: - base_type data_; + base_type data_{}; }; // if we have the same fractional portion, but differing integer portions, we trivially upgrade the From e63a5459e361d8f33fb9045bc9684d516d087b0c Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:27:48 -0700 Subject: [PATCH 156/245] core: hle: kernel: svc_common: Add WaitInfinite & cleanup. --- src/core/hle/kernel/svc_common.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 95750c3ebe..85506710ef 100644 --- a/src/core/hle/kernel/svc_common.h +++ b/src/core/hle/kernel/svc_common.h @@ -14,8 +14,11 @@ namespace Kernel::Svc { using namespace Common::Literals; -constexpr s32 ArgumentHandleCountMax = 0x40; -constexpr u32 HandleWaitMask{1u << 30}; +constexpr inline s32 ArgumentHandleCountMax = 0x40; + +constexpr inline u32 HandleWaitMask = 1u << 30; + +constexpr inline s64 WaitInfinite = -1; constexpr inline std::size_t HeapSizeAlignment = 2_MiB; From cb073f95dc6ef07934a47137b121a1328667df6b Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:29:14 -0700 Subject: [PATCH 157/245] core: hle: result: Add GetInnerValue and Includes methods. --- src/core/hle/result.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/hle/result.h b/src/core/hle/result.h index d67e68bae3..d714dea38e 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -135,6 +135,14 @@ union Result { [[nodiscard]] constexpr bool IsFailure() const { return !IsSuccess(); } + + [[nodiscard]] constexpr u32 GetInnerValue() const { + return static_cast(module.Value()) | (description << module.bits); + } + + [[nodiscard]] constexpr bool Includes(Result result) const { + return GetInnerValue() == result.GetInnerValue(); + } }; static_assert(std::is_trivial_v); From 47b8160666da8dcb679bb7cabe35a615a1786155 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:42:24 -0700 Subject: [PATCH 158/245] core: device_memory: Templatize GetPointer(..). --- src/core/device_memory.h | 10 ++++++---- src/core/hle/kernel/init/init_slab_setup.cpp | 6 +++--- src/core/hle/kernel/k_code_memory.cpp | 2 +- src/core/hle/kernel/k_memory_manager.cpp | 2 +- src/core/hle/kernel/k_page_buffer.cpp | 2 +- src/core/hle/kernel/k_page_table.cpp | 6 +++--- src/core/hle/kernel/k_shared_memory.cpp | 2 +- src/core/hle/kernel/k_shared_memory.h | 4 ++-- src/core/memory.cpp | 6 +++--- 9 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/core/device_memory.h b/src/core/device_memory.h index df61b0c0b6..90510733c8 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -31,12 +31,14 @@ public: DramMemoryMap::Base; } - u8* GetPointer(PAddr addr) { - return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base); + template + T* GetPointer(PAddr addr) { + return reinterpret_cast(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base)); } - const u8* GetPointer(PAddr addr) const { - return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base); + template + const T* GetPointer(PAddr addr) const { + return reinterpret_cast(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base)); } Common::HostMemory buffer; diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 9b6b284d08..c84d36c8c2 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -94,8 +94,8 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd // TODO(bunnei): Fix this once we support the kernel virtual memory layout. if (size > 0) { - void* backing_kernel_memory{ - system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))}; + void* backing_kernel_memory{system.DeviceMemory().GetPointer( + TranslateSlabAddrToPhysical(memory_layout, start))}; const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); ASSERT(region != nullptr); @@ -181,7 +181,7 @@ void InitializeKPageBufferSlabHeap(Core::System& system) { ASSERT(slab_address != 0); // Initialize the slabheap. - KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), + KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), slab_size); } diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index da57ceb21e..4b1c134d40 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -34,7 +34,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si // Clear the memory. for (const auto& block : m_page_group.Nodes()) { - std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); + std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); } // Set remaining tracking members. diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 5b0a9963a8..6467115056 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -331,7 +331,7 @@ Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pag // Set all the allocated memory. for (const auto& block : out->Nodes()) { - std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, + std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, block.GetSize()); } diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp index 1a0bf44393..0c16dded4b 100644 --- a/src/core/hle/kernel/k_page_buffer.cpp +++ b/src/core/hle/kernel/k_page_buffer.cpp @@ -12,7 +12,7 @@ namespace Kernel { KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) { ASSERT(Common::IsAligned(phys_addr, PageSize)); - return reinterpret_cast(system.DeviceMemory().GetPointer(phys_addr)); + return system.DeviceMemory().GetPointer(phys_addr); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index d975de8449..8ebb753381 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -1648,7 +1648,7 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { // Clear all the newly allocated pages. for (const auto& it : pg.Nodes()) { - std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, + std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, it.GetSize()); } @@ -1805,9 +1805,9 @@ bool KPageTable::IsRegionMapped(VAddr address, u64 size) { } bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { - auto start_ptr = system.Memory().GetPointer(addr); + auto start_ptr = system.DeviceMemory().GetPointer(addr); for (u64 offset{}; offset < size; offset += PageSize) { - if (start_ptr != system.Memory().GetPointer(addr + offset)) { + if (start_ptr != system.DeviceMemory().GetPointer(addr + offset)) { return false; } start_ptr += PageSize; diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 8ff1545b6c..a039cc591c 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -50,7 +50,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o is_initialized = true; // Clear all pages in the memory. - std::memset(device_memory_.GetPointer(physical_address_), 0, size_); + std::memset(device_memory_.GetPointer(physical_address_), 0, size_); return ResultSuccess; } diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 34cb984564..5620c3660a 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -54,7 +54,7 @@ public: * @return A pointer to the shared memory block from the specified offset */ u8* GetPointer(std::size_t offset = 0) { - return device_memory->GetPointer(physical_address + offset); + return device_memory->GetPointer(physical_address + offset); } /** @@ -63,7 +63,7 @@ public: * @return A pointer to the shared memory block from the specified offset */ const u8* GetPointer(std::size_t offset = 0) const { - return device_memory->GetPointer(physical_address + offset); + return device_memory->GetPointer(physical_address + offset); } void Finalize() override; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 2ac792566e..9637cb5b1e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -65,7 +65,7 @@ struct Memory::Impl { return {}; } - return system.DeviceMemory().GetPointer(paddr) + vaddr; + return system.DeviceMemory().GetPointer(paddr) + vaddr; } [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { @@ -75,7 +75,7 @@ struct Memory::Impl { return {}; } - return system.DeviceMemory().GetPointer(paddr) + vaddr; + return system.DeviceMemory().GetPointer(paddr) + vaddr; } u8 Read8(const VAddr addr) { @@ -499,7 +499,7 @@ struct Memory::Impl { } else { while (base != end) { page_table.pointers[base].Store( - system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); + system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS); ASSERT_MSG(page_table.pointers[base].Pointer(), From 113a5ed68fd2ab0050ebfb520bbd17399cc51298 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:43:36 -0700 Subject: [PATCH 159/245] core: hle: kernel: svc_types: Add SystemThreadPriorityHighest and ProcessState. --- src/core/hle/kernel/svc_types.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 79e15183aa..bb4f7b004b 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -95,6 +95,19 @@ constexpr inline s32 IdealCoreNoUpdate = -3; constexpr inline s32 LowestThreadPriority = 63; constexpr inline s32 HighestThreadPriority = 0; +constexpr inline s32 SystemThreadPriorityHighest = 16; + +enum ProcessState : u32 { + ProcessState_Created = 0, + ProcessState_CreatedAttached = 1, + ProcessState_Running = 2, + ProcessState_Crashed = 3, + ProcessState_RunningAttached = 4, + ProcessState_Terminating = 5, + ProcessState_Terminated = 6, + ProcessState_DebugBreak = 7, +}; + constexpr inline size_t ThreadLocalRegionSize = 0x200; } // namespace Kernel::Svc From 25dcaf1ecaeb3998a2cb8b03a7aa8a02402e0bad Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:47:00 -0700 Subject: [PATCH 160/245] core: hle: kernel: k_process: Change Status -> State. --- src/core/hle/kernel/k_process.cpp | 20 ++++++++-------- src/core/hle/kernel/k_process.h | 40 ++++++++++++------------------- src/core/hle/kernel/svc.cpp | 4 ++-- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d3e99665f8..1d3157a9f4 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -72,7 +72,7 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process->name = std::move(process_name); process->resource_limit = res_limit; - process->status = ProcessStatus::Created; + process->state = State::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() : kernel.CreateNewUserProcessID(); @@ -289,7 +289,7 @@ Result KProcess::Reset() { KScopedSchedulerLock sl{kernel}; // Validate that we're in a state that we can reset. - R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); + R_UNLESS(state != State::Terminated, ResultInvalidState); R_UNLESS(is_signaled, ResultInvalidState); // Clear signaled. @@ -304,8 +304,8 @@ Result KProcess::SetActivity(ProcessActivity activity) { KScopedSchedulerLock sl{kernel}; // Validate our state. - R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState); - R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); + R_UNLESS(state != State::Terminating, ResultInvalidState); + R_UNLESS(state != State::Terminated, ResultInvalidState); // Either pause or resume. if (activity == ProcessActivity::Paused) { @@ -411,13 +411,13 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) { const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError()); - ChangeStatus(ProcessStatus::Running); + ChangeState(State::Running); SetupMainThread(kernel.System(), *this, main_thread_priority, main_thread_stack_top); } void KProcess::PrepareForTermination() { - ChangeStatus(ProcessStatus::Exiting); + ChangeState(State::Terminating); const auto stop_threads = [this](const std::vector& in_thread_list) { for (auto* thread : in_thread_list) { @@ -445,7 +445,7 @@ void KProcess::PrepareForTermination() { main_thread_stack_size + image_size); } - ChangeStatus(ProcessStatus::Exited); + ChangeState(State::Terminated); } void KProcess::Finalize() { @@ -652,12 +652,12 @@ KProcess::KProcess(KernelCore& kernel_) KProcess::~KProcess() = default; -void KProcess::ChangeStatus(ProcessStatus new_status) { - if (status == new_status) { +void KProcess::ChangeState(State new_state) { + if (state == new_state) { return; } - status = new_status; + state = new_state; is_signaled = true; NotifyAvailable(); } diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index d56d73bab1..b1c7da4543 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -45,24 +45,6 @@ enum class MemoryRegion : u16 { BASE = 3, }; -/** - * Indicates the status of a Process instance. - * - * @note These match the values as used by kernel, - * so new entries should only be added if RE - * shows that a new value has been introduced. - */ -enum class ProcessStatus { - Created, - CreatedWithDebuggerAttached, - Running, - WaitingForDebuggerToAttach, - DebuggerAttached, - Exiting, - Exited, - DebugBreak, -}; - enum class ProcessActivity : u32 { Runnable, Paused, @@ -89,6 +71,17 @@ public: explicit KProcess(KernelCore& kernel_); ~KProcess() override; + enum class State { + Created = Svc::ProcessState_Created, + CreatedAttached = Svc::ProcessState_CreatedAttached, + Running = Svc::ProcessState_Running, + Crashed = Svc::ProcessState_Crashed, + RunningAttached = Svc::ProcessState_RunningAttached, + Terminating = Svc::ProcessState_Terminating, + Terminated = Svc::ProcessState_Terminated, + DebugBreak = Svc::ProcessState_DebugBreak, + }; + enum : u64 { /// Lowest allowed process ID for a kernel initial process. InitialKIPIDMin = 1, @@ -163,8 +156,8 @@ public: } /// Gets the current status of the process - ProcessStatus GetStatus() const { - return status; + State GetState() const { + return state; } /// Gets the unique ID that identifies this particular process. @@ -415,10 +408,7 @@ private: pinned_threads[core_id] = nullptr; } - /// Changes the process status. If the status is different - /// from the current process status, then this will trigger - /// a process signal. - void ChangeStatus(ProcessStatus new_status); + void ChangeState(State new_state); /// Allocates the main thread stack for the process, given the stack size in bytes. Result AllocateMainThreadStack(std::size_t stack_size); @@ -427,7 +417,7 @@ private: std::unique_ptr page_table; /// Current status of the process - ProcessStatus status{}; + State state{}; /// The ID of this process u64 process_id = 0; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1d145ea91e..bac61fd096 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1888,7 +1888,7 @@ static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); - ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, + ASSERT_MSG(current_process->GetState() == KProcess::State::Running, "Process has already exited"); system.Exit(); @@ -2557,7 +2557,7 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand return ResultInvalidEnumValue; } - *out = static_cast(process->GetStatus()); + *out = static_cast(process->GetState()); return ResultSuccess; } From 345b9e6a08f7ce99bb71f7184157ed0fe22bf27d Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:51:50 -0700 Subject: [PATCH 161/245] core: hle: kernel: Add KDynamicPageManager. --- src/core/CMakeLists.txt | 1 + src/core/hle/kernel/k_dynamic_page_manager.h | 136 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/core/hle/kernel/k_dynamic_page_manager.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index abeb5859b5..2bb4dea6a5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -190,6 +190,7 @@ add_library(core STATIC hle/kernel/k_code_memory.h hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.h + hle/kernel/k_dynamic_page_manager.h hle/kernel/k_event.cpp hle/kernel/k_event.h hle/kernel/k_handle_table.cpp diff --git a/src/core/hle/kernel/k_dynamic_page_manager.h b/src/core/hle/kernel/k_dynamic_page_manager.h new file mode 100644 index 0000000000..88d53776ae --- /dev/null +++ b/src/core/hle/kernel/k_dynamic_page_manager.h @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_page_bitmap.h" +#include "core/hle/kernel/k_spin_lock.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel { + +class KDynamicPageManager { +public: + class PageBuffer { + private: + u8 m_buffer[PageSize]; + }; + static_assert(sizeof(PageBuffer) == PageSize); + +public: + KDynamicPageManager() = default; + + template + T* GetPointer(VAddr addr) { + return reinterpret_cast(m_backing_memory.data() + (addr - m_address)); + } + + template + const T* GetPointer(VAddr addr) const { + return reinterpret_cast(m_backing_memory.data() + (addr - m_address)); + } + + Result Initialize(VAddr addr, size_t sz) { + // We need to have positive size. + R_UNLESS(sz > 0, ResultOutOfMemory); + m_backing_memory.resize(sz); + + // Calculate management overhead. + const size_t management_size = + KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer)); + const size_t allocatable_size = sz - management_size; + + // Set tracking fields. + m_address = addr; + m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer)); + m_count = allocatable_size / sizeof(PageBuffer); + R_UNLESS(m_count > 0, ResultOutOfMemory); + + // Clear the management region. + u64* management_ptr = GetPointer(m_address + allocatable_size); + std::memset(management_ptr, 0, management_size); + + // Initialize the bitmap. + m_page_bitmap.Initialize(management_ptr, m_count); + + // Free the pages to the bitmap. + for (size_t i = 0; i < m_count; i++) { + // Ensure the freed page is all-zero. + std::memset(GetPointer(m_address) + i, 0, PageSize); + + // Set the bit for the free page. + m_page_bitmap.SetBit(i); + } + + return ResultSuccess; + } + + VAddr GetAddress() const { + return m_address; + } + size_t GetSize() const { + return m_size; + } + size_t GetUsed() const { + return m_used; + } + size_t GetPeak() const { + return m_peak; + } + size_t GetCount() const { + return m_count; + } + + PageBuffer* Allocate() { + // Take the lock. + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + KScopedSpinLock lk(m_lock); + + // Find a random free block. + s64 soffset = m_page_bitmap.FindFreeBlock(true); + if (soffset < 0) [[unlikely]] { + return nullptr; + } + + const size_t offset = static_cast(soffset); + + // Update our tracking. + m_page_bitmap.ClearBit(offset); + m_peak = std::max(m_peak, (++m_used)); + + return GetPointer(m_address) + offset; + } + + void Free(PageBuffer* pb) { + // Ensure all pages in the heap are zero. + std::memset(pb, 0, PageSize); + + // Take the lock. + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + KScopedSpinLock lk(m_lock); + + // Set the bit for the free page. + size_t offset = (reinterpret_cast(pb) - m_address) / sizeof(PageBuffer); + m_page_bitmap.SetBit(offset); + + // Decrement our used count. + --m_used; + } + +private: + KSpinLock m_lock; + KPageBitmap m_page_bitmap; + size_t m_used{}; + size_t m_peak{}; + size_t m_count{}; + VAddr m_address{}; + size_t m_size{}; + + // TODO(bunnei): Back by host memory until we emulate kernel virtual address space. + std::vector m_backing_memory; +}; + +} // namespace Kernel From 9ec5f75f43c2ecbfdf52b45f78029b1fd1080658 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:53:44 -0700 Subject: [PATCH 162/245] core: hle: kernel: Add KDynamicSlabHeap. --- src/core/CMakeLists.txt | 1 + src/core/hle/kernel/k_dynamic_slab_heap.h | 122 ++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/core/hle/kernel/k_dynamic_slab_heap.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2bb4dea6a5..2965717627 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -191,6 +191,7 @@ add_library(core STATIC hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.h hle/kernel/k_dynamic_page_manager.h + hle/kernel/k_dynamic_slab_heap.h hle/kernel/k_event.cpp hle/kernel/k_event.h hle/kernel/k_handle_table.cpp diff --git a/src/core/hle/kernel/k_dynamic_slab_heap.h b/src/core/hle/kernel/k_dynamic_slab_heap.h new file mode 100644 index 0000000000..3a0ddd0500 --- /dev/null +++ b/src/core/hle/kernel/k_dynamic_slab_heap.h @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "core/hle/kernel/k_dynamic_page_manager.h" +#include "core/hle/kernel/k_slab_heap.h" + +namespace Kernel { + +template +class KDynamicSlabHeap : protected impl::KSlabHeapImpl { + YUZU_NON_COPYABLE(KDynamicSlabHeap); + YUZU_NON_MOVEABLE(KDynamicSlabHeap); + +public: + constexpr KDynamicSlabHeap() = default; + + constexpr VAddr GetAddress() const { + return m_address; + } + constexpr size_t GetSize() const { + return m_size; + } + constexpr size_t GetUsed() const { + return m_used.load(); + } + constexpr size_t GetPeak() const { + return m_peak.load(); + } + constexpr size_t GetCount() const { + return m_count.load(); + } + + constexpr bool IsInRange(VAddr addr) const { + return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1; + } + + void Initialize(KDynamicPageManager* page_allocator, size_t num_objects) { + ASSERT(page_allocator != nullptr); + + // Initialize members. + m_address = page_allocator->GetAddress(); + m_size = page_allocator->GetSize(); + + // Initialize the base allocator. + KSlabHeapImpl::Initialize(); + + // Allocate until we have the correct number of objects. + while (m_count.load() < num_objects) { + auto* allocated = reinterpret_cast(page_allocator->Allocate()); + ASSERT(allocated != nullptr); + + for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) { + KSlabHeapImpl::Free(allocated + i); + } + + m_count += sizeof(PageBuffer) / sizeof(T); + } + } + + T* Allocate(KDynamicPageManager* page_allocator) { + T* allocated = static_cast(KSlabHeapImpl::Allocate()); + + // If we successfully allocated and we should clear the node, do so. + if constexpr (ClearNode) { + if (allocated != nullptr) [[likely]] { + reinterpret_cast(allocated)->next = nullptr; + } + } + + // If we fail to allocate, try to get a new page from our next allocator. + if (allocated == nullptr) [[unlikely]] { + if (page_allocator != nullptr) { + allocated = reinterpret_cast(page_allocator->Allocate()); + if (allocated != nullptr) { + // If we succeeded in getting a page, free the rest to our slab. + for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) { + KSlabHeapImpl::Free(allocated + i); + } + m_count += sizeof(PageBuffer) / sizeof(T); + } + } + } + + if (allocated != nullptr) [[likely]] { + // Construct the object. + std::construct_at(allocated); + + // Update our tracking. + const size_t used = ++m_used; + size_t peak = m_peak.load(); + while (peak < used) { + if (m_peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) { + break; + } + } + } + + return allocated; + } + + void Free(T* t) { + KSlabHeapImpl::Free(t); + --m_used; + } + +private: + using PageBuffer = KDynamicPageManager::PageBuffer; + +private: + std::atomic m_used{}; + std::atomic m_peak{}; + std::atomic m_count{}; + VAddr m_address{}; + size_t m_size{}; +}; + +} // namespace Kernel From d02ccfb15d1f3d4fcdb9feae60ae136fcfd99788 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 17:55:51 -0700 Subject: [PATCH 163/245] core: hle: kernel: Add KDynamicResourceManager. --- src/core/CMakeLists.txt | 1 + .../hle/kernel/k_dynamic_resource_manager.h | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/core/hle/kernel/k_dynamic_resource_manager.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2965717627..e7fe675cbf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -191,6 +191,7 @@ add_library(core STATIC hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.h hle/kernel/k_dynamic_page_manager.h + hle/kernel/k_dynamic_resource_manager.h hle/kernel/k_dynamic_slab_heap.h hle/kernel/k_event.cpp hle/kernel/k_event.h diff --git a/src/core/hle/kernel/k_dynamic_resource_manager.h b/src/core/hle/kernel/k_dynamic_resource_manager.h new file mode 100644 index 0000000000..1ce517e8e9 --- /dev/null +++ b/src/core/hle/kernel/k_dynamic_resource_manager.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "core/hle/kernel/k_dynamic_slab_heap.h" +#include "core/hle/kernel/k_memory_block.h" + +namespace Kernel { + +template +class KDynamicResourceManager { + YUZU_NON_COPYABLE(KDynamicResourceManager); + YUZU_NON_MOVEABLE(KDynamicResourceManager); + +public: + using DynamicSlabType = KDynamicSlabHeap; + +public: + constexpr KDynamicResourceManager() = default; + + constexpr size_t GetSize() const { + return m_slab_heap->GetSize(); + } + constexpr size_t GetUsed() const { + return m_slab_heap->GetUsed(); + } + constexpr size_t GetPeak() const { + return m_slab_heap->GetPeak(); + } + constexpr size_t GetCount() const { + return m_slab_heap->GetCount(); + } + + void Initialize(KDynamicPageManager* page_allocator, DynamicSlabType* slab_heap) { + m_page_allocator = page_allocator; + m_slab_heap = slab_heap; + } + + T* Allocate() const { + return m_slab_heap->Allocate(m_page_allocator); + } + + void Free(T* t) const { + m_slab_heap->Free(t); + } + +private: + KDynamicPageManager* m_page_allocator{}; + DynamicSlabType* m_slab_heap{}; +}; + +class KMemoryBlockSlabManager : public KDynamicResourceManager {}; + +using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType; + +} // namespace Kernel From 57a77e9ff4b4a63c106c0ac3448a8f1452b5384c Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Sep 2022 18:19:30 -0700 Subject: [PATCH 164/245] core: hle: kernel: k_thread: Implement thread termination DPC. --- src/core/arm/arm_interface.cpp | 8 +++ src/core/hle/kernel/k_interrupt_manager.cpp | 8 +++ src/core/hle/kernel/k_interrupt_manager.h | 4 +- src/core/hle/kernel/k_thread.cpp | 76 +++++++++++++++++++++ src/core/hle/kernel/k_thread.h | 4 ++ 5 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 953d964399..29ba562dce 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -134,6 +134,14 @@ void ARM_Interface::Run() { } system.ExitDynarmicProfile(); + // If the thread is scheduled for termination, exit the thread. + if (current_thread->HasDpc()) { + if (current_thread->IsTerminationRequested()) { + current_thread->Exit(); + UNREACHABLE(); + } + } + // Notify the debugger and go to sleep if a breakpoint was hit, // or if the thread is unable to continue for any reason. if (Has(hr, breakpoint) || Has(hr, no_execute)) { diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index 1b577a5b3e..ad73f3eab5 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -36,4 +36,12 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { kernel.CurrentScheduler()->RequestScheduleOnInterrupt(); } +void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) { + for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) { + if (core_mask & (1ULL << core_id)) { + kernel.PhysicalCore(core_id).Interrupt(); + } + } +} + } // namespace Kernel::KInterruptManager diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h index f103dfe3f1..803dc92117 100644 --- a/src/core/hle/kernel/k_interrupt_manager.h +++ b/src/core/hle/kernel/k_interrupt_manager.h @@ -11,6 +11,8 @@ class KernelCore; namespace KInterruptManager { void HandleInterrupt(KernelCore& kernel, s32 core_id); -} +void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask); + +} // namespace KInterruptManager } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 174afc80d7..89b32d509e 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -30,6 +30,7 @@ #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" #include "core/memory.h" @@ -38,6 +39,9 @@ #endif namespace { + +constexpr inline s32 TerminatingThreadPriority = Kernel::Svc::SystemThreadPriorityHighest - 1; + static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, u32 entry_point, u32 arg) { context = {}; @@ -1073,6 +1077,78 @@ void KThread::Exit() { UNREACHABLE_MSG("KThread::Exit() would return"); } +Result KThread::Terminate() { + ASSERT(this != GetCurrentThreadPointer(kernel)); + + // Request the thread terminate if it hasn't already. + if (const auto new_state = this->RequestTerminate(); new_state != ThreadState::Terminated) { + // If the thread isn't terminated, wait for it to terminate. + s32 index; + KSynchronizationObject* objects[] = {this}; + R_TRY(KSynchronizationObject::Wait(kernel, std::addressof(index), objects, 1, + Svc::WaitInfinite)); + } + + return ResultSuccess; +} + +ThreadState KThread::RequestTerminate() { + ASSERT(this != GetCurrentThreadPointer(kernel)); + + KScopedSchedulerLock sl{kernel}; + + // Determine if this is the first termination request. + const bool first_request = [&]() -> bool { + // Perform an atomic compare-and-swap from false to true. + bool expected = false; + return termination_requested.compare_exchange_strong(expected, true); + }(); + + // If this is the first request, start termination procedure. + if (first_request) { + // If the thread is in initialized state, just change state to terminated. + if (this->GetState() == ThreadState::Initialized) { + thread_state = ThreadState::Terminated; + return ThreadState::Terminated; + } + + // Register the terminating dpc. + this->RegisterDpc(DpcFlag::Terminating); + + // If the thread is pinned, unpin it. + if (this->GetStackParameters().is_pinned) { + this->GetOwnerProcess()->UnpinThread(this); + } + + // If the thread is suspended, continue it. + if (this->IsSuspended()) { + suspend_allowed_flags = 0; + this->UpdateState(); + } + + // Change the thread's priority to be higher than any system thread's. + if (this->GetBasePriority() >= Svc::SystemThreadPriorityHighest) { + this->SetBasePriority(TerminatingThreadPriority); + } + + // If the thread is runnable, send a termination interrupt to other cores. + if (this->GetState() == ThreadState::Runnable) { + if (const u64 core_mask = + physical_affinity_mask.GetAffinityMask() & ~(1ULL << GetCurrentCoreId(kernel)); + core_mask != 0) { + Kernel::KInterruptManager::SendInterProcessorInterrupt(kernel, core_mask); + } + } + + // Wake up the thread. + if (this->GetState() == ThreadState::Waiting) { + wait_queue->CancelWait(this, ResultTerminationRequested, true); + } + } + + return this->GetState(); +} + Result KThread::Sleep(s64 timeout) { ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); ASSERT(this == GetCurrentThreadPointer(kernel)); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 9ee20208eb..e2a27d6036 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -180,6 +180,10 @@ public: void Exit(); + Result Terminate(); + + ThreadState RequestTerminate(); + [[nodiscard]] u32 GetSuspendFlags() const { return suspend_allowed_flags & suspend_request_flags; } From 2bb41cffca7e5ec6383a59c513ef9d7e2def5f51 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 Sep 2022 21:12:37 -0700 Subject: [PATCH 165/245] core: hle: kernel: k_memory_block_manager: Update. --- .../hle/kernel/k_memory_block_manager.cpp | 441 +++++++++++------- src/core/hle/kernel/k_memory_block_manager.h | 147 ++++-- 2 files changed, 397 insertions(+), 191 deletions(-) diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp index 3ddb9984fa..c908af75a9 100644 --- a/src/core/hle/kernel/k_memory_block_manager.cpp +++ b/src/core/hle/kernel/k_memory_block_manager.cpp @@ -2,221 +2,336 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_memory_block_manager.h" -#include "core/hle/kernel/memory_types.h" namespace Kernel { -KMemoryBlockManager::KMemoryBlockManager(VAddr start_addr_, VAddr end_addr_) - : start_addr{start_addr_}, end_addr{end_addr_} { - const u64 num_pages{(end_addr - start_addr) / PageSize}; - memory_block_tree.emplace_back(start_addr, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None); +KMemoryBlockManager::KMemoryBlockManager() = default; + +Result KMemoryBlockManager::Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager) { + // Allocate a block to encapsulate the address space, insert it into the tree. + KMemoryBlock* start_block = slab_manager->Allocate(); + R_UNLESS(start_block != nullptr, ResultOutOfResource); + + // Set our start and end. + m_start_address = st; + m_end_address = nd; + ASSERT(Common::IsAligned(m_start_address, PageSize)); + ASSERT(Common::IsAligned(m_end_address, PageSize)); + + // Initialize and insert the block. + start_block->Initialize(m_start_address, (m_end_address - m_start_address) / PageSize, + KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None); + m_memory_block_tree.insert(*start_block); + + return ResultSuccess; } -KMemoryBlockManager::iterator KMemoryBlockManager::FindIterator(VAddr addr) { - auto node{memory_block_tree.begin()}; - while (node != end()) { - const VAddr node_end_addr{node->GetNumPages() * PageSize + node->GetAddress()}; - if (node->GetAddress() <= addr && node_end_addr - 1 >= addr) { - return node; - } - node = std::next(node); +void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, + HostUnmapCallback&& host_unmap_callback) { + // Erase every block until we have none left. + auto it = m_memory_block_tree.begin(); + while (it != m_memory_block_tree.end()) { + KMemoryBlock* block = std::addressof(*it); + it = m_memory_block_tree.erase(it); + slab_manager->Free(block); + host_unmap_callback(block->GetAddress(), block->GetSize()); } - return end(); + + ASSERT(m_memory_block_tree.empty()); } -VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages, - std::size_t num_pages, std::size_t align, - std::size_t offset, std::size_t guard_pages) { - if (num_pages == 0) { - return {}; - } +VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, size_t region_num_pages, + size_t num_pages, size_t alignment, size_t offset, + size_t guard_pages) const { + if (num_pages > 0) { + const VAddr region_end = region_start + region_num_pages * PageSize; + const VAddr region_last = region_end - 1; + for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend(); + it++) { + const KMemoryInfo info = it->GetMemoryInfo(); + if (region_last < info.GetAddress()) { + break; + } + if (info.m_state != KMemoryState::Free) { + continue; + } - const VAddr region_end{region_start + region_num_pages * PageSize}; - const VAddr region_last{region_end - 1}; - for (auto it{FindIterator(region_start)}; it != memory_block_tree.cend(); it++) { - const auto info{it->GetMemoryInfo()}; - if (region_last < info.GetAddress()) { - break; - } + VAddr area = (info.GetAddress() <= region_start) ? region_start : info.GetAddress(); + area += guard_pages * PageSize; - if (info.state != KMemoryState::Free) { - continue; - } + const VAddr offset_area = Common::AlignDown(area, alignment) + offset; + area = (area <= offset_area) ? offset_area : offset_area + alignment; - VAddr area{(info.GetAddress() <= region_start) ? region_start : info.GetAddress()}; - area += guard_pages * PageSize; + const VAddr area_end = area + num_pages * PageSize + guard_pages * PageSize; + const VAddr area_last = area_end - 1; - const VAddr offset_area{Common::AlignDown(area, align) + offset}; - area = (area <= offset_area) ? offset_area : offset_area + align; - - const VAddr area_end{area + num_pages * PageSize + guard_pages * PageSize}; - const VAddr area_last{area_end - 1}; - - if (info.GetAddress() <= area && area < area_last && area_last <= region_last && - area_last <= info.GetLastAddress()) { - return area; + if (info.GetAddress() <= area && area < area_last && area_last <= region_last && + area_last <= info.GetLastAddress()) { + return area; + } } } return {}; } -void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state, - KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, - KMemoryState state, KMemoryPermission perm, - KMemoryAttribute attribute) { - const VAddr update_end_addr{addr + num_pages * PageSize}; - iterator node{memory_block_tree.begin()}; +void KMemoryBlockManager::CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, + VAddr address, size_t num_pages) { + // Find the iterator now that we've updated. + iterator it = this->FindIterator(address); + if (address != m_start_address) { + it--; + } - prev_attribute |= KMemoryAttribute::IpcAndDeviceMapped; - - while (node != memory_block_tree.end()) { - KMemoryBlock* block{&(*node)}; - iterator next_node{std::next(node)}; - const VAddr cur_addr{block->GetAddress()}; - const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; - - if (addr < cur_end_addr && cur_addr < update_end_addr) { - if (!block->HasProperties(prev_state, prev_perm, prev_attribute)) { - node = next_node; - continue; - } - - iterator new_node{node}; - if (addr > cur_addr) { - memory_block_tree.insert(node, block->Split(addr)); - } - - if (update_end_addr < cur_end_addr) { - new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); - } - - new_node->Update(state, perm, attribute); - - MergeAdjacent(new_node, next_node); - } - - if (cur_end_addr - 1 >= update_end_addr - 1) { + // Coalesce blocks that we can. + while (true) { + iterator prev = it++; + if (it == m_memory_block_tree.end()) { break; } - node = next_node; - } -} - -void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState state, - KMemoryPermission perm, KMemoryAttribute attribute) { - const VAddr update_end_addr{addr + num_pages * PageSize}; - iterator node{memory_block_tree.begin()}; - - while (node != memory_block_tree.end()) { - KMemoryBlock* block{&(*node)}; - iterator next_node{std::next(node)}; - const VAddr cur_addr{block->GetAddress()}; - const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; - - if (addr < cur_end_addr && cur_addr < update_end_addr) { - iterator new_node{node}; - - if (addr > cur_addr) { - memory_block_tree.insert(node, block->Split(addr)); - } - - if (update_end_addr < cur_end_addr) { - new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); - } - - new_node->Update(state, perm, attribute); - - MergeAdjacent(new_node, next_node); + if (prev->CanMergeWith(*it)) { + KMemoryBlock* block = std::addressof(*it); + m_memory_block_tree.erase(it); + prev->Add(*block); + allocator->Free(block); + it = prev; } - if (cur_end_addr - 1 >= update_end_addr - 1) { + if (address + num_pages * PageSize < it->GetMemoryInfo().GetEndAddress()) { break; } - - node = next_node; } } -void KMemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, +void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages, KMemoryState state, KMemoryPermission perm, + KMemoryAttribute attr, + KMemoryBlockDisableMergeAttribute set_disable_attr, + KMemoryBlockDisableMergeAttribute clear_disable_attr) { + // Ensure for auditing that we never end up with an invalid tree. + KScopedMemoryBlockManagerAuditor auditor(this); + ASSERT(Common::IsAligned(address, PageSize)); + ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == + KMemoryAttribute::None); + + VAddr cur_address = address; + size_t remaining_pages = num_pages; + iterator it = this->FindIterator(address); + + while (remaining_pages > 0) { + const size_t remaining_size = remaining_pages * PageSize; + KMemoryInfo cur_info = it->GetMemoryInfo(); + if (it->HasProperties(state, perm, attr)) { + // If we already have the right properties, just advance. + if (cur_address + remaining_size < cur_info.GetEndAddress()) { + remaining_pages = 0; + cur_address += remaining_size; + } else { + remaining_pages = + (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; + cur_address = cur_info.GetEndAddress(); + } + } else { + // If we need to, create a new block before and insert it. + if (cur_info.GetAddress() != cur_address) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address); + it = m_memory_block_tree.insert(*new_block); + it++; + + cur_info = it->GetMemoryInfo(); + cur_address = cur_info.GetAddress(); + } + + // If we need to, create a new block after and insert it. + if (cur_info.GetSize() > remaining_size) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address + remaining_size); + it = m_memory_block_tree.insert(*new_block); + + cur_info = it->GetMemoryInfo(); + } + + // Update block state. + it->Update(state, perm, attr, cur_address == address, static_cast(set_disable_attr), + static_cast(clear_disable_attr)); + cur_address += cur_info.GetSize(); + remaining_pages -= cur_info.GetNumPages(); + } + it++; + } + + this->CoalesceForUpdate(allocator, address, num_pages); +} + +void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, + VAddr address, size_t num_pages, KMemoryState test_state, + KMemoryPermission test_perm, KMemoryAttribute test_attr, + KMemoryState state, KMemoryPermission perm, + KMemoryAttribute attr) { + // Ensure for auditing that we never end up with an invalid tree. + KScopedMemoryBlockManagerAuditor auditor(this); + ASSERT(Common::IsAligned(address, PageSize)); + ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == + KMemoryAttribute::None); + + VAddr cur_address = address; + size_t remaining_pages = num_pages; + iterator it = this->FindIterator(address); + + while (remaining_pages > 0) { + const size_t remaining_size = remaining_pages * PageSize; + KMemoryInfo cur_info = it->GetMemoryInfo(); + if (it->HasProperties(test_state, test_perm, test_attr) && + !it->HasProperties(state, perm, attr)) { + // If we need to, create a new block before and insert it. + if (cur_info.GetAddress() != cur_address) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address); + it = m_memory_block_tree.insert(*new_block); + it++; + + cur_info = it->GetMemoryInfo(); + cur_address = cur_info.GetAddress(); + } + + // If we need to, create a new block after and insert it. + if (cur_info.GetSize() > remaining_size) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address + remaining_size); + it = m_memory_block_tree.insert(*new_block); + + cur_info = it->GetMemoryInfo(); + } + + // Update block state. + it->Update(state, perm, attr, false, 0, 0); + cur_address += cur_info.GetSize(); + remaining_pages -= cur_info.GetNumPages(); + } else { + // If we already have the right properties, just advance. + if (cur_address + remaining_size < cur_info.GetEndAddress()) { + remaining_pages = 0; + cur_address += remaining_size; + } else { + remaining_pages = + (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; + cur_address = cur_info.GetEndAddress(); + } + } + it++; + } + + this->CoalesceForUpdate(allocator, address, num_pages); +} + +void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages, MemoryBlockLockFunction lock_func, KMemoryPermission perm) { - const VAddr update_end_addr{addr + num_pages * PageSize}; - iterator node{memory_block_tree.begin()}; + // Ensure for auditing that we never end up with an invalid tree. + KScopedMemoryBlockManagerAuditor auditor(this); + ASSERT(Common::IsAligned(address, PageSize)); - while (node != memory_block_tree.end()) { - KMemoryBlock* block{&(*node)}; - iterator next_node{std::next(node)}; - const VAddr cur_addr{block->GetAddress()}; - const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; + VAddr cur_address = address; + size_t remaining_pages = num_pages; + iterator it = this->FindIterator(address); - if (addr < cur_end_addr && cur_addr < update_end_addr) { - iterator new_node{node}; + const VAddr end_address = address + (num_pages * PageSize); - if (addr > cur_addr) { - memory_block_tree.insert(node, block->Split(addr)); - } + while (remaining_pages > 0) { + const size_t remaining_size = remaining_pages * PageSize; + KMemoryInfo cur_info = it->GetMemoryInfo(); - if (update_end_addr < cur_end_addr) { - new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); - } + // If we need to, create a new block before and insert it. + if (cur_info.m_address != cur_address) { + KMemoryBlock* new_block = allocator->Allocate(); - lock_func(new_node, perm); + it->Split(new_block, cur_address); + it = m_memory_block_tree.insert(*new_block); + it++; - MergeAdjacent(new_node, next_node); + cur_info = it->GetMemoryInfo(); + cur_address = cur_info.GetAddress(); } - if (cur_end_addr - 1 >= update_end_addr - 1) { - break; + if (cur_info.GetSize() > remaining_size) { + // If we need to, create a new block after and insert it. + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address + remaining_size); + it = m_memory_block_tree.insert(*new_block); + + cur_info = it->GetMemoryInfo(); } - node = next_node; + // Call the locked update function. + (std::addressof(*it)->*lock_func)(perm, cur_info.GetAddress() == address, + cur_info.GetEndAddress() == end_address); + cur_address += cur_info.GetSize(); + remaining_pages -= cur_info.GetNumPages(); + it++; } + + this->CoalesceForUpdate(allocator, address, num_pages); } -void KMemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { - const_iterator it{FindIterator(start)}; - KMemoryInfo info{}; - do { - info = it->GetMemoryInfo(); - func(info); - it = std::next(it); - } while (info.addr + info.size - 1 < end - 1 && it != cend()); -} +// Debug. +bool KMemoryBlockManager::CheckState() const { + // Loop over every block, ensuring that we are sorted and coalesced. + auto it = m_memory_block_tree.cbegin(); + auto prev = it++; + while (it != m_memory_block_tree.cend()) { + const KMemoryInfo prev_info = prev->GetMemoryInfo(); + const KMemoryInfo cur_info = it->GetMemoryInfo(); -void KMemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) { - KMemoryBlock* block{&(*it)}; - - auto EraseIt = [&](const iterator it_to_erase) { - if (next_it == it_to_erase) { - next_it = std::next(next_it); + // Sequential blocks which can be merged should be merged. + if (prev->CanMergeWith(*it)) { + return false; } - memory_block_tree.erase(it_to_erase); - }; - if (it != memory_block_tree.begin()) { - KMemoryBlock* prev{&(*std::prev(it))}; + // Sequential blocks should be sequential. + if (prev_info.GetEndAddress() != cur_info.GetAddress()) { + return false; + } - if (block->HasSameProperties(*prev)) { - const iterator prev_it{std::prev(it)}; + // If the block is ipc locked, it must have a count. + if ((cur_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None && + cur_info.m_ipc_lock_count == 0) { + return false; + } - prev->Add(block->GetNumPages()); - EraseIt(it); + // If the block is device shared, it must have a count. + if ((cur_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None && + cur_info.m_device_use_count == 0) { + return false; + } - it = prev_it; - block = prev; + // Advance the iterator. + prev = it++; + } + + // Our loop will miss checking the last block, potentially, so check it. + if (prev != m_memory_block_tree.cend()) { + const KMemoryInfo prev_info = prev->GetMemoryInfo(); + // If the block is ipc locked, it must have a count. + if ((prev_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None && + prev_info.m_ipc_lock_count == 0) { + return false; + } + + // If the block is device shared, it must have a count. + if ((prev_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None && + prev_info.m_device_use_count == 0) { + return false; } } - if (it != cend()) { - const KMemoryBlock* const next{&(*std::next(it))}; - - if (block->HasSameProperties(*next)) { - block->Add(next->GetNumPages()); - EraseIt(std::next(it)); - } - } + return true; } } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index e14741b898..b4ee4e319d 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -4,63 +4,154 @@ #pragma once #include -#include +#include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" #include "core/hle/kernel/k_memory_block.h" namespace Kernel { +class KMemoryBlockManagerUpdateAllocator { +public: + static constexpr size_t MaxBlocks = 2; + +private: + KMemoryBlock* m_blocks[MaxBlocks]; + size_t m_index; + KMemoryBlockSlabManager* m_slab_manager; + +private: + Result Initialize(size_t num_blocks) { + // Check num blocks. + ASSERT(num_blocks <= MaxBlocks); + + // Set index. + m_index = MaxBlocks - num_blocks; + + // Allocate the blocks. + for (size_t i = 0; i < num_blocks && i < MaxBlocks; ++i) { + m_blocks[m_index + i] = m_slab_manager->Allocate(); + R_UNLESS(m_blocks[m_index + i] != nullptr, ResultOutOfResource); + } + + return ResultSuccess; + } + +public: + KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm, + size_t num_blocks = MaxBlocks) + : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { + *out_result = this->Initialize(num_blocks); + } + + ~KMemoryBlockManagerUpdateAllocator() { + for (const auto& block : m_blocks) { + if (block != nullptr) { + m_slab_manager->Free(block); + } + } + } + + KMemoryBlock* Allocate() { + ASSERT(m_index < MaxBlocks); + ASSERT(m_blocks[m_index] != nullptr); + KMemoryBlock* block = nullptr; + std::swap(block, m_blocks[m_index++]); + return block; + } + + void Free(KMemoryBlock* block) { + ASSERT(m_index <= MaxBlocks); + ASSERT(block != nullptr); + if (m_index == 0) { + m_slab_manager->Free(block); + } else { + m_blocks[--m_index] = block; + } + } +}; + class KMemoryBlockManager final { public: - using MemoryBlockTree = std::list; + using MemoryBlockTree = + Common::IntrusiveRedBlackTreeBaseTraits::TreeType; + using MemoryBlockLockFunction = void (KMemoryBlock::*)(KMemoryPermission new_perm, bool left, + bool right); using iterator = MemoryBlockTree::iterator; using const_iterator = MemoryBlockTree::const_iterator; public: - KMemoryBlockManager(VAddr start_addr_, VAddr end_addr_); + KMemoryBlockManager(); + + using HostUnmapCallback = std::function; + + Result Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager); + void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback); iterator end() { - return memory_block_tree.end(); + return m_memory_block_tree.end(); } const_iterator end() const { - return memory_block_tree.end(); + return m_memory_block_tree.end(); } const_iterator cend() const { - return memory_block_tree.cend(); + return m_memory_block_tree.cend(); } - iterator FindIterator(VAddr addr); + VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, + size_t alignment, size_t offset, size_t guard_pages) const; - VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, - std::size_t align, std::size_t offset, std::size_t guard_pages); + void Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages, + KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr, + KMemoryBlockDisableMergeAttribute set_disable_attr, + KMemoryBlockDisableMergeAttribute clear_disable_attr); + void UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages, + MemoryBlockLockFunction lock_func, KMemoryPermission perm); - void Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state, - KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, KMemoryState state, - KMemoryPermission perm, KMemoryAttribute attribute); + void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, + KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, + KMemoryAttribute attr); - void Update(VAddr addr, std::size_t num_pages, KMemoryState state, - KMemoryPermission perm = KMemoryPermission::None, - KMemoryAttribute attribute = KMemoryAttribute::None); + iterator FindIterator(VAddr address) const { + return m_memory_block_tree.find(KMemoryBlock( + address, 1, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None)); + } - using LockFunc = std::function; - void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, - KMemoryPermission perm); + const KMemoryBlock* FindBlock(VAddr address) const { + if (const_iterator it = this->FindIterator(address); it != m_memory_block_tree.end()) { + return std::addressof(*it); + } - using IterateFunc = std::function; - void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); + return nullptr; + } - KMemoryBlock& FindBlock(VAddr addr) { - return *FindIterator(addr); + // Debug. + bool CheckState() const; + +private: + void CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages); + + MemoryBlockTree m_memory_block_tree; + VAddr m_start_address{}; + VAddr m_end_address{}; +}; + +class KScopedMemoryBlockManagerAuditor { +public: + explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) { + ASSERT(m_manager->CheckState()); + } + explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager& m) + : KScopedMemoryBlockManagerAuditor(std::addressof(m)) {} + ~KScopedMemoryBlockManagerAuditor() { + ASSERT(m_manager->CheckState()); } private: - void MergeAdjacent(iterator it, iterator& next_it); - - [[maybe_unused]] const VAddr start_addr; - [[maybe_unused]] const VAddr end_addr; - - MemoryBlockTree memory_block_tree; + KMemoryBlockManager* m_manager; }; } // namespace Kernel From 58eb6953d1417d667af36461ac8391e005f49457 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 Sep 2022 21:17:52 -0700 Subject: [PATCH 166/245] core: hle: kernel: k_memory_block: Update. --- src/core/hle/kernel/k_memory_block.h | 514 ++++++++++++++++++++------- src/core/hle/service/ldr/ldr.cpp | 4 +- 2 files changed, 395 insertions(+), 123 deletions(-) diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 18df1f836a..9444f6bd28 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -6,6 +6,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" #include "core/hle/kernel/memory_types.h" #include "core/hle/kernel/svc_types.h" @@ -168,9 +169,8 @@ constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission per enum class KMemoryAttribute : u8 { None = 0x00, - Mask = 0x7F, - All = Mask, - DontCareMask = 0x80, + All = 0xFF, + UserMask = All, Locked = static_cast(Svc::MemoryAttribute::Locked), IpcLocked = static_cast(Svc::MemoryAttribute::IpcLocked), @@ -178,76 +178,112 @@ enum class KMemoryAttribute : u8 { Uncached = static_cast(Svc::MemoryAttribute::Uncached), SetMask = Uncached, - - IpcAndDeviceMapped = IpcLocked | DeviceShared, - LockedAndIpcLocked = Locked | IpcLocked, - DeviceSharedAndUncached = DeviceShared | Uncached }; DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute); -static_assert((static_cast(KMemoryAttribute::Mask) & - static_cast(KMemoryAttribute::DontCareMask)) == 0); +enum class KMemoryBlockDisableMergeAttribute : u8 { + None = 0, + Normal = (1u << 0), + DeviceLeft = (1u << 1), + IpcLeft = (1u << 2), + Locked = (1u << 3), + DeviceRight = (1u << 4), + + AllLeft = Normal | DeviceLeft | IpcLeft | Locked, + AllRight = DeviceRight, +}; +DECLARE_ENUM_FLAG_OPERATORS(KMemoryBlockDisableMergeAttribute); struct KMemoryInfo { - VAddr addr{}; - std::size_t size{}; - KMemoryState state{}; - KMemoryPermission perm{}; - KMemoryAttribute attribute{}; - KMemoryPermission original_perm{}; - u16 ipc_lock_count{}; - u16 device_use_count{}; + uintptr_t m_address; + size_t m_size; + KMemoryState m_state; + u16 m_device_disable_merge_left_count; + u16 m_device_disable_merge_right_count; + u16 m_ipc_lock_count; + u16 m_device_use_count; + u16 m_ipc_disable_merge_count; + KMemoryPermission m_permission; + KMemoryAttribute m_attribute; + KMemoryPermission m_original_permission; + KMemoryBlockDisableMergeAttribute m_disable_merge_attribute; constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { return { - addr, - size, - static_cast(state & KMemoryState::Mask), - static_cast(attribute & KMemoryAttribute::Mask), - static_cast(perm & KMemoryPermission::UserMask), - ipc_lock_count, - device_use_count, + .addr = m_address, + .size = m_size, + .state = static_cast(m_state & KMemoryState::Mask), + .attr = static_cast(m_attribute & KMemoryAttribute::UserMask), + .perm = static_cast(m_permission & KMemoryPermission::UserMask), + .ipc_refcount = m_ipc_lock_count, + .device_refcount = m_device_use_count, + .padding = {}, }; } - constexpr VAddr GetAddress() const { - return addr; + constexpr uintptr_t GetAddress() const { + return m_address; } - constexpr std::size_t GetSize() const { - return size; + + constexpr size_t GetSize() const { + return m_size; } - constexpr std::size_t GetNumPages() const { - return GetSize() / PageSize; + + constexpr size_t GetNumPages() const { + return this->GetSize() / PageSize; } - constexpr VAddr GetEndAddress() const { - return GetAddress() + GetSize(); + + constexpr uintptr_t GetEndAddress() const { + return this->GetAddress() + this->GetSize(); } - constexpr VAddr GetLastAddress() const { - return GetEndAddress() - 1; + + constexpr uintptr_t GetLastAddress() const { + return this->GetEndAddress() - 1; } + + constexpr u16 GetIpcLockCount() const { + return m_ipc_lock_count; + } + + constexpr u16 GetIpcDisableMergeCount() const { + return m_ipc_disable_merge_count; + } + constexpr KMemoryState GetState() const { - return state; - } - constexpr KMemoryAttribute GetAttribute() const { - return attribute; + return m_state; } + constexpr KMemoryPermission GetPermission() const { - return perm; + return m_permission; + } + + constexpr KMemoryPermission GetOriginalPermission() const { + return m_original_permission; + } + + constexpr KMemoryAttribute GetAttribute() const { + return m_attribute; + } + + constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const { + return m_disable_merge_attribute; } }; -class KMemoryBlock final { - friend class KMemoryBlockManager; - +class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode { private: - VAddr addr{}; - std::size_t num_pages{}; - KMemoryState state{KMemoryState::None}; - u16 ipc_lock_count{}; - u16 device_use_count{}; - KMemoryPermission perm{KMemoryPermission::None}; - KMemoryPermission original_perm{KMemoryPermission::None}; - KMemoryAttribute attribute{KMemoryAttribute::None}; + u16 m_device_disable_merge_left_count; + u16 m_device_disable_merge_right_count; + VAddr m_address; + size_t m_num_pages; + KMemoryState m_memory_state; + u16 m_ipc_lock_count; + u16 m_device_use_count; + u16 m_ipc_disable_merge_count; + KMemoryPermission m_permission; + KMemoryPermission m_original_permission; + KMemoryAttribute m_attribute; + KMemoryBlockDisableMergeAttribute m_disable_merge_attribute; public: static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) { @@ -261,113 +297,349 @@ public: } public: - constexpr KMemoryBlock() = default; - constexpr KMemoryBlock(VAddr addr_, std::size_t num_pages_, KMemoryState state_, - KMemoryPermission perm_, KMemoryAttribute attribute_) - : addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {} - constexpr VAddr GetAddress() const { - return addr; + return m_address; } - constexpr std::size_t GetNumPages() const { - return num_pages; + constexpr size_t GetNumPages() const { + return m_num_pages; } - constexpr std::size_t GetSize() const { - return GetNumPages() * PageSize; + constexpr size_t GetSize() const { + return this->GetNumPages() * PageSize; } constexpr VAddr GetEndAddress() const { - return GetAddress() + GetSize(); + return this->GetAddress() + this->GetSize(); } constexpr VAddr GetLastAddress() const { - return GetEndAddress() - 1; + return this->GetEndAddress() - 1; + } + + constexpr u16 GetIpcLockCount() const { + return m_ipc_lock_count; + } + + constexpr u16 GetIpcDisableMergeCount() const { + return m_ipc_disable_merge_count; + } + + constexpr KMemoryPermission GetPermission() const { + return m_permission; + } + + constexpr KMemoryPermission GetOriginalPermission() const { + return m_original_permission; + } + + constexpr KMemoryAttribute GetAttribute() const { + return m_attribute; } constexpr KMemoryInfo GetMemoryInfo() const { return { - GetAddress(), GetSize(), state, perm, - attribute, original_perm, ipc_lock_count, device_use_count, + .m_address = this->GetAddress(), + .m_size = this->GetSize(), + .m_state = m_memory_state, + .m_device_disable_merge_left_count = m_device_disable_merge_left_count, + .m_device_disable_merge_right_count = m_device_disable_merge_right_count, + .m_ipc_lock_count = m_ipc_lock_count, + .m_device_use_count = m_device_use_count, + .m_ipc_disable_merge_count = m_ipc_disable_merge_count, + .m_permission = m_permission, + .m_attribute = m_attribute, + .m_original_permission = m_original_permission, + .m_disable_merge_attribute = m_disable_merge_attribute, }; } - void ShareToDevice(KMemoryPermission /*new_perm*/) { - ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared || - device_use_count == 0); - attribute |= KMemoryAttribute::DeviceShared; - const u16 new_use_count{++device_use_count}; - ASSERT(new_use_count > 0); +public: + explicit KMemoryBlock() = default; + + constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, + KMemoryAttribute attr) + : Common::IntrusiveRedBlackTreeBaseNode(), + m_device_disable_merge_left_count(), m_device_disable_merge_right_count(), + m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0), + m_device_use_count(0), m_ipc_disable_merge_count(), m_permission(p), + m_original_permission(KMemoryPermission::None), m_attribute(attr), + m_disable_merge_attribute() {} + + constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, + KMemoryAttribute attr) { + m_device_disable_merge_left_count = 0; + m_device_disable_merge_right_count = 0; + m_address = addr; + m_num_pages = np; + m_memory_state = ms; + m_ipc_lock_count = 0; + m_device_use_count = 0; + m_permission = p; + m_original_permission = KMemoryPermission::None; + m_attribute = attr; + m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None; } - void UnshareToDevice(KMemoryPermission /*new_perm*/) { - ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); - const u16 prev_use_count{device_use_count--}; - ASSERT(prev_use_count > 0); - if (prev_use_count == 1) { - attribute &= ~KMemoryAttribute::DeviceShared; - } - } - -private: constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const { - constexpr KMemoryAttribute AttributeIgnoreMask{KMemoryAttribute::DontCareMask | - KMemoryAttribute::IpcLocked | - KMemoryAttribute::DeviceShared}; - return state == s && perm == p && - (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); + constexpr auto AttributeIgnoreMask = + KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; + return m_memory_state == s && m_permission == p && + (m_attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); } constexpr bool HasSameProperties(const KMemoryBlock& rhs) const { - return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm && - attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count && - device_use_count == rhs.device_use_count; + return m_memory_state == rhs.m_memory_state && m_permission == rhs.m_permission && + m_original_permission == rhs.m_original_permission && + m_attribute == rhs.m_attribute && m_ipc_lock_count == rhs.m_ipc_lock_count && + m_device_use_count == rhs.m_device_use_count; } - constexpr bool Contains(VAddr start) const { - return GetAddress() <= start && start <= GetEndAddress(); + constexpr bool CanMergeWith(const KMemoryBlock& rhs) const { + return this->HasSameProperties(rhs) && + (m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllRight) == + KMemoryBlockDisableMergeAttribute::None && + (rhs.m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllLeft) == + KMemoryBlockDisableMergeAttribute::None; } - constexpr void Add(std::size_t count) { - ASSERT(count > 0); - ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1); - - num_pages += count; + constexpr bool Contains(VAddr addr) const { + return this->GetAddress() <= addr && addr <= this->GetEndAddress(); } - constexpr void Update(KMemoryState new_state, KMemoryPermission new_perm, - KMemoryAttribute new_attribute) { - ASSERT(original_perm == KMemoryPermission::None); - ASSERT((attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None); + constexpr void Add(const KMemoryBlock& added_block) { + ASSERT(added_block.GetNumPages() > 0); + ASSERT(this->GetAddress() + added_block.GetSize() - 1 < + this->GetEndAddress() + added_block.GetSize() - 1); - state = new_state; - perm = new_perm; - - attribute = static_cast( - new_attribute | - (attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared))); + m_num_pages += added_block.GetNumPages(); + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | added_block.m_disable_merge_attribute); + m_device_disable_merge_right_count = added_block.m_device_disable_merge_right_count; } - constexpr KMemoryBlock Split(VAddr split_addr) { - ASSERT(GetAddress() < split_addr); - ASSERT(Contains(split_addr)); - ASSERT(Common::IsAligned(split_addr, PageSize)); + constexpr void Update(KMemoryState s, KMemoryPermission p, KMemoryAttribute a, + bool set_disable_merge_attr, u8 set_mask, u8 clear_mask) { + ASSERT(m_original_permission == KMemoryPermission::None); + ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None); - KMemoryBlock block; - block.addr = addr; - block.num_pages = (split_addr - GetAddress()) / PageSize; - block.state = state; - block.ipc_lock_count = ipc_lock_count; - block.device_use_count = device_use_count; - block.perm = perm; - block.original_perm = original_perm; - block.attribute = attribute; + m_memory_state = s; + m_permission = p; + m_attribute = static_cast( + a | (m_attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared))); - addr = split_addr; - num_pages -= block.num_pages; + if (set_disable_merge_attr && set_mask != 0) { + m_disable_merge_attribute = m_disable_merge_attribute | + static_cast(set_mask); + } + if (clear_mask != 0) { + m_disable_merge_attribute = m_disable_merge_attribute & + static_cast(~clear_mask); + } + } - return block; + constexpr void Split(KMemoryBlock* block, VAddr addr) { + ASSERT(this->GetAddress() < addr); + ASSERT(this->Contains(addr)); + ASSERT(Common::IsAligned(addr, PageSize)); + + block->m_address = m_address; + block->m_num_pages = (addr - this->GetAddress()) / PageSize; + block->m_memory_state = m_memory_state; + block->m_ipc_lock_count = m_ipc_lock_count; + block->m_device_use_count = m_device_use_count; + block->m_permission = m_permission; + block->m_original_permission = m_original_permission; + block->m_attribute = m_attribute; + block->m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllLeft); + block->m_ipc_disable_merge_count = m_ipc_disable_merge_count; + block->m_device_disable_merge_left_count = m_device_disable_merge_left_count; + block->m_device_disable_merge_right_count = 0; + + m_address = addr; + m_num_pages -= block->m_num_pages; + + m_ipc_disable_merge_count = 0; + m_device_disable_merge_left_count = 0; + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllRight); + } + + constexpr void UpdateDeviceDisableMergeStateForShareLeft( + [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { + if (left) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft); + const u16 new_device_disable_merge_left_count = ++m_device_disable_merge_left_count; + ASSERT(new_device_disable_merge_left_count > 0); + } + } + + constexpr void UpdateDeviceDisableMergeStateForShareRight( + [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) { + if (right) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight); + const u16 new_device_disable_merge_right_count = ++m_device_disable_merge_right_count; + ASSERT(new_device_disable_merge_right_count > 0); + } + } + + constexpr void UpdateDeviceDisableMergeStateForShare(KMemoryPermission new_perm, bool left, + bool right) { + this->UpdateDeviceDisableMergeStateForShareLeft(new_perm, left, right); + this->UpdateDeviceDisableMergeStateForShareRight(new_perm, left, right); + } + + constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left, + bool right) { + // We must either be shared or have a zero lock count. + ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared || + m_device_use_count == 0); + + // Share. + const u16 new_count = ++m_device_use_count; + ASSERT(new_count > 0); + + m_attribute = static_cast(m_attribute | KMemoryAttribute::DeviceShared); + + this->UpdateDeviceDisableMergeStateForShare(new_perm, left, right); + } + + constexpr void UpdateDeviceDisableMergeStateForUnshareLeft( + [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { + + if (left) { + if (!m_device_disable_merge_left_count) { + return; + } + --m_device_disable_merge_left_count; + } + + m_device_disable_merge_left_count = + std::min(m_device_disable_merge_left_count, m_device_use_count); + + if (m_device_disable_merge_left_count == 0) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::DeviceLeft); + } + } + + constexpr void UpdateDeviceDisableMergeStateForUnshareRight( + [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) { + if (right) { + const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--; + ASSERT(old_device_disable_merge_right_count > 0); + if (old_device_disable_merge_right_count == 1) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::DeviceRight); + } + } + } + + constexpr void UpdateDeviceDisableMergeStateForUnshare(KMemoryPermission new_perm, bool left, + bool right) { + this->UpdateDeviceDisableMergeStateForUnshareLeft(new_perm, left, right); + this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right); + } + + constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left, + bool right) { + // We must be shared. + ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); + + // Unhare. + const u16 old_count = m_device_use_count--; + ASSERT(old_count > 0); + + if (old_count == 1) { + m_attribute = + static_cast(m_attribute & ~KMemoryAttribute::DeviceShared); + } + + this->UpdateDeviceDisableMergeStateForUnshare(new_perm, left, right); + } + + constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left, + bool right) { + + // We must be shared. + ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); + + // Unhare. + const u16 old_count = m_device_use_count--; + ASSERT(old_count > 0); + + if (old_count == 1) { + m_attribute = + static_cast(m_attribute & ~KMemoryAttribute::DeviceShared); + } + + this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right); + } + + constexpr void LockForIpc(KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { + // We must either be locked or have a zero lock count. + ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked || + m_ipc_lock_count == 0); + + // Lock. + const u16 new_lock_count = ++m_ipc_lock_count; + ASSERT(new_lock_count > 0); + + // If this is our first lock, update our permissions. + if (new_lock_count == 1) { + ASSERT(m_original_permission == KMemoryPermission::None); + ASSERT((m_permission | new_perm | KMemoryPermission::NotMapped) == + (m_permission | KMemoryPermission::NotMapped)); + ASSERT((m_permission & KMemoryPermission::UserExecute) != + KMemoryPermission::UserExecute || + (new_perm == KMemoryPermission::UserRead)); + m_original_permission = m_permission; + m_permission = static_cast( + (new_perm & KMemoryPermission::IpcLockChangeMask) | + (m_original_permission & ~KMemoryPermission::IpcLockChangeMask)); + } + m_attribute = static_cast(m_attribute | KMemoryAttribute::IpcLocked); + + if (left) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::IpcLeft); + const u16 new_ipc_disable_merge_count = ++m_ipc_disable_merge_count; + ASSERT(new_ipc_disable_merge_count > 0); + } + } + + constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left, + [[maybe_unused]] bool right) { + // We must be locked. + ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked); + + // Unlock. + const u16 old_lock_count = m_ipc_lock_count--; + ASSERT(old_lock_count > 0); + + // If this is our last unlock, update our permissions. + if (old_lock_count == 1) { + ASSERT(m_original_permission != KMemoryPermission::None); + m_permission = m_original_permission; + m_original_permission = KMemoryPermission::None; + m_attribute = static_cast(m_attribute & ~KMemoryAttribute::IpcLocked); + } + + if (left) { + const u16 old_ipc_disable_merge_count = m_ipc_disable_merge_count--; + ASSERT(old_ipc_disable_merge_count > 0); + if (old_ipc_disable_merge_count == 1) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::IpcLeft); + } + } + } + + constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const { + return m_disable_merge_attribute; } }; static_assert(std::is_trivially_destructible::value); diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index becd6d1b9f..652441bc29 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -290,7 +290,7 @@ public: const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; const auto start_info{page_table.QueryInfo(start - 1)}; - if (start_info.state != Kernel::KMemoryState::Free) { + if (start_info.GetState() != Kernel::KMemoryState::Free) { return {}; } @@ -300,7 +300,7 @@ public: const auto end_info{page_table.QueryInfo(start + size)}; - if (end_info.state != Kernel::KMemoryState::Free) { + if (end_info.GetState() != Kernel::KMemoryState::Free) { return {}; } From ed591934fbffa32af0151302fd07e9fce776eb17 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 Sep 2022 21:38:28 -0700 Subject: [PATCH 167/245] core: hle: kernel: k_page_table: Update, and integrate with new KMemoryBlockManager/SlabManager. --- src/core/hle/kernel/k_page_table.cpp | 619 ++++++++++++++++----------- src/core/hle/kernel/k_page_table.h | 25 +- 2 files changed, 393 insertions(+), 251 deletions(-) diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 8ebb753381..2cf46af0a4 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -49,6 +49,7 @@ KPageTable::~KPageTable() = default; Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, VAddr code_addr, std::size_t code_size, + KMemoryBlockSlabManager* mem_block_slab_manager, KMemoryManager::Pool pool) { const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { @@ -113,6 +114,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type address_space_start = start; address_space_end = end; is_kernel = false; + memory_block_slab_manager = mem_block_slab_manager; // Determine the region we can place our undetermineds in VAddr alloc_start{}; @@ -254,7 +256,14 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type page_table_impl.Resize(address_space_width, PageBits); - return InitializeMemoryLayout(start, end); + return memory_block_manager.Initialize(address_space_start, address_space_end, + memory_block_slab_manager); +} + +void KPageTable::Finalize() { + memory_block_manager.Finalize(memory_block_slab_manager, [&](VAddr addr, u64 size) { + system.Memory().UnmapRegion(page_table_impl, addr, size); + }); } Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, @@ -271,6 +280,13 @@ Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryStat R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager); + + // Allocate and open. KPageGroup pg; R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( &pg, num_pages, @@ -278,7 +294,10 @@ Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryStat R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); - block_manager->Update(addr, num_pages, state, perm); + // Update the blocks. + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } @@ -307,6 +326,18 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator( + std::addressof(src_allocator_result), memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator( + std::addressof(dst_allocator_result), memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); + // Map the code memory. { // Determine the number of pages being operated on. @@ -335,10 +366,14 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size unprot_guard.Cancel(); // Apply the memory block updates. - block_manager->Update(src_address, num_pages, src_state, new_perm, - KMemoryAttribute::Locked); - block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, - KMemoryAttribute::None); + memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, + src_state, new_perm, KMemoryAttribute::Locked, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::AliasCode, new_perm, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); } return ResultSuccess; @@ -370,7 +405,7 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si // Determine whether any pages being unmapped are code. bool any_code_pages = false; { - KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); + KMemoryBlockManager::const_iterator it = memory_block_manager.FindIterator(dst_address); while (true) { // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -408,6 +443,20 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si // Determine the number of pages being operated on. const std::size_t num_pages = size / PageSize; + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + memory_block_slab_manager, + num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + memory_block_slab_manager, + num_dst_allocator_blocks); + R_TRY(dst_allocator_result); + // Unmap the aliased copy of the pages. R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); @@ -416,9 +465,14 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si OperationType::ChangePermissions)); // Apply the memory block updates. - block_manager->Update(dst_address, num_pages, KMemoryState::None); - block_manager->Update(src_address, num_pages, KMemoryState::Normal, - KMemoryPermission::UserReadWrite); + memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); + memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, + KMemoryState::Normal, KMemoryPermission::UserReadWrite, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); // Note that we reprotected pages. reprotected_pages = true; @@ -434,55 +488,12 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, if (num_pages <= region_num_pages) { if (this->IsAslrEnabled()) { - // Try to directly find a free area up to 8 times. - for (std::size_t i = 0; i < 8; i++) { - const std::size_t random_offset = - KSystemControl::GenerateRandomRange( - 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * - alignment; - const VAddr candidate = - Common::AlignDown((region_start + random_offset), alignment) + offset; - - KMemoryInfo info = this->QueryInfoImpl(candidate); - - if (info.state != KMemoryState::Free) { - continue; - } - if (region_start > candidate) { - continue; - } - if (info.GetAddress() + guard_pages * PageSize > candidate) { - continue; - } - - const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1; - if (candidate_end > info.GetLastAddress()) { - continue; - } - if (candidate_end > region_start + region_num_pages * PageSize - 1) { - continue; - } - - address = candidate; - break; - } - // Fall back to finding the first free area with a random offset. - if (address == 0) { - // NOTE: Nintendo does not account for guard pages here. - // This may theoretically cause an offset to be chosen that cannot be mapped. We - // will account for guard pages. - const std::size_t offset_pages = KSystemControl::GenerateRandomRange( - 0, region_num_pages - num_pages - guard_pages); - address = block_manager->FindFreeArea(region_start + offset_pages * PageSize, - region_num_pages - offset_pages, num_pages, - alignment, offset, guard_pages); - } + UNIMPLEMENTED(); } - // Find the first free area. if (address == 0) { - address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, - alignment, offset, guard_pages); + address = memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages, + alignment, offset, guard_pages); } } @@ -649,11 +660,19 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTab KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Apply the memory block update. - block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None); + memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); system.InvalidateCpuInstructionCaches(); @@ -682,10 +701,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { cur_address = address; mapped_size = 0; - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -739,10 +758,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { size_t checked_mapped_size = 0; cur_address = address; - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -782,6 +801,14 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { } } + // Create an update allocator. + ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, + num_allocator_blocks); + R_TRY(allocator_result); + // Reset the current tracking address, and make sure we clean up on failure. cur_address = address; auto unmap_guard = detail::ScopeExit([&] { @@ -791,10 +818,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { // Iterate, unmapping the pages. cur_address = address; - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -830,10 +857,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { PAddr pg_phys_addr = pg_it->GetAddress(); size_t pg_pages = pg_it->GetNumPages(); - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -889,10 +916,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { mapped_physical_memory_size += (size - mapped_size); // Update the relevant memory blocks. - block_manager->Update(address, size / PageSize, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None, - KMemoryState::Normal, KMemoryPermission::UserReadWrite, - KMemoryAttribute::None); + memory_block_manager.UpdateIfMatch( + std::addressof(allocator), address, size / PageSize, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None); // Cancel our guard. unmap_guard.Cancel(); @@ -924,10 +951,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { cur_address = address; mapped_size = 0; - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -1022,6 +1049,13 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { } ASSERT(pg.GetNumPages() == mapped_size / PageSize); + // Create an update allocator. + ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Reset the current tracking address, and make sure we clean up on failure. cur_address = address; auto remap_guard = detail::ScopeExit([&] { @@ -1030,7 +1064,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { cur_address = address; // Iterate over the memory we unmapped. - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); auto pg_it = pg.Nodes().begin(); PAddr pg_phys_addr = pg_it->GetAddress(); size_t pg_pages = pg_it->GetNumPages(); @@ -1085,10 +1119,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { }); // Iterate over the memory, unmapping as we go. - auto it = block_manager->FindIterator(cur_address); + auto it = memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -1120,8 +1154,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); // Update memory blocks. - block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None); + memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, + KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // TODO(bunnei): This is a workaround until the next set of changes, where we add reference // counting for mapped pages. Until then, we must manually close the reference to the page @@ -1134,83 +1170,134 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { return ResultSuccess; } -Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, std::size_t size) { + // Lock the table. KScopedLightLock lk(general_lock); - KMemoryState src_state{}; - CASCADE_CODE(CheckMemoryState( - &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, - KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::UserReadWrite, - KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); + // Validate that the source address's state is valid. + KMemoryState src_state; + size_t num_src_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr, + std::addressof(num_src_allocator_blocks), src_address, size, + KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, + KMemoryAttribute::All, KMemoryAttribute::None)); - if (IsRegionMapped(dst_addr, size)) { - return ResultInvalidCurrentMemory; - } + // Validate that the dst address's state is valid. + size_t num_dst_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, + KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryAttribute::None)); + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator( + std::addressof(src_allocator_result), memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator( + std::addressof(dst_allocator_result), memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); + + // Map the memory. KPageGroup page_linked_list; const std::size_t num_pages{size / PageSize}; + const KMemoryPermission new_src_perm = static_cast( + KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); + const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; - AddRegionToPages(src_addr, num_pages, page_linked_list); - + AddRegionToPages(src_address, num_pages, page_linked_list); { + // Reprotect the source as kernel-read/not mapped. auto block_guard = detail::ScopeExit([&] { - Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, + Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, OperationType::ChangePermissions); }); - - CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, - OperationType::ChangePermissions)); - CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::UserReadWrite)); + R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); + R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); block_guard.Cancel(); } - block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None, - KMemoryAttribute::Locked); - block_manager->Update(dst_addr, num_pages, KMemoryState::Stack, - KMemoryPermission::UserReadWrite); + // Apply the memory block updates. + memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, + new_src_perm, new_src_attr, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::Stack, KMemoryPermission::UserReadWrite, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } -Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, std::size_t size) { + // Lock the table. KScopedLightLock lk(general_lock); - KMemoryState src_state{}; - CASCADE_CODE(CheckMemoryState( - &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, - KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None, - KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); + // Validate that the source address's state is valid. + KMemoryState src_state; + size_t num_src_allocator_blocks; + R_TRY(this->CheckMemoryState( + std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks), + src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, + KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead, + KMemoryAttribute::All, KMemoryAttribute::Locked)); - KMemoryPermission dst_perm{}; - CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size, - KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); + // Validate that the dst address's state is valid. + KMemoryPermission dst_perm; + size_t num_dst_allocator_blocks; + R_TRY(this->CheckMemoryState( + nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks), + dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator( + std::addressof(src_allocator_result), memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator( + std::addressof(dst_allocator_result), memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); KPageGroup src_pages; KPageGroup dst_pages; const std::size_t num_pages{size / PageSize}; - AddRegionToPages(src_addr, num_pages, src_pages); - AddRegionToPages(dst_addr, num_pages, dst_pages); + AddRegionToPages(src_address, num_pages, src_pages); + AddRegionToPages(dst_address, num_pages, dst_pages); if (!dst_pages.IsEqual(src_pages)) { return ResultInvalidMemoryRegion; } { - auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); }); + auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); - CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); - CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, - OperationType::ChangePermissions)); + R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, + OperationType::ChangePermissions)); block_guard.Cancel(); } - block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::UserReadWrite); - block_manager->Update(dst_addr, num_pages, KMemoryState::Free); + // Apply the memory block updates. + memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); + memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::None, KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); return ResultSuccess; } @@ -1254,11 +1341,18 @@ Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemory KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager); + // Map the pages. R_TRY(MapPages(address, page_linked_list, perm)); // Update the blocks. - block_manager->Update(address, num_pages, state, perm); + memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } @@ -1288,6 +1382,11 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t KMemoryAttribute::None, KMemoryAttribute::None) .IsSuccess()); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager); + // Perform mapping operation. if (is_pa_valid) { R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); @@ -1296,7 +1395,9 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t } // Update the blocks. - block_manager->Update(addr, num_pages, state, perm); + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); // We successfully mapped the pages. *out_addr = addr; @@ -1321,25 +1422,36 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { return ResultSuccess; } -Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) { +Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { // Check that the unmap is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; - R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. KScopedLightLock lk(general_lock); // Check the memory state. - R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, + KMemoryState::All, state, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform the unmap. - R_TRY(UnmapPages(addr, page_linked_list)); + R_TRY(UnmapPages(address, page_linked_list)); // Update the blocks. - block_manager->Update(addr, num_pages, state, KMemoryPermission::None); + memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); return ResultSuccess; } @@ -1359,11 +1471,20 @@ Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform the unmap. R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Update the blocks. - block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); + memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); return ResultSuccess; } @@ -1435,13 +1556,21 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, // Succeed if there's nothing to do. R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform mapping operation. const auto operation = was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions; R_TRY(Operate(addr, num_pages, new_perm, operation)); // Update the blocks. - block_manager->Update(addr, num_pages, new_state, new_perm, KMemoryAttribute::None); + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // Ensure cache coherency, if we're setting pages as executable. if (is_x) { @@ -1454,51 +1583,30 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { KScopedLightLock lk(general_lock); - return block_manager->FindBlock(addr).GetMemoryInfo(); + return memory_block_manager.FindBlock(addr)->GetMemoryInfo(); } KMemoryInfo KPageTable::QueryInfo(VAddr addr) { if (!Contains(addr, 1)) { - return {address_space_end, 0 - address_space_end, KMemoryState::Inaccessible, - KMemoryPermission::None, KMemoryAttribute::None, KMemoryPermission::None}; + return { + .m_address = address_space_end, + .m_size = 0 - address_space_end, + .m_state = static_cast(Svc::MemoryState::Inaccessible), + .m_device_disable_merge_left_count = 0, + .m_device_disable_merge_right_count = 0, + .m_ipc_lock_count = 0, + .m_device_use_count = 0, + .m_ipc_disable_merge_count = 0, + .m_permission = KMemoryPermission::None, + .m_attribute = KMemoryAttribute::None, + .m_original_permission = KMemoryPermission::None, + .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None, + }; } return QueryInfoImpl(addr); } -Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { - KScopedLightLock lk(general_lock); - - KMemoryState state{}; - KMemoryAttribute attribute{}; - - R_TRY(CheckMemoryState(&state, nullptr, &attribute, nullptr, addr, size, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryPermission::All, KMemoryPermission::UserReadWrite, - KMemoryAttribute::Mask, KMemoryAttribute::None, - KMemoryAttribute::IpcAndDeviceMapped)); - - block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked); - - return ResultSuccess; -} - -Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryState state{}; - - R_TRY(CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); - - block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite); - return ResultSuccess; -} - Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; @@ -1509,20 +1617,30 @@ Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, // Verify we can change the memory permission. KMemoryState old_state; KMemoryPermission old_perm; - R_TRY(this->CheckMemoryState( - std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size, - KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, + std::addressof(num_allocator_blocks), addr, size, + KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::All, KMemoryAttribute::None)); // Determine new perm. const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); R_SUCCEED_IF(old_perm == new_perm); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform mapping operation. R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); // Update the blocks. - block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None); + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } @@ -1548,6 +1666,12 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Determine the new attribute. const KMemoryAttribute new_attr = static_cast(((old_attr & static_cast(~mask)) | @@ -1557,7 +1681,9 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3 this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh); // Update the blocks. - block_manager->Update(addr, num_pages, old_state, old_perm, new_attr); + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, + new_attr, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } @@ -1603,6 +1729,12 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator( + std::addressof(allocator_result), memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Unmap the end of the heap. const auto num_pages = (GetHeapSize() - size) / PageSize; R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None, @@ -1613,8 +1745,12 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { LimitableResource::PhysicalMemory, num_pages * PageSize); // Apply the memory block update. - block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None); + memory_block_manager.Update(std::addressof(allocator), heap_region_start + size, + num_pages, KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + size == 0 ? KMemoryBlockDisableMergeAttribute::Normal + : KMemoryBlockDisableMergeAttribute::None); // Update the current heap end. current_heap_end = heap_region_start + size; @@ -1667,6 +1803,12 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator( + std::addressof(allocator_result), memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Map the pages. const auto num_pages = allocation_size / PageSize; R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); @@ -1681,8 +1823,12 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { memory_reservation.Commit(); // Apply the memory block update. - block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal, - KMemoryPermission::UserReadWrite, KMemoryAttribute::None); + memory_block_manager.Update( + std::addressof(allocator), current_heap_end, num_pages, KMemoryState::Normal, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + heap_region_start == current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal + : KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // Update the current heap end. current_heap_end = heap_region_start + size; @@ -1713,6 +1859,11 @@ ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, return ResultOutOfMemory; } + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager); + if (is_map_only) { R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); } else { @@ -1723,53 +1874,38 @@ ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); } - block_manager->Update(addr, needed_num_pages, state, perm); + // Update the blocks. + memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return addr; } -Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { +Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, std::size_t size) { + // Lightly validate the range before doing anything else. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. KScopedLightLock lk(general_lock); - KMemoryPermission perm{}; - if (const Result result{CheckMemoryState( - nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, - KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, - KMemoryAttribute::DeviceSharedAndUncached)}; - result.IsError()) { - return result; - } + // Check the memory state. + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryStateContiguous( + std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap, + KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->ShareToDevice(permission); - }, - perm); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); - return ResultSuccess; -} - -Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryPermission perm{}; - if (const Result result{CheckMemoryState( - nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, - KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, - KMemoryAttribute::DeviceSharedAndUncached)}; - result.IsError()) { - return result; - } - - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->UnshareToDevice(permission); - }, - perm); + // Update the memory blocks. + memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, + &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); return ResultSuccess; } @@ -1791,19 +1927,6 @@ Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPage KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); } -Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { - block_manager = std::make_unique(start, end); - - return ResultSuccess; -} - -bool KPageTable::IsRegionMapped(VAddr address, u64 size) { - return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, - KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped) - .IsError(); -} - bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { auto start_ptr = system.DeviceMemory().GetPointer(addr); for (u64 offset{}; offset < size; offset += PageSize) { @@ -1831,8 +1954,8 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page if (is_aslr_enabled) { UNIMPLEMENTED(); } - return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, - IsKernel() ? 1 : 4); + return memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, + IsKernel() ? 1 : 4); } Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, @@ -2008,9 +2131,9 @@ Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_ KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const { // Validate the states match expectation. - R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory); - R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory); - R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory); + R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory); + R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory); + R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory); return ResultSuccess; } @@ -2024,7 +2147,7 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA // Get information about the first block. const VAddr last_addr = addr + size - 1; - KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); + KMemoryBlockManager::const_iterator it = memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. @@ -2042,7 +2165,7 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA // Advance our iterator. it++; - ASSERT(it != block_manager->cend()); + ASSERT(it != memory_block_manager.cend()); info = it->GetMemoryInfo(); } @@ -2067,7 +2190,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // Get information about the first block. const VAddr last_addr = addr + size - 1; - KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); + KMemoryBlockManager::const_iterator it = memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. @@ -2075,14 +2198,14 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; // Validate all blocks in the range have correct state. - const KMemoryState first_state = info.state; - const KMemoryPermission first_perm = info.perm; - const KMemoryAttribute first_attr = info.attribute; + const KMemoryState first_state = info.m_state; + const KMemoryPermission first_perm = info.m_permission; + const KMemoryAttribute first_attr = info.m_attribute; while (true) { // Validate the current block. - R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory); - R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory); - R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr), + R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory); + R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory); + R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr), ResultInvalidCurrentMemory); // Validate against the provided masks. @@ -2095,7 +2218,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // Advance our iterator. it++; - ASSERT(it != block_manager->cend()); + ASSERT(it != memory_block_manager.cend()); info = it->GetMemoryInfo(); } @@ -2162,6 +2285,12 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); } + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Decide on new perm and attr. new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; KMemoryAttribute new_attr = static_cast(old_attr | lock_attr); @@ -2172,7 +2301,9 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr } // Apply the memory block updates. - block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + new_attr, KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } @@ -2213,13 +2344,21 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; KMemoryAttribute new_attr = static_cast(old_attr & ~lock_attr); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Update permission, if we need to. if (new_perm != old_perm) { R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); } // Apply the memory block updates. - block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + new_attr, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); return ResultSuccess; } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 25774f2321..fa11a0fe36 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -9,8 +9,10 @@ #include "common/common_types.h" #include "common/page_table.h" #include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_memory_block_manager.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/result.h" @@ -34,7 +36,12 @@ public: ~KPageTable(); Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool); + VAddr code_addr, std::size_t code_size, + KMemoryBlockSlabManager* mem_block_slab_manager, + KMemoryManager::Pool pool); + + void Finalize(); + Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, KMemoryPermission perm); Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); @@ -58,8 +65,6 @@ public: Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm); KMemoryInfo QueryInfo(VAddr addr); - Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); - Result ResetTransferMemory(VAddr addr, std::size_t size); Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); Result SetMaxHeapSize(std::size_t size); @@ -68,7 +73,6 @@ public: bool is_map_only, VAddr region_start, std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm, PAddr map_addr = 0); - Result LockForDeviceAddressSpace(VAddr addr, std::size_t size); Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size); Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg); @@ -96,17 +100,14 @@ private: ChangePermissionsAndRefresh, }; - static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = KMemoryAttribute::DontCareMask | - KMemoryAttribute::IpcLocked | - KMemoryAttribute::DeviceShared; + static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = + KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; - Result InitializeMemoryLayout(VAddr start, VAddr end); Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, bool is_pa_valid, VAddr region_start, std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm); Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); - bool IsRegionMapped(VAddr address, u64 size); bool IsRegionContiguous(VAddr addr, u64 size) const; void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list); KMemoryInfo QueryInfoImpl(VAddr addr); @@ -194,8 +195,6 @@ private: mutable KLightLock general_lock; mutable KLightLock map_physical_memory_lock; - std::unique_ptr block_manager; - public: constexpr VAddr GetAddressSpaceStart() const { return address_space_start; @@ -346,9 +345,13 @@ private: std::size_t max_physical_memory_size{}; std::size_t address_space_width{}; + KMemoryBlockManager memory_block_manager; + bool is_kernel{}; bool is_aslr_enabled{}; + KMemoryBlockSlabManager* memory_block_slab_manager{}; + u32 heap_fill_value{}; const KMemoryRegion* cached_physical_heap_region{}; From 1baedfa12cc84efd878567e91672f7e0f6de7b5a Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 16 Sep 2022 23:33:47 -0700 Subject: [PATCH 168/245] core: hle: kernel: Integration application memory block slab manager. --- src/core/hle/kernel/k_process.cpp | 6 +++--- src/core/hle/kernel/kernel.cpp | 34 +++++++++++++++++++++++++++++++ src/core/hle/kernel/kernel.h | 7 +++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 1d3157a9f4..abc2115bd9 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -356,9 +356,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: return ResultLimitReached; } // Initialize proces address space - if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, - 0x8000000, code_size, - KMemoryManager::Pool::Application)}; + if (const Result result{page_table->InitializeForProcess( + metadata.GetAddressSpaceType(), false, 0x8000000, code_size, + &kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)}; result.IsError()) { return result; } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 9251f29ad7..d572394727 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -24,6 +24,7 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" @@ -76,6 +77,14 @@ struct KernelCore::Impl { InitializePreemption(kernel); InitializePhysicalCores(); + // Initialize the Dynamic Slab Heaps. + { + const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion(); + ASSERT(pt_heap_region.GetEndAddress() != 0); + + InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize()); + } + RegisterHostThread(); } @@ -257,6 +266,18 @@ struct KernelCore::Impl { system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); } + void InitializeResourceManagers(VAddr address, size_t size) { + dynamic_page_manager = std::make_unique(); + memory_block_heap = std::make_unique(); + app_memory_block_manager = std::make_unique(); + + dynamic_page_manager->Initialize(address, size); + static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000; + memory_block_heap->Initialize(dynamic_page_manager.get(), + ApplicationMemoryBlockSlabHeapSize); + app_memory_block_manager->Initialize(nullptr, memory_block_heap.get()); + } + void InitializeShutdownThreads() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { shutdown_threads[core_id] = KThread::Create(system.Kernel()); @@ -770,6 +791,11 @@ struct KernelCore::Impl { // Kernel memory management std::unique_ptr memory_manager; + // Dynamic slab managers + std::unique_ptr dynamic_page_manager; + std::unique_ptr memory_block_heap; + std::unique_ptr app_memory_block_manager; + // Shared memory for services Kernel::KSharedMemory* hid_shared_mem{}; Kernel::KSharedMemory* font_shared_mem{}; @@ -1041,6 +1067,14 @@ const KMemoryManager& KernelCore::MemoryManager() const { return *impl->memory_manager; } +KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() { + return *impl->app_memory_block_manager; +} + +const KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() const { + return *impl->app_memory_block_manager; +} + Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { return *impl->hid_shared_mem; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 0847cbcbf7..79e66483ee 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -37,6 +37,7 @@ class KClientSession; class KEvent; class KHandleTable; class KLinkedListNode; +class KMemoryBlockSlabManager; class KMemoryLayout; class KMemoryManager; class KPageBuffer; @@ -238,6 +239,12 @@ public: /// Gets the virtual memory manager for the kernel. const KMemoryManager& MemoryManager() const; + /// Gets the application memory block manager for the kernel. + KMemoryBlockSlabManager& GetApplicationMemoryBlockManager(); + + /// Gets the application memory block manager for the kernel. + const KMemoryBlockSlabManager& GetApplicationMemoryBlockManager() const; + /// Gets the shared memory object for HID services. Kernel::KSharedMemory& GetHidSharedMem(); From d00245d4440d30a2217c025572cf8a47f4ea2573 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 10 Sep 2022 23:59:34 -0700 Subject: [PATCH 169/245] video_core: renderer_vulkan: vk_query_cache: Avoid shutdown crash in QueryPool::Reserve. --- src/video_core/renderer_vulkan/vk_query_cache.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 7cb02631c4..4b15c0f85b 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -59,10 +59,11 @@ void QueryPool::Reserve(std::pair query) { std::find_if(pools.begin(), pools.end(), [query_pool = query.first](vk::QueryPool& pool) { return query_pool == *pool; }); - ASSERT(it != std::end(pools)); - const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it); - usage[pool_index * GROW_STEP + static_cast(query.second)] = false; + if (it != std::end(pools)) { + const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it); + usage[pool_index * GROW_STEP + static_cast(query.second)] = false; + } } QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, From ff26190d422599ded0166cba686e7456c59163a5 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 1 Oct 2022 14:08:47 -0700 Subject: [PATCH 170/245] core: hle: kernel: k_page_table: Impl. LockForUn/MapDeviceAddressSpace, cleanup. --- src/core/hle/kernel/k_page_table.cpp | 881 ++++++++++--------- src/core/hle/kernel/k_page_table.h | 269 +++--- src/core/hle/service/nvdrv/devices/nvmap.cpp | 3 +- 3 files changed, 616 insertions(+), 537 deletions(-) diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 2cf46af0a4..fcffc0b88d 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -25,7 +25,7 @@ namespace { using namespace Common::Literals; -constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { +constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { switch (as_type) { case FileSys::ProgramAddressSpaceType::Is32Bit: case FileSys::ProgramAddressSpaceType::Is32BitNoMap: @@ -43,28 +43,29 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT } // namespace KPageTable::KPageTable(Core::System& system_) - : general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {} + : m_general_lock{system_.Kernel()}, + m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {} KPageTable::~KPageTable() = default; Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, + VAddr code_addr, size_t code_size, KMemoryBlockSlabManager* mem_block_slab_manager, KMemoryManager::Pool pool) { const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { - return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type); + return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); }; const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) { - return KAddressSpaceInfo::GetAddressSpaceSize(address_space_width, type); + return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type); }; // Set our width and heap/alias sizes - address_space_width = GetAddressSpaceWidthFromType(as_type); + m_address_space_width = GetAddressSpaceWidthFromType(as_type); const VAddr start = 0; - const VAddr end{1ULL << address_space_width}; - std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; - std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; + const VAddr end{1ULL << m_address_space_width}; + size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; + size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; ASSERT(code_addr < code_addr + code_size); ASSERT(code_addr + code_size - 1 <= end - 1); @@ -76,67 +77,68 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type } // Set code regions and determine remaining - constexpr std::size_t RegionAlignment{2_MiB}; + constexpr size_t RegionAlignment{2_MiB}; VAddr process_code_start{}; VAddr process_code_end{}; - std::size_t stack_region_size{}; - std::size_t kernel_map_region_size{}; + size_t stack_region_size{}; + size_t kernel_map_region_size{}; - if (address_space_width == 39) { + if (m_address_space_width == 39) { alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias); heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); - code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); - code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); - alias_code_region_start = code_region_start; - alias_code_region_end = code_region_end; + m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); + m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); + m_alias_code_region_start = m_code_region_start; + m_alias_code_region_end = m_code_region_end; process_code_start = Common::AlignDown(code_addr, RegionAlignment); process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment); } else { stack_region_size = 0; kernel_map_region_size = 0; - code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); - code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); - stack_region_start = code_region_start; - alias_code_region_start = code_region_start; - alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + - GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); - stack_region_end = code_region_end; - kernel_map_region_start = code_region_start; - kernel_map_region_end = code_region_end; - process_code_start = code_region_start; - process_code_end = code_region_end; + m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); + m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); + m_stack_region_start = m_code_region_start; + m_alias_code_region_start = m_code_region_start; + m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + + GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); + m_stack_region_end = m_code_region_end; + m_kernel_map_region_start = m_code_region_start; + m_kernel_map_region_end = m_code_region_end; + process_code_start = m_code_region_start; + process_code_end = m_code_region_end; } // Set other basic fields - is_aslr_enabled = enable_aslr; - address_space_start = start; - address_space_end = end; - is_kernel = false; - memory_block_slab_manager = mem_block_slab_manager; + m_enable_aslr = enable_aslr; + m_enable_device_address_space_merge = false; + m_address_space_start = start; + m_address_space_end = end; + m_is_kernel = false; + m_memory_block_slab_manager = mem_block_slab_manager; // Determine the region we can place our undetermineds in VAddr alloc_start{}; - std::size_t alloc_size{}; - if ((process_code_start - code_region_start) >= (end - process_code_end)) { - alloc_start = code_region_start; - alloc_size = process_code_start - code_region_start; + size_t alloc_size{}; + if ((process_code_start - m_code_region_start) >= (end - process_code_end)) { + alloc_start = m_code_region_start; + alloc_size = process_code_start - m_code_region_start; } else { alloc_start = process_code_end; alloc_size = end - process_code_end; } - const std::size_t needed_size{ + const size_t needed_size{ (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; if (alloc_size < needed_size) { ASSERT(false); return ResultOutOfMemory; } - const std::size_t remaining_size{alloc_size - needed_size}; + const size_t remaining_size{alloc_size - needed_size}; // Determine random placements for each region - std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; + size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; if (enable_aslr) { alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; @@ -149,124 +151,124 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type } // Setup heap and alias regions - alias_region_start = alloc_start + alias_rnd; - alias_region_end = alias_region_start + alias_region_size; - heap_region_start = alloc_start + heap_rnd; - heap_region_end = heap_region_start + heap_region_size; + m_alias_region_start = alloc_start + alias_rnd; + m_alias_region_end = m_alias_region_start + alias_region_size; + m_heap_region_start = alloc_start + heap_rnd; + m_heap_region_end = m_heap_region_start + heap_region_size; if (alias_rnd <= heap_rnd) { - heap_region_start += alias_region_size; - heap_region_end += alias_region_size; + m_heap_region_start += alias_region_size; + m_heap_region_end += alias_region_size; } else { - alias_region_start += heap_region_size; - alias_region_end += heap_region_size; + m_alias_region_start += heap_region_size; + m_alias_region_end += heap_region_size; } // Setup stack region if (stack_region_size) { - stack_region_start = alloc_start + stack_rnd; - stack_region_end = stack_region_start + stack_region_size; + m_stack_region_start = alloc_start + stack_rnd; + m_stack_region_end = m_stack_region_start + stack_region_size; if (alias_rnd < stack_rnd) { - stack_region_start += alias_region_size; - stack_region_end += alias_region_size; + m_stack_region_start += alias_region_size; + m_stack_region_end += alias_region_size; } else { - alias_region_start += stack_region_size; - alias_region_end += stack_region_size; + m_alias_region_start += stack_region_size; + m_alias_region_end += stack_region_size; } if (heap_rnd < stack_rnd) { - stack_region_start += heap_region_size; - stack_region_end += heap_region_size; + m_stack_region_start += heap_region_size; + m_stack_region_end += heap_region_size; } else { - heap_region_start += stack_region_size; - heap_region_end += stack_region_size; + m_heap_region_start += stack_region_size; + m_heap_region_end += stack_region_size; } } // Setup kernel map region if (kernel_map_region_size) { - kernel_map_region_start = alloc_start + kmap_rnd; - kernel_map_region_end = kernel_map_region_start + kernel_map_region_size; + m_kernel_map_region_start = alloc_start + kmap_rnd; + m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size; if (alias_rnd < kmap_rnd) { - kernel_map_region_start += alias_region_size; - kernel_map_region_end += alias_region_size; + m_kernel_map_region_start += alias_region_size; + m_kernel_map_region_end += alias_region_size; } else { - alias_region_start += kernel_map_region_size; - alias_region_end += kernel_map_region_size; + m_alias_region_start += kernel_map_region_size; + m_alias_region_end += kernel_map_region_size; } if (heap_rnd < kmap_rnd) { - kernel_map_region_start += heap_region_size; - kernel_map_region_end += heap_region_size; + m_kernel_map_region_start += heap_region_size; + m_kernel_map_region_end += heap_region_size; } else { - heap_region_start += kernel_map_region_size; - heap_region_end += kernel_map_region_size; + m_heap_region_start += kernel_map_region_size; + m_heap_region_end += kernel_map_region_size; } if (stack_region_size) { if (stack_rnd < kmap_rnd) { - kernel_map_region_start += stack_region_size; - kernel_map_region_end += stack_region_size; + m_kernel_map_region_start += stack_region_size; + m_kernel_map_region_end += stack_region_size; } else { - stack_region_start += kernel_map_region_size; - stack_region_end += kernel_map_region_size; + m_stack_region_start += kernel_map_region_size; + m_stack_region_end += kernel_map_region_size; } } } // Set heap members - current_heap_end = heap_region_start; - max_heap_size = 0; - max_physical_memory_size = 0; + m_current_heap_end = m_heap_region_start; + m_max_heap_size = 0; + m_max_physical_memory_size = 0; // Ensure that we regions inside our address space auto IsInAddressSpace = [&](VAddr addr) { - return address_space_start <= addr && addr <= address_space_end; + return m_address_space_start <= addr && addr <= m_address_space_end; }; - ASSERT(IsInAddressSpace(alias_region_start)); - ASSERT(IsInAddressSpace(alias_region_end)); - ASSERT(IsInAddressSpace(heap_region_start)); - ASSERT(IsInAddressSpace(heap_region_end)); - ASSERT(IsInAddressSpace(stack_region_start)); - ASSERT(IsInAddressSpace(stack_region_end)); - ASSERT(IsInAddressSpace(kernel_map_region_start)); - ASSERT(IsInAddressSpace(kernel_map_region_end)); + ASSERT(IsInAddressSpace(m_alias_region_start)); + ASSERT(IsInAddressSpace(m_alias_region_end)); + ASSERT(IsInAddressSpace(m_heap_region_start)); + ASSERT(IsInAddressSpace(m_heap_region_end)); + ASSERT(IsInAddressSpace(m_stack_region_start)); + ASSERT(IsInAddressSpace(m_stack_region_end)); + ASSERT(IsInAddressSpace(m_kernel_map_region_start)); + ASSERT(IsInAddressSpace(m_kernel_map_region_end)); // Ensure that we selected regions that don't overlap - const VAddr alias_start{alias_region_start}; - const VAddr alias_last{alias_region_end - 1}; - const VAddr heap_start{heap_region_start}; - const VAddr heap_last{heap_region_end - 1}; - const VAddr stack_start{stack_region_start}; - const VAddr stack_last{stack_region_end - 1}; - const VAddr kmap_start{kernel_map_region_start}; - const VAddr kmap_last{kernel_map_region_end - 1}; + const VAddr alias_start{m_alias_region_start}; + const VAddr alias_last{m_alias_region_end - 1}; + const VAddr heap_start{m_heap_region_start}; + const VAddr heap_last{m_heap_region_end - 1}; + const VAddr stack_start{m_stack_region_start}; + const VAddr stack_last{m_stack_region_end - 1}; + const VAddr kmap_start{m_kernel_map_region_start}; + const VAddr kmap_last{m_kernel_map_region_end - 1}; ASSERT(alias_last < heap_start || heap_last < alias_start); ASSERT(alias_last < stack_start || stack_last < alias_start); ASSERT(alias_last < kmap_start || kmap_last < alias_start); ASSERT(heap_last < stack_start || stack_last < heap_start); ASSERT(heap_last < kmap_start || kmap_last < heap_start); - current_heap_end = heap_region_start; - max_heap_size = 0; - mapped_physical_memory_size = 0; - memory_pool = pool; + m_current_heap_end = m_heap_region_start; + m_max_heap_size = 0; + m_mapped_physical_memory_size = 0; + m_memory_pool = pool; - page_table_impl.Resize(address_space_width, PageBits); + m_page_table_impl.Resize(m_address_space_width, PageBits); - return memory_block_manager.Initialize(address_space_start, address_space_end, - memory_block_slab_manager); + return m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, + m_memory_block_slab_manager); } void KPageTable::Finalize() { - memory_block_manager.Finalize(memory_block_slab_manager, [&](VAddr addr, u64 size) { - system.Memory().UnmapRegion(page_table_impl, addr, size); + m_memory_block_manager.Finalize(m_memory_block_slab_manager, [&](VAddr addr, u64 size) { + m_system.Memory().UnmapRegion(m_page_table_impl, addr, size); }); } -Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, +Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState state, KMemoryPermission perm) { const u64 size{num_pages * PageSize}; @@ -274,7 +276,7 @@ Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryStat R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify that the destination memory is unmapped. R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, @@ -284,43 +286,43 @@ Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryStat // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager); + m_memory_block_slab_manager); // Allocate and open. KPageGroup pg; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( &pg, num_pages, - KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option))); + KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } -Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { +Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidMemoryRegion); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify that the source memory is normal heap. KMemoryState src_state{}; KMemoryPermission src_perm{}; - std::size_t num_src_allocator_blocks{}; + size_t num_src_allocator_blocks{}; R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, src_address, size, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)); // Verify that the destination memory is unmapped. - std::size_t num_dst_allocator_blocks{}; + size_t num_dst_allocator_blocks{}; R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, @@ -328,20 +330,22 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size // Create an update allocator for the source. Result src_allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator src_allocator( - std::addressof(src_allocator_result), memory_block_slab_manager, num_src_allocator_blocks); + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); R_TRY(src_allocator_result); // Create an update allocator for the destination. Result dst_allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator dst_allocator( - std::addressof(dst_allocator_result), memory_block_slab_manager, num_dst_allocator_blocks); + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); R_TRY(dst_allocator_result); // Map the code memory. { // Determine the number of pages being operated on. - const std::size_t num_pages = size / PageSize; + const size_t num_pages = size / PageSize; // Create page groups for the memory being mapped. KPageGroup pg; @@ -366,37 +370,37 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size unprot_guard.Cancel(); // Apply the memory block updates. - memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, - src_state, new_perm, KMemoryAttribute::Locked, - KMemoryBlockDisableMergeAttribute::Locked, - KMemoryBlockDisableMergeAttribute::None); - memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, - KMemoryState::AliasCode, new_perm, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, + src_state, new_perm, KMemoryAttribute::Locked, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::AliasCode, new_perm, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); } return ResultSuccess; } -Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, +Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size, ICacheInvalidationStrategy icache_invalidation_strategy) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidMemoryRegion); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify that the source memory is locked normal heap. - std::size_t num_src_allocator_blocks{}; + size_t num_src_allocator_blocks{}; R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::Locked)); // Verify that the destination memory is aliasable code. - std::size_t num_dst_allocator_blocks{}; + size_t num_dst_allocator_blocks{}; R_TRY(this->CheckMemoryStateContiguous( std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, @@ -405,7 +409,7 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si // Determine whether any pages being unmapped are code. bool any_code_pages = false; { - KMemoryBlockManager::const_iterator it = memory_block_manager.FindIterator(dst_address); + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address); while (true) { // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -431,9 +435,9 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si SCOPE_EXIT({ if (reprotected_pages && any_code_pages) { if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { - system.InvalidateCpuInstructionCacheRange(dst_address, size); + m_system.InvalidateCpuInstructionCacheRange(dst_address, size); } else { - system.InvalidateCpuInstructionCaches(); + m_system.InvalidateCpuInstructionCaches(); } } }); @@ -441,19 +445,19 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si // Unmap. { // Determine the number of pages being operated on. - const std::size_t num_pages = size / PageSize; + const size_t num_pages = size / PageSize; // Create an update allocator for the source. Result src_allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), - memory_block_slab_manager, + m_memory_block_slab_manager, num_src_allocator_blocks); R_TRY(src_allocator_result); // Create an update allocator for the destination. Result dst_allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), - memory_block_slab_manager, + m_memory_block_slab_manager, num_dst_allocator_blocks); R_TRY(dst_allocator_result); @@ -465,14 +469,14 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si OperationType::ChangePermissions)); // Apply the memory block updates. - memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, - KMemoryState::None, KMemoryPermission::None, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal); - memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, - KMemoryState::Normal, KMemoryPermission::UserReadWrite, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Locked); + m_memory_block_manager.Update( + std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); + m_memory_block_manager.Update( + std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); // Note that we reprotected pages. reprotected_pages = true; @@ -481,9 +485,8 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si return ResultSuccess; } -VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, - std::size_t num_pages, std::size_t alignment, std::size_t offset, - std::size_t guard_pages) { +VAddr KPageTable::FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, + size_t alignment, size_t offset, size_t guard_pages) { VAddr address = 0; if (num_pages <= region_num_pages) { @@ -492,8 +495,8 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, } // Find the first free area. if (address == 0) { - address = memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages, - alignment, offset, guard_pages); + address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages, + alignment, offset, guard_pages); } } @@ -511,7 +514,8 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); + R_UNLESS(m_page_table_impl.BeginTraversal(next_entry, context, addr), + ResultInvalidCurrentMemory); // Prepare tracking variables. PAddr cur_addr = next_entry.phys_addr; @@ -519,9 +523,9 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { size_t tot_size = cur_size; // Iterate, adding to group as we go. - const auto& memory_layout = system.Kernel().MemoryLayout(); + const auto& memory_layout = m_system.Kernel().MemoryLayout(); while (tot_size < size) { - R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context), + R_UNLESS(m_page_table_impl.ContinueTraversal(next_entry, context), ResultInvalidCurrentMemory); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -557,7 +561,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu const size_t size = num_pages * PageSize; const auto& pg = pg_ll.Nodes(); - const auto& memory_layout = system.Kernel().MemoryLayout(); + const auto& memory_layout = m_system.Kernel().MemoryLayout(); // Empty groups are necessarily invalid. if (pg.empty()) { @@ -584,7 +588,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - if (!page_table_impl.BeginTraversal(next_entry, context, addr)) { + if (!m_page_table_impl.BeginTraversal(next_entry, context, addr)) { return false; } @@ -595,7 +599,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu // Iterate, comparing expected to actual. while (tot_size < size) { - if (!page_table_impl.ContinueTraversal(next_entry, context)) { + if (!m_page_table_impl.ContinueTraversal(next_entry, context)) { return false; } @@ -641,11 +645,11 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); } -Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, +Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table, VAddr src_addr) { - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); - const std::size_t num_pages{size / PageSize}; + const size_t num_pages{size / PageSize}; // Check that the memory is mapped in the destination process. size_t num_allocator_blocks; @@ -663,48 +667,48 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTab // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Apply the memory block update. - memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal); + m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages, + KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); - system.InvalidateCpuInstructionCaches(); + m_system.InvalidateCpuInstructionCaches(); return ResultSuccess; } -Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { +Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // Lock the physical memory lock. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); // Calculate the last address for convenience. const VAddr last_address = address + size - 1; // Define iteration variables. VAddr cur_address; - std::size_t mapped_size; + size_t mapped_size; // The entire mapping process can be retried. while (true) { // Check if the memory is already mapped. { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Iterate over the memory. cur_address = address; mapped_size = 0; - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != memory_block_manager.end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -735,20 +739,20 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { { // Reserve the memory from the process resource limit. KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), + m_system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the new memory. KPageGroup pg; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpenForProcess( &pg, (size - mapped_size) / PageSize, - KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); + KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); // Map the memory. { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); size_t num_allocator_blocks = 0; @@ -758,10 +762,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { size_t checked_mapped_size = 0; cur_address = address; - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != memory_block_manager.end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -805,7 +809,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); @@ -818,10 +822,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { // Iterate, unmapping the pages. cur_address = address; - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != memory_block_manager.end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -857,10 +861,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { PAddr pg_phys_addr = pg_it->GetAddress(); size_t pg_pages = pg_it->GetNumPages(); - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != memory_block_manager.end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -913,10 +917,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { memory_reservation.Commit(); // Increase our tracked mapped size. - mapped_physical_memory_size += (size - mapped_size); + m_mapped_physical_memory_size += (size - mapped_size); // Update the relevant memory blocks. - memory_block_manager.UpdateIfMatch( + m_memory_block_manager.UpdateIfMatch( std::addressof(allocator), address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, KMemoryPermission::UserReadWrite, KMemoryAttribute::None); @@ -930,20 +934,20 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { } } -Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { +Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { // Lock the physical memory lock. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Calculate the last address for convenience. const VAddr last_address = address + size - 1; // Define iteration variables. VAddr cur_address = 0; - std::size_t mapped_size = 0; - std::size_t num_allocator_blocks = 0; + size_t mapped_size = 0; + size_t num_allocator_blocks = 0; // Check if the memory is mapped. { @@ -951,10 +955,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { cur_address = address; mapped_size = 0; - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != memory_block_manager.end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -1053,7 +1057,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Reset the current tracking address, and make sure we clean up on failure. @@ -1064,7 +1068,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { cur_address = address; // Iterate over the memory we unmapped. - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); auto pg_it = pg.Nodes().begin(); PAddr pg_phys_addr = pg_it->GetAddress(); size_t pg_pages = pg_it->GetNumPages(); @@ -1119,10 +1123,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { }); // Iterate over the memory, unmapping as we go. - auto it = memory_block_manager.FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != memory_block_manager.end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -1149,20 +1153,20 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { } // Release the memory resource. - mapped_physical_memory_size -= mapped_size; - auto process{system.Kernel().CurrentProcess()}; + m_mapped_physical_memory_size -= mapped_size; + auto process{m_system.Kernel().CurrentProcess()}; process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); // Update memory blocks. - memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, - KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, + KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // TODO(bunnei): This is a workaround until the next set of changes, where we add reference // counting for mapped pages. Until then, we must manually close the reference to the page // group. - system.Kernel().MemoryManager().Close(pg); + m_system.Kernel().MemoryManager().Close(pg); // We succeeded. remap_guard.Cancel(); @@ -1170,9 +1174,9 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { return ResultSuccess; } -Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, std::size_t size) { +Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Validate that the source address's state is valid. KMemoryState src_state; @@ -1192,19 +1196,21 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, std::size_t s // Create an update allocator for the source. Result src_allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator src_allocator( - std::addressof(src_allocator_result), memory_block_slab_manager, num_src_allocator_blocks); + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); R_TRY(src_allocator_result); // Create an update allocator for the destination. Result dst_allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator dst_allocator( - std::addressof(dst_allocator_result), memory_block_slab_manager, num_dst_allocator_blocks); + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); R_TRY(dst_allocator_result); // Map the memory. KPageGroup page_linked_list; - const std::size_t num_pages{size / PageSize}; + const size_t num_pages{size / PageSize}; const KMemoryPermission new_src_perm = static_cast( KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; @@ -1223,21 +1229,21 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, std::size_t s } // Apply the memory block updates. - memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, - new_src_perm, new_src_attr, - KMemoryBlockDisableMergeAttribute::Locked, - KMemoryBlockDisableMergeAttribute::None); - memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, - KMemoryState::Stack, KMemoryPermission::UserReadWrite, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, + new_src_perm, new_src_attr, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::Stack, KMemoryPermission::UserReadWrite, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } -Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, std::size_t size) { +Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Validate that the source address's state is valid. KMemoryState src_state; @@ -1258,19 +1264,21 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, std::size_t // Create an update allocator for the source. Result src_allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator src_allocator( - std::addressof(src_allocator_result), memory_block_slab_manager, num_src_allocator_blocks); + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); R_TRY(src_allocator_result); // Create an update allocator for the destination. Result dst_allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator dst_allocator( - std::addressof(dst_allocator_result), memory_block_slab_manager, num_dst_allocator_blocks); + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); R_TRY(dst_allocator_result); KPageGroup src_pages; KPageGroup dst_pages; - const std::size_t num_pages{size / PageSize}; + const size_t num_pages{size / PageSize}; AddRegionToPages(src_address, num_pages, src_pages); AddRegionToPages(dst_address, num_pages, dst_pages); @@ -1290,14 +1298,14 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, std::size_t } // Apply the memory block updates. - memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, - KMemoryPermission::UserReadWrite, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Locked); - memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, - KMemoryState::None, KMemoryPermission::None, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal); + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); + m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); return ResultSuccess; } @@ -1312,7 +1320,7 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, if (const auto result{ Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; result.IsError()) { - const std::size_t num_pages{(addr - cur_addr) / PageSize}; + const size_t num_pages{(addr - cur_addr) / PageSize}; ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) .IsSuccess()); @@ -1329,12 +1337,12 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, KMemoryPermission perm) { // Check that the map is in range. - const std::size_t num_pages{page_linked_list.GetNumPages()}; - const std::size_t size{num_pages * PageSize}; + const size_t num_pages{page_linked_list.GetNumPages()}; + const size_t size{num_pages * PageSize}; R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the memory state. R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, @@ -1344,23 +1352,22 @@ Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemory // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager); + m_memory_block_slab_manager); // Map the pages. R_TRY(MapPages(address, page_linked_list, perm)); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } -Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, - PAddr phys_addr, bool is_pa_valid, VAddr region_start, - std::size_t region_num_pages, KMemoryState state, - KMemoryPermission perm) { +Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, + bool is_pa_valid, VAddr region_start, size_t region_num_pages, + KMemoryState state, KMemoryPermission perm) { ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); // Ensure this is a valid map request. @@ -1369,7 +1376,7 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Find a random address to map at. VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, @@ -1385,7 +1392,7 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager); + m_memory_block_slab_manager); // Perform mapping operation. if (is_pa_valid) { @@ -1395,9 +1402,9 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t } // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); // We successfully mapped the pages. *out_addr = addr; @@ -1424,12 +1431,12 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { // Check that the unmap is in range. - const std::size_t num_pages{page_linked_list.GetNumPages()}; - const std::size_t size{num_pages * PageSize}; + const size_t num_pages{page_linked_list.GetNumPages()}; + const size_t size{num_pages * PageSize}; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the memory state. size_t num_allocator_blocks; @@ -1441,31 +1448,31 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Perform the unmap. R_TRY(UnmapPages(address, page_linked_list)); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); return ResultSuccess; } -Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { +Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { // Check that the unmap is in range. - const std::size_t size = num_pages * PageSize; + const size_t size = num_pages * PageSize; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the memory state. - std::size_t num_allocator_blocks{}; + size_t num_allocator_blocks{}; R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState::All, state, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, @@ -1474,17 +1481,17 @@ Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Perform the unmap. R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); return ResultSuccess; } @@ -1501,7 +1508,7 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check if state allows us to create the group. R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, @@ -1514,12 +1521,12 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n return ResultSuccess; } -Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, +Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify we can change the memory permission. KMemoryState old_state; @@ -1559,7 +1566,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Perform mapping operation. @@ -1568,29 +1575,29 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, R_TRY(Operate(addr, num_pages, new_perm, operation)); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // Ensure cache coherency, if we're setting pages as executable. if (is_x) { - system.InvalidateCpuInstructionCacheRange(addr, size); + m_system.InvalidateCpuInstructionCacheRange(addr, size); } return ResultSuccess; } KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); - return memory_block_manager.FindBlock(addr)->GetMemoryInfo(); + return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo(); } KMemoryInfo KPageTable::QueryInfo(VAddr addr) { if (!Contains(addr, 1)) { return { - .m_address = address_space_end, - .m_size = 0 - address_space_end, + .m_address = m_address_space_end, + .m_size = 0 - m_address_space_end, .m_state = static_cast(Svc::MemoryState::Inaccessible), .m_device_disable_merge_left_count = 0, .m_device_disable_merge_right_count = 0, @@ -1607,12 +1614,11 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) { return QueryInfoImpl(addr); } -Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm) { +Result KPageTable::SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify we can change the memory permission. KMemoryState old_state; @@ -1631,27 +1637,27 @@ Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Perform mapping operation. R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } -Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { +Result KPageTable::SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr) { const size_t num_pages = size / PageSize; ASSERT((static_cast(mask) | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify we can change the memory attribute. KMemoryState old_state; @@ -1669,7 +1675,7 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3 // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Determine the new attribute. @@ -1681,124 +1687,125 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3 this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh); // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, - new_attr, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, + new_attr, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } -Result KPageTable::SetMaxHeapSize(std::size_t size) { +Result KPageTable::SetMaxHeapSize(size_t size) { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Only process page tables are allowed to set heap size. ASSERT(!this->IsKernel()); - max_heap_size = size; + m_max_heap_size = size; return ResultSuccess; } -Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { +Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Lock the physical memory mutex. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); // Try to perform a reduction in heap, instead of an extension. VAddr cur_address{}; - std::size_t allocation_size{}; + size_t allocation_size{}; { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Validate that setting heap size is possible at all. - R_UNLESS(!is_kernel, ResultOutOfMemory); - R_UNLESS(size <= static_cast(heap_region_end - heap_region_start), + R_UNLESS(!m_is_kernel, ResultOutOfMemory); + R_UNLESS(size <= static_cast(m_heap_region_end - m_heap_region_start), ResultOutOfMemory); - R_UNLESS(size <= max_heap_size, ResultOutOfMemory); + R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory); if (size < GetHeapSize()) { // The size being requested is less than the current size, so we need to free the end of // the heap. // Validate memory state. - std::size_t num_allocator_blocks; + size_t num_allocator_blocks; R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), - heap_region_start + size, GetHeapSize() - size, + m_heap_region_start + size, GetHeapSize() - size, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)); // Create an update allocator. Result allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator allocator( - std::addressof(allocator_result), memory_block_slab_manager, num_allocator_blocks); + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, + num_allocator_blocks); R_TRY(allocator_result); // Unmap the end of the heap. const auto num_pages = (GetHeapSize() - size) / PageSize; - R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None, + R_TRY(Operate(m_heap_region_start + size, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Release the memory from the resource limit. - system.Kernel().CurrentProcess()->GetResourceLimit()->Release( + m_system.Kernel().CurrentProcess()->GetResourceLimit()->Release( LimitableResource::PhysicalMemory, num_pages * PageSize); // Apply the memory block update. - memory_block_manager.Update(std::addressof(allocator), heap_region_start + size, - num_pages, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - size == 0 ? KMemoryBlockDisableMergeAttribute::Normal - : KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, + num_pages, KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + size == 0 ? KMemoryBlockDisableMergeAttribute::Normal + : KMemoryBlockDisableMergeAttribute::None); // Update the current heap end. - current_heap_end = heap_region_start + size; + m_current_heap_end = m_heap_region_start + size; // Set the output. - *out = heap_region_start; + *out = m_heap_region_start; return ResultSuccess; } else if (size == GetHeapSize()) { // The size requested is exactly the current size. - *out = heap_region_start; + *out = m_heap_region_start; return ResultSuccess; } else { // We have to allocate memory. Determine how much to allocate and where while the table // is locked. - cur_address = current_heap_end; + cur_address = m_current_heap_end; allocation_size = size - GetHeapSize(); } } // Reserve memory for the heap extension. KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, + m_system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, allocation_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. KPageGroup pg; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( &pg, allocation_size / PageSize, - KMemoryManager::EncodeOption(memory_pool, allocation_option))); + KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); // Clear all the newly allocated pages. for (const auto& it : pg.Nodes()) { - std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, + std::memset(m_system.DeviceMemory().GetPointer(it.GetAddress()), m_heap_fill_value, it.GetSize()); } // Map the pages. { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Ensure that the heap hasn't changed since we began executing. - ASSERT(cur_address == current_heap_end); + ASSERT(cur_address == m_current_heap_end); // Check the memory state. - std::size_t num_allocator_blocks{}; - R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end, + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end, allocation_size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); @@ -1806,16 +1813,16 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator( - std::addressof(allocator_result), memory_block_slab_manager, num_allocator_blocks); + std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Map the pages. const auto num_pages = allocation_size / PageSize; - R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); + R_TRY(Operate(m_current_heap_end, num_pages, pg, OperationType::MapGroup)); // Clear all the newly allocated pages. - for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { - std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0, + for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) { + std::memset(m_system.Memory().GetPointer(m_current_heap_end + (cur_page * PageSize)), 0, PageSize); } @@ -1823,27 +1830,27 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { memory_reservation.Commit(); // Apply the memory block update. - memory_block_manager.Update( - std::addressof(allocator), current_heap_end, num_pages, KMemoryState::Normal, + m_memory_block_manager.Update( + std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal, KMemoryPermission::UserReadWrite, KMemoryAttribute::None, - heap_region_start == current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal - : KMemoryBlockDisableMergeAttribute::None, + m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal + : KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::None); // Update the current heap end. - current_heap_end = heap_region_start + size; + m_current_heap_end = m_heap_region_start + size; // Set the output. - *out = heap_region_start; + *out = m_heap_region_start; return ResultSuccess; } } -ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, +ResultVal KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only, VAddr region_start, - std::size_t region_num_pages, KMemoryState state, + size_t region_num_pages, KMemoryState state, KMemoryPermission perm, PAddr map_addr) { - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); if (!CanContain(region_start, region_num_pages * PageSize, state)) { return ResultInvalidCurrentMemory; @@ -1862,33 +1869,98 @@ ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager); + m_memory_block_slab_manager); if (is_map_only) { R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); } else { KPageGroup page_group; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpenForProcess( &page_group, needed_num_pages, - KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); + KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); } // Update the blocks. - memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return addr; } -Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, std::size_t size) { +Result KPageTable::LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm, + bool is_aligned) { // Lightly validate the range before doing anything else. const size_t num_pages = size / PageSize; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); + + // Check the memory state. + const auto test_state = + (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap); + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, test_state, + test_state, perm, perm, + KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, + KMemoryAttribute::None, KMemoryAttribute::DeviceShared)); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // Update the memory blocks. + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, + &KMemoryBlock::ShareToDevice, KMemoryPermission::None); + + return ResultSuccess; +} + +Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size) { + // Lightly validate the range before doing anything else. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(m_general_lock); + + // Check the memory state. + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryStateContiguous( + std::addressof(num_allocator_blocks), address, size, + KMemoryState::FlagReferenceCounted | KMemoryState::FlagCanDeviceMap, + KMemoryState::FlagReferenceCounted | KMemoryState::FlagCanDeviceMap, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // Update the memory blocks. + const KMemoryBlockManager::MemoryBlockLockFunction lock_func = + m_enable_device_address_space_merge + ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare + : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, + KMemoryPermission::None); + + return ResultSuccess; +} + +Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) { + // Lightly validate the range before doing anything else. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(m_general_lock); // Check the memory state. size_t num_allocator_blocks; @@ -1900,17 +1972,17 @@ Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, std::size_t size) // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Update the memory blocks. - memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, - &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, + &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); return ResultSuccess; } -Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) { +Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) { return this->LockMemoryAndOpen( out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, @@ -1920,7 +1992,7 @@ Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t si KMemoryAttribute::Locked); } -Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) { +Result KPageTable::UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg) { return this->UnlockMemory( addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, @@ -1928,9 +2000,9 @@ Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPage } bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { - auto start_ptr = system.DeviceMemory().GetPointer(addr); + auto start_ptr = m_system.DeviceMemory().GetPointer(addr); for (u64 offset{}; offset < size; offset += PageSize) { - if (start_ptr != system.DeviceMemory().GetPointer(addr + offset)) { + if (start_ptr != m_system.DeviceMemory().GetPointer(addr + offset)) { return false; } start_ptr += PageSize; @@ -1938,8 +2010,7 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { return true; } -void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, - KPageGroup& page_linked_list) { +void KPageTable::AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list) { VAddr addr{start}; while (addr < start + (num_pages * PageSize)) { const PAddr paddr{GetPhysicalAddr(addr)}; @@ -1949,16 +2020,16 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, } } -VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, - u64 needed_num_pages, std::size_t align) { - if (is_aslr_enabled) { +VAddr KPageTable::AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages, + size_t align) { + if (m_enable_aslr) { UNIMPLEMENTED(); } - return memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, - IsKernel() ? 1 : 4); + return m_memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, + IsKernel() ? 1 : 4); } -Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, +Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group, OperationType operation) { ASSERT(this->IsLockedByCurrentThread()); @@ -1967,11 +2038,11 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& ASSERT(num_pages == page_group.GetNumPages()); for (const auto& node : page_group.Nodes()) { - const std::size_t size{node.GetNumPages() * PageSize}; + const size_t size{node.GetNumPages() * PageSize}; switch (operation) { case OperationType::MapGroup: - system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); + m_system.Memory().MapMemoryRegion(m_page_table_impl, addr, size, node.GetAddress()); break; default: ASSERT(false); @@ -1983,7 +2054,7 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& return ResultSuccess; } -Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, +Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation, PAddr map_addr) { ASSERT(this->IsLockedByCurrentThread()); @@ -1993,12 +2064,12 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission switch (operation) { case OperationType::Unmap: - system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize); + m_system.Memory().UnmapRegion(m_page_table_impl, addr, num_pages * PageSize); break; case OperationType::Map: { ASSERT(map_addr); ASSERT(Common::IsAligned(map_addr, PageSize)); - system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr); + m_system.Memory().MapMemoryRegion(m_page_table_impl, addr, num_pages * PageSize, map_addr); break; } case OperationType::ChangePermissions: @@ -2014,18 +2085,18 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: - return address_space_start; + return m_address_space_start; case KMemoryState::Normal: - return heap_region_start; + return m_heap_region_start; case KMemoryState::Ipc: case KMemoryState::NonSecureIpc: case KMemoryState::NonDeviceIpc: - return alias_region_start; + return m_alias_region_start; case KMemoryState::Stack: - return stack_region_start; + return m_stack_region_start; case KMemoryState::Static: case KMemoryState::ThreadLocal: - return kernel_map_region_start; + return m_kernel_map_region_start; case KMemoryState::Io: case KMemoryState::Shared: case KMemoryState::AliasCode: @@ -2036,31 +2107,31 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const { case KMemoryState::GeneratedCode: case KMemoryState::CodeOut: case KMemoryState::Coverage: - return alias_code_region_start; + return m_alias_code_region_start; case KMemoryState::Code: case KMemoryState::CodeData: - return code_region_start; + return m_code_region_start; default: UNREACHABLE(); } } -std::size_t KPageTable::GetRegionSize(KMemoryState state) const { +size_t KPageTable::GetRegionSize(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: - return address_space_end - address_space_start; + return m_address_space_end - m_address_space_start; case KMemoryState::Normal: - return heap_region_end - heap_region_start; + return m_heap_region_end - m_heap_region_start; case KMemoryState::Ipc: case KMemoryState::NonSecureIpc: case KMemoryState::NonDeviceIpc: - return alias_region_end - alias_region_start; + return m_alias_region_end - m_alias_region_start; case KMemoryState::Stack: - return stack_region_end - stack_region_start; + return m_stack_region_end - m_stack_region_start; case KMemoryState::Static: case KMemoryState::ThreadLocal: - return kernel_map_region_end - kernel_map_region_start; + return m_kernel_map_region_end - m_kernel_map_region_start; case KMemoryState::Io: case KMemoryState::Shared: case KMemoryState::AliasCode: @@ -2071,16 +2142,16 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const { case KMemoryState::GeneratedCode: case KMemoryState::CodeOut: case KMemoryState::Coverage: - return alias_code_region_end - alias_code_region_start; + return m_alias_code_region_end - m_alias_code_region_start; case KMemoryState::Code: case KMemoryState::CodeData: - return code_region_end - code_region_start; + return m_code_region_end - m_code_region_start; default: UNREACHABLE(); } } -bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const { +bool KPageTable::CanContain(VAddr addr, size_t size, KMemoryState state) const { const VAddr end = addr + size; const VAddr last = end - 1; @@ -2089,10 +2160,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co const bool is_in_region = region_start <= addr && addr < end && last <= region_start + region_size - 1; - const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr || - heap_region_start == heap_region_end); - const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr || - alias_region_start == alias_region_end); + const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr || + m_heap_region_start == m_heap_region_end); + const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || + m_alias_region_start == m_alias_region_end); switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -2138,16 +2209,16 @@ Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_ return ResultSuccess; } -Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, +Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. const VAddr last_addr = addr + size - 1; - KMemoryBlockManager::const_iterator it = memory_block_manager.FindIterator(addr); + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. @@ -2165,7 +2236,7 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA // Advance our iterator. it++; - ASSERT(it != memory_block_manager.cend()); + ASSERT(it != m_memory_block_manager.cend()); info = it->GetMemoryInfo(); } @@ -2181,8 +2252,8 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA } Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, - VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryAttribute* out_attr, size_t* out_blocks_needed, + VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { @@ -2190,7 +2261,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // Get information about the first block. const VAddr last_addr = addr + size - 1; - KMemoryBlockManager::const_iterator it = memory_block_manager.FindIterator(addr); + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. @@ -2218,7 +2289,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // Advance our iterator. it++; - ASSERT(it != memory_block_manager.cend()); + ASSERT(it != m_memory_block_manager.cend()); info = it->GetMemoryInfo(); } @@ -2257,7 +2328,7 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check that the output page group is empty, if it exists. if (out_pg) { @@ -2288,7 +2359,7 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Decide on new perm and attr. @@ -2301,9 +2372,9 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr } // Apply the memory block updates. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, - new_attr, KMemoryBlockDisableMergeAttribute::Locked, - KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + new_attr, KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); return ResultSuccess; } @@ -2322,7 +2393,7 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the state. KMemoryState old_state{}; @@ -2347,7 +2418,7 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask // Create an update allocator. Result allocator_result{ResultSuccess}; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - memory_block_slab_manager, num_allocator_blocks); + m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); // Update permission, if we need to. @@ -2356,9 +2427,9 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask } // Apply the memory block updates. - memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, - new_attr, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Locked); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + new_attr, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); return ResultSuccess; } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index fa11a0fe36..2258543197 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -36,60 +36,66 @@ public: ~KPageTable(); Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, + VAddr code_addr, size_t code_size, KMemoryBlockSlabManager* mem_block_slab_manager, KMemoryManager::Pool pool); void Finalize(); - Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, + Result MapProcessCode(VAddr addr, size_t pages_count, KMemoryState state, KMemoryPermission perm); - Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); - Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + Result MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size); + Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size, ICacheInvalidationStrategy icache_invalidation_strategy); - Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, + Result UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table, VAddr src_addr); - Result MapPhysicalMemory(VAddr addr, std::size_t size); - Result UnmapPhysicalMemory(VAddr addr, std::size_t size); - Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + Result MapPhysicalMemory(VAddr addr, size_t size); + Result UnmapPhysicalMemory(VAddr addr, size_t size); + Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); + Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, KMemoryPermission perm); - Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, + Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, KMemoryState state, KMemoryPermission perm) { return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, state, perm); } Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); - Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); - Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm); + Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state); + Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); KMemoryInfo QueryInfo(VAddr addr); - Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); - Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); - Result SetMaxHeapSize(std::size_t size); - Result SetHeapSize(VAddr* out, std::size_t size); - ResultVal AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, - bool is_map_only, VAddr region_start, - std::size_t region_num_pages, KMemoryState state, - KMemoryPermission perm, PAddr map_addr = 0); - Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); - Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size); - Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg); + Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); + Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); + Result SetMaxHeapSize(size_t size); + Result SetHeapSize(VAddr* out, size_t size); + ResultVal AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only, + VAddr region_start, size_t region_num_pages, + KMemoryState state, KMemoryPermission perm, + PAddr map_addr = 0); + + Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm, + bool is_aligned); + Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size); + + Result UnlockForDeviceAddressSpace(VAddr addr, size_t size); + + Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size); + Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg); Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr); Common::PageTable& PageTableImpl() { - return page_table_impl; + return m_page_table_impl; } const Common::PageTable& PageTableImpl() const { - return page_table_impl; + return m_page_table_impl; } - bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const; + bool CanContain(VAddr addr, size_t size, KMemoryState state) const; private: enum class OperationType : u32 { @@ -104,30 +110,30 @@ private: KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); - Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, - bool is_pa_valid, VAddr region_start, std::size_t region_num_pages, + Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, + bool is_pa_valid, VAddr region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); bool IsRegionContiguous(VAddr addr, u64 size) const; - void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list); + void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); KMemoryInfo QueryInfoImpl(VAddr addr); - VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, - std::size_t align); - Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, + VAddr AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages, + size_t align); + Result Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group, OperationType operation); - Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr = 0); + Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation, + PAddr map_addr = 0); VAddr GetRegionAddress(KMemoryState state) const; - std::size_t GetRegionSize(KMemoryState state) const; + size_t GetRegionSize(KMemoryState state) const; - VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, - std::size_t alignment, std::size_t offset, std::size_t guard_pages); + VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, + size_t alignment, size_t offset, size_t guard_pages); - Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + Result CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const; - Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, + Result CheckMemoryStateContiguous(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const { @@ -139,12 +145,12 @@ private: KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const; Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryAttribute* out_attr, size_t* out_blocks_needed, VAddr addr, + size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; - Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + Result CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, @@ -152,8 +158,8 @@ private: return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); } - Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, + Result CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, @@ -175,13 +181,13 @@ private: bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages); bool IsLockedByCurrentThread() const { - return general_lock.IsLockedByCurrentThread(); + return m_general_lock.IsLockedByCurrentThread(); } bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) { ASSERT(this->IsLockedByCurrentThread()); - return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr); + return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr); } bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const { @@ -192,93 +198,93 @@ private: return *out != 0; } - mutable KLightLock general_lock; - mutable KLightLock map_physical_memory_lock; + mutable KLightLock m_general_lock; + mutable KLightLock m_map_physical_memory_lock; public: constexpr VAddr GetAddressSpaceStart() const { - return address_space_start; + return m_address_space_start; } constexpr VAddr GetAddressSpaceEnd() const { - return address_space_end; + return m_address_space_end; } - constexpr std::size_t GetAddressSpaceSize() const { - return address_space_end - address_space_start; + constexpr size_t GetAddressSpaceSize() const { + return m_address_space_end - m_address_space_start; } constexpr VAddr GetHeapRegionStart() const { - return heap_region_start; + return m_heap_region_start; } constexpr VAddr GetHeapRegionEnd() const { - return heap_region_end; + return m_heap_region_end; } - constexpr std::size_t GetHeapRegionSize() const { - return heap_region_end - heap_region_start; + constexpr size_t GetHeapRegionSize() const { + return m_heap_region_end - m_heap_region_start; } constexpr VAddr GetAliasRegionStart() const { - return alias_region_start; + return m_alias_region_start; } constexpr VAddr GetAliasRegionEnd() const { - return alias_region_end; + return m_alias_region_end; } - constexpr std::size_t GetAliasRegionSize() const { - return alias_region_end - alias_region_start; + constexpr size_t GetAliasRegionSize() const { + return m_alias_region_end - m_alias_region_start; } constexpr VAddr GetStackRegionStart() const { - return stack_region_start; + return m_stack_region_start; } constexpr VAddr GetStackRegionEnd() const { - return stack_region_end; + return m_stack_region_end; } - constexpr std::size_t GetStackRegionSize() const { - return stack_region_end - stack_region_start; + constexpr size_t GetStackRegionSize() const { + return m_stack_region_end - m_stack_region_start; } constexpr VAddr GetKernelMapRegionStart() const { - return kernel_map_region_start; + return m_kernel_map_region_start; } constexpr VAddr GetKernelMapRegionEnd() const { - return kernel_map_region_end; + return m_kernel_map_region_end; } constexpr VAddr GetCodeRegionStart() const { - return code_region_start; + return m_code_region_start; } constexpr VAddr GetCodeRegionEnd() const { - return code_region_end; + return m_code_region_end; } constexpr VAddr GetAliasCodeRegionStart() const { - return alias_code_region_start; + return m_alias_code_region_start; } constexpr VAddr GetAliasCodeRegionSize() const { - return alias_code_region_end - alias_code_region_start; + return m_alias_code_region_end - m_alias_code_region_start; } - std::size_t GetNormalMemorySize() { - KScopedLightLock lk(general_lock); - return GetHeapSize() + mapped_physical_memory_size; + size_t GetNormalMemorySize() { + KScopedLightLock lk(m_general_lock); + return GetHeapSize() + m_mapped_physical_memory_size; } - constexpr std::size_t GetAddressSpaceWidth() const { - return address_space_width; + constexpr size_t GetAddressSpaceWidth() const { + return m_address_space_width; } - constexpr std::size_t GetHeapSize() const { - return current_heap_end - heap_region_start; + constexpr size_t GetHeapSize() const { + return m_current_heap_end - m_heap_region_start; } - constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const { - return address_space_start <= address && address + size - 1 <= address_space_end - 1; + constexpr bool IsInsideAddressSpace(VAddr address, size_t size) const { + return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1; } - constexpr bool IsOutsideAliasRegion(VAddr address, std::size_t size) const { - return alias_region_start > address || address + size - 1 > alias_region_end - 1; + constexpr bool IsOutsideAliasRegion(VAddr address, size_t size) const { + return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1; } - constexpr bool IsOutsideStackRegion(VAddr address, std::size_t size) const { - return stack_region_start > address || address + size - 1 > stack_region_end - 1; + constexpr bool IsOutsideStackRegion(VAddr address, size_t size) const { + return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1; } - constexpr bool IsInvalidRegion(VAddr address, std::size_t size) const { + constexpr bool IsInvalidRegion(VAddr address, size_t size) const { return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1; } - constexpr bool IsInsideHeapRegion(VAddr address, std::size_t size) const { - return address + size > heap_region_start && heap_region_end > address; + constexpr bool IsInsideHeapRegion(VAddr address, size_t size) const { + return address + size > m_heap_region_start && m_heap_region_end > address; } - constexpr bool IsInsideAliasRegion(VAddr address, std::size_t size) const { - return address + size > alias_region_start && alias_region_end > address; + constexpr bool IsInsideAliasRegion(VAddr address, size_t size) const { + return address + size > m_alias_region_start && m_alias_region_end > address; } - constexpr bool IsOutsideASLRRegion(VAddr address, std::size_t size) const { + constexpr bool IsOutsideASLRRegion(VAddr address, size_t size) const { if (IsInvalidRegion(address, size)) { return true; } @@ -290,77 +296,78 @@ public: } return {}; } - constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { + constexpr bool IsInsideASLRRegion(VAddr address, size_t size) const { return !IsOutsideASLRRegion(address, size); } - constexpr std::size_t GetNumGuardPages() const { + constexpr size_t GetNumGuardPages() const { return IsKernel() ? 1 : 4; } PAddr GetPhysicalAddr(VAddr addr) const { - const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; + const auto backing_addr = m_page_table_impl.backing_addr[addr >> PageBits]; ASSERT(backing_addr); return backing_addr + addr; } constexpr bool Contains(VAddr addr) const { - return address_space_start <= addr && addr <= address_space_end - 1; + return m_address_space_start <= addr && addr <= m_address_space_end - 1; } - constexpr bool Contains(VAddr addr, std::size_t size) const { - return address_space_start <= addr && addr < addr + size && - addr + size - 1 <= address_space_end - 1; + constexpr bool Contains(VAddr addr, size_t size) const { + return m_address_space_start <= addr && addr < addr + size && + addr + size - 1 <= m_address_space_end - 1; } private: constexpr bool IsKernel() const { - return is_kernel; + return m_is_kernel; } constexpr bool IsAslrEnabled() const { - return is_aslr_enabled; + return m_enable_aslr; } - constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { - return (address_space_start <= addr) && - (num_pages <= (address_space_end - address_space_start) / PageSize) && - (addr + num_pages * PageSize - 1 <= address_space_end - 1); + constexpr bool ContainsPages(VAddr addr, size_t num_pages) const { + return (m_address_space_start <= addr) && + (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) && + (addr + num_pages * PageSize - 1 <= m_address_space_end - 1); } private: - VAddr address_space_start{}; - VAddr address_space_end{}; - VAddr heap_region_start{}; - VAddr heap_region_end{}; - VAddr current_heap_end{}; - VAddr alias_region_start{}; - VAddr alias_region_end{}; - VAddr stack_region_start{}; - VAddr stack_region_end{}; - VAddr kernel_map_region_start{}; - VAddr kernel_map_region_end{}; - VAddr code_region_start{}; - VAddr code_region_end{}; - VAddr alias_code_region_start{}; - VAddr alias_code_region_end{}; + VAddr m_address_space_start{}; + VAddr m_address_space_end{}; + VAddr m_heap_region_start{}; + VAddr m_heap_region_end{}; + VAddr m_current_heap_end{}; + VAddr m_alias_region_start{}; + VAddr m_alias_region_end{}; + VAddr m_stack_region_start{}; + VAddr m_stack_region_end{}; + VAddr m_kernel_map_region_start{}; + VAddr m_kernel_map_region_end{}; + VAddr m_code_region_start{}; + VAddr m_code_region_end{}; + VAddr m_alias_code_region_start{}; + VAddr m_alias_code_region_end{}; - std::size_t mapped_physical_memory_size{}; - std::size_t max_heap_size{}; - std::size_t max_physical_memory_size{}; - std::size_t address_space_width{}; + size_t m_mapped_physical_memory_size{}; + size_t m_max_heap_size{}; + size_t m_max_physical_memory_size{}; + size_t m_address_space_width{}; - KMemoryBlockManager memory_block_manager; + KMemoryBlockManager m_memory_block_manager; - bool is_kernel{}; - bool is_aslr_enabled{}; + bool m_is_kernel{}; + bool m_enable_aslr{}; + bool m_enable_device_address_space_merge{}; - KMemoryBlockSlabManager* memory_block_slab_manager{}; + KMemoryBlockSlabManager* m_memory_block_slab_manager{}; - u32 heap_fill_value{}; - const KMemoryRegion* cached_physical_heap_region{}; + u32 m_heap_fill_value{}; + const KMemoryRegion* m_cached_physical_heap_region{}; - KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; - KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; + KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application}; + KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront}; - Common::PageTable page_table_impl; + Common::PageTable m_page_table_impl; - Core::System& system; + Core::System& m_system; }; } // namespace Kernel diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index ddf273b5ec..b606790217 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -128,7 +128,8 @@ NvResult nvmap::IocAlloc(const std::vector& input, std::vector& output) } ASSERT(system.CurrentProcess() ->PageTable() - .LockForDeviceAddressSpace(handle_description->address, handle_description->size) + .LockForMapDeviceAddressSpace(handle_description->address, handle_description->size, + Kernel::KMemoryPermission::None, true) .IsSuccess()); std::memcpy(output.data(), ¶ms, sizeof(params)); return result; From 8d4e026d0575fe705957d6f17dc90a57f1bc0bf7 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 11 Sep 2022 00:06:41 -0700 Subject: [PATCH 171/245] core: hle: kernel: Remove junk. --- src/core/hle/kernel/kernel.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d572394727..b6bbd4984b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -108,10 +108,6 @@ struct KernelCore::Impl { next_user_process_id = KProcess::ProcessIDMin; next_thread_id = 1; - for (auto& core : cores) { - core = nullptr; - } - global_handle_table->Finalize(); global_handle_table.reset(); @@ -365,11 +361,6 @@ struct KernelCore::Impl { static inline thread_local KThread* current_thread{nullptr}; KThread* GetCurrentEmuThread() { - // If we are shutting down the kernel, none of this is relevant anymore. - if (IsShuttingDown()) { - return {}; - } - const auto thread_id = GetCurrentHostThreadID(); if (thread_id >= Core::Hardware::NUM_CPU_CORES) { return GetHostDummyThread(); From 79bcb38321cbde163280b04ee5a03773d54edfd9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 2 Oct 2022 02:06:13 -0700 Subject: [PATCH 172/245] core: hle: kernel: k_interrupt_manager: HandleInterrupt should not depend on current process. --- src/core/hle/kernel/k_interrupt_manager.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index ad73f3eab5..4a6b60d268 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -11,25 +11,22 @@ namespace Kernel::KInterruptManager { void HandleInterrupt(KernelCore& kernel, s32 core_id) { - auto* process = kernel.CurrentProcess(); - if (!process) { - return; - } - // Acknowledge the interrupt. kernel.PhysicalCore(core_id).ClearInterrupt(); auto& current_thread = GetCurrentThread(kernel); - // If the user disable count is set, we may need to pin the current thread. - if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { - KScopedSchedulerLock sl{kernel}; + if (auto* process = kernel.CurrentProcess(); process) { + // If the user disable count is set, we may need to pin the current thread. + if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { + KScopedSchedulerLock sl{kernel}; - // Pin the current thread. - process->PinCurrentThread(core_id); + // Pin the current thread. + process->PinCurrentThread(core_id); - // Set the interrupt flag for the thread. - GetCurrentThread(kernel).SetInterruptFlag(); + // Set the interrupt flag for the thread. + GetCurrentThread(kernel).SetInterruptFlag(); + } } // Request interrupt scheduling. From abcc009dff5d98b5a04229f3a82baab23d568244 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 2 Oct 2022 14:26:30 -0700 Subject: [PATCH 173/245] core: hle: kernel: k_process: Improve management of page table & cleanup. --- src/core/hle/kernel/k_page_table.cpp | 23 +++++++---- src/core/hle/kernel/k_page_table.h | 8 ++-- src/core/hle/kernel/k_process.cpp | 62 +++++++++++++++++----------- src/core/hle/kernel/k_process.h | 31 ++++++++------ src/core/hle/kernel/kernel.cpp | 23 +++++++---- src/core/hle/kernel/kernel.h | 3 ++ src/core/hle/kernel/svc.cpp | 2 +- 7 files changed, 92 insertions(+), 60 deletions(-) diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index fcffc0b88d..22098c056e 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -256,16 +256,21 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type m_mapped_physical_memory_size = 0; m_memory_pool = pool; - m_page_table_impl.Resize(m_address_space_width, PageBits); + m_page_table_impl = std::make_unique(); + m_page_table_impl->Resize(m_address_space_width, PageBits); return m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, m_memory_block_slab_manager); } void KPageTable::Finalize() { + // Finalize memory blocks. m_memory_block_manager.Finalize(m_memory_block_slab_manager, [&](VAddr addr, u64 size) { - m_system.Memory().UnmapRegion(m_page_table_impl, addr, size); + m_system.Memory().UnmapRegion(*m_page_table_impl, addr, size); }); + + // Close the backing page table, as the destructor is not called for guest objects. + m_page_table_impl.reset(); } Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState state, @@ -514,7 +519,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - R_UNLESS(m_page_table_impl.BeginTraversal(next_entry, context, addr), + R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); // Prepare tracking variables. @@ -525,7 +530,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { // Iterate, adding to group as we go. const auto& memory_layout = m_system.Kernel().MemoryLayout(); while (tot_size < size) { - R_UNLESS(m_page_table_impl.ContinueTraversal(next_entry, context), + R_UNLESS(m_page_table_impl->ContinueTraversal(next_entry, context), ResultInvalidCurrentMemory); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -588,7 +593,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - if (!m_page_table_impl.BeginTraversal(next_entry, context, addr)) { + if (!m_page_table_impl->BeginTraversal(next_entry, context, addr)) { return false; } @@ -599,7 +604,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu // Iterate, comparing expected to actual. while (tot_size < size) { - if (!m_page_table_impl.ContinueTraversal(next_entry, context)) { + if (!m_page_table_impl->ContinueTraversal(next_entry, context)) { return false; } @@ -2042,7 +2047,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ switch (operation) { case OperationType::MapGroup: - m_system.Memory().MapMemoryRegion(m_page_table_impl, addr, size, node.GetAddress()); + m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); break; default: ASSERT(false); @@ -2064,12 +2069,12 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, switch (operation) { case OperationType::Unmap: - m_system.Memory().UnmapRegion(m_page_table_impl, addr, num_pages * PageSize); + m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); break; case OperationType::Map: { ASSERT(map_addr); ASSERT(Common::IsAligned(map_addr, PageSize)); - m_system.Memory().MapMemoryRegion(m_page_table_impl, addr, num_pages * PageSize, map_addr); + m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); break; } case OperationType::ChangePermissions: diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 2258543197..1811d3e2d2 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -88,11 +88,11 @@ public: KMemoryAttribute attr_mask, KMemoryAttribute attr); Common::PageTable& PageTableImpl() { - return m_page_table_impl; + return *m_page_table_impl; } const Common::PageTable& PageTableImpl() const { - return m_page_table_impl; + return *m_page_table_impl; } bool CanContain(VAddr addr, size_t size, KMemoryState state) const; @@ -303,7 +303,7 @@ public: return IsKernel() ? 1 : 4; } PAddr GetPhysicalAddr(VAddr addr) const { - const auto backing_addr = m_page_table_impl.backing_addr[addr >> PageBits]; + const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits]; ASSERT(backing_addr); return backing_addr + addr; } @@ -365,7 +365,7 @@ private: KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application}; KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront}; - Common::PageTable m_page_table_impl; + std::unique_ptr m_page_table_impl; Core::System& m_system; }; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index abc2115bd9..1a0aec56a7 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -72,6 +72,7 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process->name = std::move(process_name); process->resource_limit = res_limit; + process->system_resource_address = 0; process->state = State::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() @@ -92,6 +93,7 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process->exception_thread = nullptr; process->is_suspended = false; process->schedule_count = 0; + process->is_handle_table_initialized = false; // Open a reference to the resource limit. process->resource_limit->Open(); @@ -121,9 +123,9 @@ void KProcess::DecrementRunningThreadCount() { } } -u64 KProcess::GetTotalPhysicalMemoryAvailable() const { +u64 KProcess::GetTotalPhysicalMemoryAvailable() { const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + - page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size + + page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); capacity != pool_size) { @@ -135,16 +137,16 @@ u64 KProcess::GetTotalPhysicalMemoryAvailable() const { return memory_usage_capacity; } -u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { +u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() { return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize(); } -u64 KProcess::GetTotalPhysicalMemoryUsed() const { - return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() + +u64 KProcess::GetTotalPhysicalMemoryUsed() { + return image_size + main_thread_stack_size + page_table.GetNormalMemorySize() + GetSystemResourceSize(); } -u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { +u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() { return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } @@ -348,6 +350,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: system_resource_size = metadata.GetSystemResourceSize(); image_size = code_size; + // We currently do not support process-specific system resource + UNIMPLEMENTED_IF(system_resource_size != 0); + KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, code_size + system_resource_size); if (!memory_reservation.Succeeded()) { @@ -356,7 +361,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: return ResultLimitReached; } // Initialize proces address space - if (const Result result{page_table->InitializeForProcess( + if (const Result result{page_table.InitializeForProcess( metadata.GetAddressSpaceType(), false, 0x8000000, code_size, &kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)}; result.IsError()) { @@ -364,9 +369,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: } // Map process code region - if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), - code_size / PageSize, KMemoryState::Code, - KMemoryPermission::None)}; + if (const Result result{page_table.MapProcessCode(page_table.GetCodeRegionStart(), + code_size / PageSize, KMemoryState::Code, + KMemoryPermission::None)}; result.IsError()) { return result; } @@ -374,7 +379,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: // Initialize process capabilities const auto& caps{metadata.GetKernelCapabilities()}; if (const Result result{ - capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; + capabilities.InitializeForUserProcess(caps.data(), caps.size(), page_table)}; result.IsError()) { return result; } @@ -384,12 +389,12 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: case FileSys::ProgramAddressSpaceType::Is32Bit: case FileSys::ProgramAddressSpaceType::Is36Bit: case FileSys::ProgramAddressSpaceType::Is39Bit: - memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart(); + memory_usage_capacity = page_table.GetHeapRegionEnd() - page_table.GetHeapRegionStart(); break; case FileSys::ProgramAddressSpaceType::Is32BitNoMap: - memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart() + - page_table->GetAliasRegionEnd() - page_table->GetAliasRegionStart(); + memory_usage_capacity = page_table.GetHeapRegionEnd() - page_table.GetHeapRegionStart() + + page_table.GetAliasRegionEnd() - page_table.GetAliasRegionStart(); break; default: @@ -397,7 +402,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: } // Create TLS region - R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); + R_TRY(this->CreateThreadLocalRegion(std::addressof(plr_address))); memory_reservation.Commit(); return handle_table.Initialize(capabilities.GetHandleTableSize()); @@ -409,7 +414,7 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) { resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; - ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError()); + ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); ChangeState(State::Running); @@ -437,8 +442,8 @@ void KProcess::PrepareForTermination() { stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); - this->DeleteThreadLocalRegion(tls_region_address); - tls_region_address = 0; + this->DeleteThreadLocalRegion(plr_address); + plr_address = 0; if (resource_limit) { resource_limit->Release(LimitableResource::PhysicalMemory, @@ -474,7 +479,7 @@ void KProcess::Finalize() { } // Finalize the page table. - page_table.reset(); + page_table.Finalize(); // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer::Finalize(); @@ -628,7 +633,7 @@ bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { const auto ReprotectSegment = [&](const CodeSet::Segment& segment, Svc::MemoryPermission permission) { - page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); + page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); }; kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), @@ -645,8 +650,7 @@ bool KProcess::IsSignaled() const { } KProcess::KProcess(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique( - kernel_.System())}, + : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{kernel_.System()}, handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, state_lock{kernel_}, list_lock{kernel_} {} @@ -668,11 +672,11 @@ Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { // The kernel always ensures that the given stack size is page aligned. main_thread_stack_size = Common::AlignUp(stack_size, PageSize); - const VAddr start{page_table->GetStackRegionStart()}; - const std::size_t size{page_table->GetStackRegionEnd() - start}; + const VAddr start{page_table.GetStackRegionStart()}; + const std::size_t size{page_table.GetStackRegionEnd() - start}; CASCADE_RESULT(main_thread_stack_top, - page_table->AllocateAndMapMemory( + page_table.AllocateAndMapMemory( main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, KMemoryState::Stack, KMemoryPermission::UserReadWrite)); @@ -681,4 +685,12 @@ Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { return ResultSuccess; } +void KProcess::FinalizeHandleTable() { + // Finalize the table. + handle_table.Finalize(); + + // Note that the table is finalized. + is_handle_table_initialized = false; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index b1c7da4543..fcc2897f99 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -13,6 +13,7 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_handle_table.h" +#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_worker_task.h" @@ -31,7 +32,6 @@ class ProgramMetadata; namespace Kernel { class KernelCore; -class KPageTable; class KResourceLimit; class KThread; class KSharedMemoryInfo; @@ -107,12 +107,12 @@ public: /// Gets a reference to the process' page table. KPageTable& PageTable() { - return *page_table; + return page_table; } /// Gets const a reference to the process' page table. const KPageTable& PageTable() const { - return *page_table; + return page_table; } /// Gets a reference to the process' handle table. @@ -150,9 +150,8 @@ public: return address_arbiter.WaitForAddress(address, arb_type, value, timeout); } - /// Gets the address to the process' dedicated TLS region. - VAddr GetTLSRegionAddress() const { - return tls_region_address; + VAddr GetProcessLocalRegionAddress() const { + return plr_address; } /// Gets the current status of the process @@ -279,18 +278,18 @@ public: } /// Retrieves the total physical memory available to this process in bytes. - u64 GetTotalPhysicalMemoryAvailable() const; + u64 GetTotalPhysicalMemoryAvailable(); /// Retrieves the total physical memory available to this process in bytes, /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const; + u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource(); /// Retrieves the total physical memory used by this process in bytes. - u64 GetTotalPhysicalMemoryUsed() const; + u64 GetTotalPhysicalMemoryUsed(); /// Retrieves the total physical memory used by this process in bytes, /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; + u64 GetTotalPhysicalMemoryUsedWithoutSystemResource(); /// Gets the list of all threads created with this process as their owner. std::list& GetThreadList() { @@ -413,8 +412,10 @@ private: /// Allocates the main thread stack for the process, given the stack size in bytes. Result AllocateMainThreadStack(std::size_t stack_size); + void FinalizeHandleTable(); + /// Memory manager for this process - std::unique_ptr page_table; + KPageTable page_table; /// Current status of the process State state{}; @@ -433,6 +434,8 @@ private: /// Resource limit descriptor for this process KResourceLimit* resource_limit{}; + VAddr system_resource_address{}; + /// The ideal CPU core for this process, threads are scheduled on this core by default. u8 ideal_core = 0; @@ -459,7 +462,7 @@ private: KConditionVariable condition_var; /// Address indicating the location of the process' dedicated TLS region. - VAddr tls_region_address = 0; + VAddr plr_address = 0; /// Random values for svcGetInfo RandomEntropy std::array random_entropy{}; @@ -485,8 +488,12 @@ private: /// Schedule count of this process s64 schedule_count{}; + size_t memory_release_hint{}; + bool is_signaled{}; bool is_suspended{}; + bool is_immortal{}; + bool is_handle_table_initialized{}; bool is_initialized{}; std::atomic num_running_threads{}; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b6bbd4984b..6879de9ef3 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -95,6 +95,15 @@ struct KernelCore::Impl { } } + void CloseCurrentProcess() { + (*current_process).Finalize(); + // current_process->Close(); + // TODO: The current process should be destroyed based on accurate ref counting after + // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. + (*current_process).Destroy(); + current_process = nullptr; + } + void Shutdown() { is_shutting_down.store(true, std::memory_order_relaxed); SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); @@ -157,15 +166,7 @@ struct KernelCore::Impl { } } - // Shutdown all processes. - if (current_process) { - (*current_process).Finalize(); - // current_process->Close(); - // TODO: The current process should be destroyed based on accurate ref counting after - // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. - (*current_process).Destroy(); - current_process = nullptr; - } + CloseCurrentProcess(); // Track kernel objects that were not freed on shutdown { @@ -870,6 +871,10 @@ const KProcess* KernelCore::CurrentProcess() const { return impl->current_process; } +void KernelCore::CloseCurrentProcess() { + impl->CloseCurrentProcess(); +} + const std::vector& KernelCore::GetProcessList() const { return impl->process_list; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 79e66483ee..6eded95393 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -131,6 +131,9 @@ public: /// Retrieves a const pointer to the current process. const KProcess* CurrentProcess() const; + /// Closes the current process. + void CloseCurrentProcess(); + /// Retrieves the list of processes. const std::vector& GetProcessList() const; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index bac61fd096..b07ae3f027 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -933,7 +933,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultSuccess; case GetInfoType::UserExceptionContextAddr: - *result = process->GetTLSRegionAddress(); + *result = process->GetProcessLocalRegionAddress(); return ResultSuccess; case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: From 1b787adbd0b58986e6efdf6d536dcc949362c108 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 10 Sep 2022 23:45:07 -0700 Subject: [PATCH 174/245] core: hle: kernel: Fix InitializePreemption order. --- src/core/hle/kernel/kernel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 6879de9ef3..eed2dc9f3e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -74,8 +74,8 @@ struct KernelCore::Impl { InitializeMemoryLayout(); Init::InitializeKPageBufferSlabHeap(system); InitializeShutdownThreads(); - InitializePreemption(kernel); InitializePhysicalCores(); + InitializePreemption(kernel); // Initialize the Dynamic Slab Heaps. { From a4d11f4427859cbe82fc825f8493844e17bb668f Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 10 Sep 2022 01:48:15 -0700 Subject: [PATCH 175/245] core: Partially persist emulation state across game boots. --- src/core/core.cpp | 65 +++++++++++++++++++--------------- src/core/core.h | 10 ++++-- src/core/core_timing.cpp | 29 +++++++-------- src/core/core_timing.h | 7 ++-- src/tests/core/core_timing.cpp | 3 -- src/yuzu/bootmanager.cpp | 4 +-- src/yuzu/main.cpp | 1 + src/yuzu_cmd/yuzu.cpp | 4 ++- 8 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 1deeee1545..2c4c0dbe40 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -133,6 +133,30 @@ struct System::Impl { : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} + void Initialize(System& system) { + device_memory = std::make_unique(); + + is_multicore = Settings::values.use_multi_core.GetValue(); + + core_timing.SetMulticore(is_multicore); + core_timing.Initialize([&system]() { system.RegisterHostThread(); }); + + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); + const auto current_time = + std::chrono::duration_cast(posix_time).count(); + Settings::values.custom_rtc_differential = + Settings::values.custom_rtc.value_or(current_time) - current_time; + + // Create a default fs if one doesn't already exist. + if (virtual_filesystem == nullptr) + virtual_filesystem = std::make_shared(); + if (content_provider == nullptr) + content_provider = std::make_unique(); + + // Create default implementations of applets if one is not provided. + applet_manager.SetDefaultAppletsIfMissing(); + } + SystemResultStatus Run() { std::unique_lock lk(suspend_guard); status = SystemResultStatus::Success; @@ -178,37 +202,17 @@ struct System::Impl { debugger = std::make_unique(system, port); } - SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { + SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(Core, "initialized OK"); - device_memory = std::make_unique(); - - is_multicore = Settings::values.use_multi_core.GetValue(); is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); kernel.SetMulticore(is_multicore); cpu_manager.SetMulticore(is_multicore); cpu_manager.SetAsyncGpu(is_async_gpu); - core_timing.SetMulticore(is_multicore); kernel.Initialize(); cpu_manager.Initialize(); - core_timing.Initialize([&system]() { system.RegisterHostThread(); }); - - const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); - const auto current_time = - std::chrono::duration_cast(posix_time).count(); - Settings::values.custom_rtc_differential = - Settings::values.custom_rtc.value_or(current_time) - current_time; - - // Create a default fs if one doesn't already exist. - if (virtual_filesystem == nullptr) - virtual_filesystem = std::make_shared(); - if (content_provider == nullptr) - content_provider = std::make_unique(); - - /// Create default implementations of applets if one is not provided. - applet_manager.SetDefaultAppletsIfMissing(); /// Reset all glue registrations arp_manager.ResetAll(); @@ -253,11 +257,11 @@ struct System::Impl { return SystemResultStatus::ErrorGetLoader; } - SystemResultStatus init_result{Init(system, emu_window)}; + SystemResultStatus init_result{SetupForMainProcess(system, emu_window)}; if (init_result != SystemResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", static_cast(init_result)); - Shutdown(); + ShutdownMainProcess(); return init_result; } @@ -276,7 +280,7 @@ struct System::Impl { const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); if (load_result != Loader::ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result); - Shutdown(); + ShutdownMainProcess(); return static_cast( static_cast(SystemResultStatus::ErrorLoader) + static_cast(load_result)); @@ -335,7 +339,7 @@ struct System::Impl { return status; } - void Shutdown() { + void ShutdownMainProcess() { SetShuttingDown(true); // Log last frame performance stats if game was loded @@ -369,7 +373,7 @@ struct System::Impl { cheat_engine.reset(); telemetry_session.reset(); time_manager.Shutdown(); - core_timing.Shutdown(); + core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); gpu_core.reset(); @@ -377,7 +381,6 @@ struct System::Impl { perf_stats.reset(); kernel.Shutdown(); memory.Reset(); - applet_manager.ClearAll(); if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info{}; @@ -520,6 +523,10 @@ const CpuManager& System::GetCpuManager() const { return impl->cpu_manager; } +void System::Initialize() { + impl->Initialize(*this); +} + SystemResultStatus System::Run() { return impl->Run(); } @@ -540,8 +547,8 @@ void System::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { impl->kernel.InvalidateCpuInstructionCacheRange(addr, size); } -void System::Shutdown() { - impl->Shutdown(); +void System::ShutdownMainProcess() { + impl->ShutdownMainProcess(); } bool System::IsShuttingDown() const { diff --git a/src/core/core.h b/src/core/core.h index 7843cc8ad9..4ebedffd91 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -142,6 +142,12 @@ public: System(System&&) = delete; System& operator=(System&&) = delete; + /** + * Initializes the system + * This function will initialize core functionaility used for system emulation + */ + void Initialize(); + /** * Run the OS and Application * This function will start emulation and run the relevant devices @@ -166,8 +172,8 @@ public: void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); - /// Shutdown the emulated system. - void Shutdown(); + /// Shutdown the main emulated process. + void ShutdownMainProcess(); /// Check if the core is shutting down. [[nodiscard]] bool IsShuttingDown() const; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 2678ce5328..2afb2696cc 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -40,7 +40,17 @@ struct CoreTiming::Event { CoreTiming::CoreTiming() : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} -CoreTiming::~CoreTiming() = default; +CoreTiming::~CoreTiming() { + paused = true; + shutting_down = true; + pause_event.Set(); + event.Set(); + if (timer_thread) { + timer_thread->join(); + } + timer_thread.reset(); + has_started = false; +} void CoreTiming::ThreadEntry(CoreTiming& instance) { constexpr char name[] = "HostTiming"; @@ -65,17 +75,8 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { } } -void CoreTiming::Shutdown() { - paused = true; - shutting_down = true; - pause_event.Set(); - event.Set(); - if (timer_thread) { - timer_thread->join(); - } - ClearPendingEvents(); - timer_thread.reset(); - has_started = false; +void CoreTiming::ClearPendingEvents() { + event_queue.clear(); } void CoreTiming::Pause(bool is_paused) { @@ -196,10 +197,6 @@ u64 CoreTiming::GetClockTicks() const { return CpuCyclesToClockCycles(ticks); } -void CoreTiming::ClearPendingEvents() { - event_queue.clear(); -} - void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { std::scoped_lock lock{basic_lock}; diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 3259397b28..7996b529fe 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -61,8 +61,8 @@ public: /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. void Initialize(std::function&& on_thread_init_); - /// Tears down all timing related functionality. - void Shutdown(); + /// Clear all pending events. This should ONLY be done on exit. + void ClearPendingEvents(); /// Sets if emulation is multicore or single core, must be set before Initialize void SetMulticore(bool is_multicore_) { @@ -136,9 +136,6 @@ public: private: struct Event; - /// Clear all pending events. This should ONLY be done on exit. - void ClearPendingEvents(); - static void ThreadEntry(CoreTiming& instance); void ThreadLoop(); diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 7c432a63c5..284b2ae66f 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -40,9 +40,6 @@ struct ScopeInit final { core_timing.SetMulticore(true); core_timing.Initialize([]() {}); } - ~ScopeInit() { - core_timing.Shutdown(); - } Core::Timing::CoreTiming core_timing; }; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 24251247d2..6acfb7b06c 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -120,8 +120,8 @@ void EmuThread::run() { } } - // Shutdown the core emulation - system.Shutdown(); + // Shutdown the main emulated process + system.ShutdownMainProcess(); #if MICROPROFILE_ENABLED MicroProfileOnThreadExit(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a94624be63..501c342551 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -294,6 +294,7 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan #ifdef __linux__ SetupSigInterrupts(); #endif + system->Initialize(); Common::Log::Initialize(); LoadTranslation(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 3a0f33cba1..e16f79eb40 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -302,6 +302,8 @@ int main(int argc, char** argv) { } Core::System system{}; + system.Initialize(); + InputCommon::InputSubsystem input_subsystem{}; // Apply the command line arguments @@ -392,7 +394,7 @@ int main(int argc, char** argv) { } system.DetachDebugger(); void(system.Pause()); - system.Shutdown(); + system.ShutdownMainProcess(); detached_tasks.WaitForAllTasks(); return 0; From 829e82e264504696ce1d0ae9421a53d16bf104ea Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 14 Oct 2022 22:55:51 -0700 Subject: [PATCH 176/245] core: hle: kernel: Use result macros for new/changed code. --- src/core/hle/kernel/k_dynamic_page_manager.h | 2 +- .../hle/kernel/k_memory_block_manager.cpp | 2 +- src/core/hle/kernel/k_memory_block_manager.h | 2 +- src/core/hle/kernel/k_page_table.cpp | 111 ++++++++---------- src/core/hle/kernel/k_page_table.h | 19 +-- src/core/hle/kernel/k_process.cpp | 42 +++---- src/core/hle/kernel/k_process.h | 16 ++- src/core/hle/kernel/k_thread.cpp | 41 +++---- src/core/hle/result.h | 3 - 9 files changed, 110 insertions(+), 128 deletions(-) diff --git a/src/core/hle/kernel/k_dynamic_page_manager.h b/src/core/hle/kernel/k_dynamic_page_manager.h index 88d53776ae..9076c8fa3c 100644 --- a/src/core/hle/kernel/k_dynamic_page_manager.h +++ b/src/core/hle/kernel/k_dynamic_page_manager.h @@ -65,7 +65,7 @@ public: m_page_bitmap.SetBit(i); } - return ResultSuccess; + R_SUCCEED(); } VAddr GetAddress() const { diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp index c908af75a9..cf4c1e371b 100644 --- a/src/core/hle/kernel/k_memory_block_manager.cpp +++ b/src/core/hle/kernel/k_memory_block_manager.cpp @@ -23,7 +23,7 @@ Result KMemoryBlockManager::Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManag KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None); m_memory_block_tree.insert(*start_block); - return ResultSuccess; + R_SUCCEED(); } void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index b4ee4e319d..9b5873883d 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -35,7 +35,7 @@ private: R_UNLESS(m_blocks[m_index + i] != nullptr, ResultOutOfResource); } - return ResultSuccess; + R_SUCCEED(); } public: diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 22098c056e..307e491cb5 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -128,12 +128,9 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type alloc_start = process_code_end; alloc_size = end - process_code_end; } - const size_t needed_size{ - (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; - if (alloc_size < needed_size) { - ASSERT(false); - return ResultOutOfMemory; - } + const size_t needed_size = + (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size); + R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory); const size_t remaining_size{alloc_size - needed_size}; @@ -259,8 +256,9 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type m_page_table_impl = std::make_unique(); m_page_table_impl->Resize(m_address_space_width, PageBits); - return m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, - m_memory_block_slab_manager); + // Initialize our memory block manager. + R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, + m_memory_block_slab_manager)); } void KPageTable::Finalize() { @@ -306,7 +304,7 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size) { @@ -385,7 +383,7 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si KMemoryBlockDisableMergeAttribute::None); } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size, @@ -487,7 +485,7 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t reprotected_pages = true; } - return ResultSuccess; + R_SUCCEED(); } VAddr KPageTable::FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, @@ -558,7 +556,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); R_TRY(pg.AddBlock(cur_addr, cur_pages)); - return ResultSuccess; + R_SUCCEED(); } bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { @@ -685,7 +683,7 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& s m_system.InvalidateCpuInstructionCaches(); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { @@ -933,7 +931,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // Cancel our guard. unmap_guard.Cancel(); - return ResultSuccess; + R_SUCCEED(); } } } @@ -1176,7 +1174,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { // We succeeded. remap_guard.Cancel(); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { @@ -1243,7 +1241,7 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { @@ -1288,9 +1286,7 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size AddRegionToPages(src_address, num_pages, src_pages); AddRegionToPages(dst_address, num_pages, dst_pages); - if (!dst_pages.IsEqual(src_pages)) { - return ResultInvalidMemoryRegion; - } + R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion); { auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); @@ -1312,7 +1308,7 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, @@ -1330,13 +1326,13 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) .IsSuccess()); - return result; + R_RETURN(result); } cur_addr += node.GetNumPages() * PageSize; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, @@ -1367,7 +1363,7 @@ Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemory KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, @@ -1413,7 +1409,7 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, // We successfully mapped the pages. *out_addr = addr; - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { @@ -1425,13 +1421,13 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, OperationType::Unmap)}; result.IsError()) { - return result; + R_RETURN(result); } cur_addr += node.GetNumPages() * PageSize; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { @@ -1465,7 +1461,7 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { @@ -1498,7 +1494,7 @@ Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState stat KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, @@ -1523,7 +1519,7 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n // Create a new page group for the region. R_TRY(this->MakePageGroup(*out, address, num_pages)); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, @@ -1589,7 +1585,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, m_system.InvalidateCpuInstructionCacheRange(addr, size); } - return ResultSuccess; + R_SUCCEED(); } KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { @@ -1653,7 +1649,7 @@ Result KPageTable::SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermi KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr) { @@ -1696,7 +1692,7 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 att new_attr, KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::SetMaxHeapSize(size_t size) { @@ -1708,7 +1704,7 @@ Result KPageTable::SetMaxHeapSize(size_t size) { m_max_heap_size = size; - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::SetHeapSize(VAddr* out, size_t size) { @@ -1769,11 +1765,11 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Set the output. *out = m_heap_region_start; - return ResultSuccess; + R_SUCCEED(); } else if (size == GetHeapSize()) { // The size requested is exactly the current size. *out = m_heap_region_start; - return ResultSuccess; + R_SUCCEED(); } else { // We have to allocate memory. Determine how much to allocate and where while the table // is locked. @@ -1847,7 +1843,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Set the output. *out = m_heap_region_start; - return ResultSuccess; + R_SUCCEED(); } } @@ -1857,19 +1853,12 @@ ResultVal KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_ KMemoryPermission perm, PAddr map_addr) { KScopedLightLock lk(m_general_lock); - if (!CanContain(region_start, region_num_pages * PageSize, state)) { - return ResultInvalidCurrentMemory; - } - - if (region_num_pages <= needed_num_pages) { - return ResultOutOfMemory; - } - + R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state), + ResultInvalidCurrentMemory); + R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory); const VAddr addr{ AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; - if (!addr) { - return ResultOutOfMemory; - } + R_UNLESS(addr, ResultOutOfMemory); // Create an update allocator. Result allocator_result{ResultSuccess}; @@ -1922,7 +1911,7 @@ Result KPageTable::LockForMapDeviceAddressSpace(VAddr address, size_t size, KMem m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::ShareToDevice, KMemoryPermission::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size) { @@ -1956,7 +1945,7 @@ Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size) { m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, KMemoryPermission::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) { @@ -1984,24 +1973,24 @@ Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) { m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) { - return this->LockMemoryAndOpen( + R_RETURN(this->LockMemoryAndOpen( out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None, static_cast(KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite), - KMemoryAttribute::Locked); + KMemoryAttribute::Locked)); } Result KPageTable::UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg) { - return this->UnlockMemory( + R_RETURN(this->UnlockMemory( addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, - KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); + KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg)); } bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { @@ -2056,7 +2045,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ addr += size; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, @@ -2083,7 +2072,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, default: ASSERT(false); } - return ResultSuccess; + R_SUCCEED(); } VAddr KPageTable::GetRegionAddress(KMemoryState state) const { @@ -2211,7 +2200,7 @@ Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_ R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory); R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size, @@ -2253,7 +2242,7 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr a *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, @@ -2315,7 +2304,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* if (out_blocks_needed != nullptr) { *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, @@ -2381,7 +2370,7 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr new_attr, KMemoryBlockDisableMergeAttribute::Locked, KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, @@ -2436,7 +2425,7 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask new_attr, KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); - return ResultSuccess; + R_SUCCEED(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 1811d3e2d2..c6aeacd96c 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -57,9 +57,9 @@ public: KMemoryPermission perm); Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, KMemoryState state, KMemoryPermission perm) { - return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, - this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, - state, perm); + R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, + this->GetRegionAddress(state), + this->GetRegionSize(state) / PageSize, state, perm)); } Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state); @@ -137,8 +137,8 @@ private: KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const { - return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, - perm, attr_mask, attr); + R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, + perm, attr_mask, attr)); } Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, @@ -155,15 +155,16 @@ private: KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { - return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, - state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); + R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, + state_mask, state, perm_mask, perm, attr_mask, attr, + ignore_attr)); } Result CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { - return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, - attr_mask, attr, ignore_attr); + R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, + attr_mask, attr, ignore_attr)); } Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 1a0aec56a7..8c3495e5a5 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -98,7 +98,7 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string // Open a reference to the resource limit. process->resource_limit->Open(); - return ResultSuccess; + R_SUCCEED(); } void KProcess::DoWorkerTaskImpl() { @@ -246,7 +246,7 @@ Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr ad shmem->Open(); shemen_info->Open(); - return ResultSuccess; + R_SUCCEED(); } void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, @@ -296,7 +296,7 @@ Result KProcess::Reset() { // Clear signaled. is_signaled = false; - return ResultSuccess; + R_SUCCEED(); } Result KProcess::SetActivity(ProcessActivity activity) { @@ -312,9 +312,7 @@ Result KProcess::SetActivity(ProcessActivity activity) { // Either pause or resume. if (activity == ProcessActivity::Paused) { // Verify that we're not suspended. - if (is_suspended) { - return ResultInvalidState; - } + R_UNLESS(!is_suspended, ResultInvalidState); // Suspend all threads. for (auto* thread : GetThreadList()) { @@ -327,9 +325,7 @@ Result KProcess::SetActivity(ProcessActivity activity) { ASSERT(activity == ProcessActivity::Runnable); // Verify that we're suspended. - if (!is_suspended) { - return ResultInvalidState; - } + R_UNLESS(is_suspended, ResultInvalidState); // Resume all threads. for (auto* thread : GetThreadList()) { @@ -340,7 +336,7 @@ Result KProcess::SetActivity(ProcessActivity activity) { SetSuspended(false); } - return ResultSuccess; + R_SUCCEED(); } Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) { @@ -358,14 +354,14 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: if (!memory_reservation.Succeeded()) { LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", code_size + system_resource_size); - return ResultLimitReached; + R_RETURN(ResultLimitReached); } // Initialize proces address space if (const Result result{page_table.InitializeForProcess( metadata.GetAddressSpaceType(), false, 0x8000000, code_size, &kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)}; result.IsError()) { - return result; + R_RETURN(result); } // Map process code region @@ -373,7 +369,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: code_size / PageSize, KMemoryState::Code, KMemoryPermission::None)}; result.IsError()) { - return result; + R_RETURN(result); } // Initialize process capabilities @@ -381,7 +377,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: if (const Result result{ capabilities.InitializeForUserProcess(caps.data(), caps.size(), page_table)}; result.IsError()) { - return result; + R_RETURN(result); } // Set memory usage capacity @@ -405,7 +401,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: R_TRY(this->CreateThreadLocalRegion(std::addressof(plr_address))); memory_reservation.Commit(); - return handle_table.Initialize(capabilities.GetHandleTableSize()); + R_RETURN(handle_table.Initialize(capabilities.GetHandleTableSize())); } void KProcess::Run(s32 main_thread_priority, u64 stack_size) { @@ -504,7 +500,7 @@ Result KProcess::CreateThreadLocalRegion(VAddr* out) { } *out = tlr; - return ResultSuccess; + R_SUCCEED(); } } @@ -533,7 +529,7 @@ Result KProcess::CreateThreadLocalRegion(VAddr* out) { // We succeeded! tlp_guard.Cancel(); *out = tlr; - return ResultSuccess; + R_SUCCEED(); } Result KProcess::DeleteThreadLocalRegion(VAddr addr) { @@ -581,7 +577,7 @@ Result KProcess::DeleteThreadLocalRegion(VAddr addr) { KThreadLocalPage::Free(kernel, page_to_free); } - return ResultSuccess; + R_SUCCEED(); } bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size, @@ -682,15 +678,7 @@ Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { main_thread_stack_top += main_thread_stack_size; - return ResultSuccess; -} - -void KProcess::FinalizeHandleTable() { - // Finalize the table. - handle_table.Finalize(); - - // Note that the table is finalized. - is_handle_table_initialized = false; + R_SUCCEED(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index fcc2897f99..788faec1d5 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -138,16 +138,16 @@ public: } Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { - return condition_var.Wait(address, cv_key, tag, ns); + R_RETURN(condition_var.Wait(address, cv_key, tag, ns)); } Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) { - return address_arbiter.SignalToAddress(address, signal_type, value, count); + R_RETURN(address_arbiter.SignalToAddress(address, signal_type, value, count)); } Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, s64 timeout) { - return address_arbiter.WaitForAddress(address, arb_type, value, timeout); + R_RETURN(address_arbiter.WaitForAddress(address, arb_type, value, timeout)); } VAddr GetProcessLocalRegionAddress() const { @@ -407,13 +407,19 @@ private: pinned_threads[core_id] = nullptr; } + void FinalizeHandleTable() { + // Finalize the table. + handle_table.Finalize(); + + // Note that the table is finalized. + is_handle_table_initialized = false; + } + void ChangeState(State new_state); /// Allocates the main thread stack for the process, given the stack size in bytes. Result AllocateMainThreadStack(std::size_t stack_size); - void FinalizeHandleTable(); - /// Memory manager for this process KPageTable page_table; diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 89b32d509e..b7bfcdce31 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -245,7 +245,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack } } - return ResultSuccess; + R_SUCCEED(); } Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, @@ -258,7 +258,7 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_ thread->host_context = std::make_shared(std::move(init_func)); thread->is_single_core = !Settings::values.use_multi_core.GetValue(); - return ResultSuccess; + R_SUCCEED(); } Result KThread::InitializeDummyThread(KThread* thread) { @@ -268,31 +268,32 @@ Result KThread::InitializeDummyThread(KThread* thread) { // Initialize emulation parameters. thread->stack_parameters.disable_count = 0; - return ResultSuccess; + R_SUCCEED(); } Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { - return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - system.GetCpuManager().GetGuestActivateFunc()); + R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, + ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc())); } Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { - return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - system.GetCpuManager().GetIdleThreadStartFunc()); + R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, + ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc())); } Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, s32 virt_core) { - return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, - system.GetCpuManager().GetShutdownThreadStartFunc()); + R_RETURN(InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, + ThreadType::HighPriority, + system.GetCpuManager().GetShutdownThreadStartFunc())); } Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); - return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()); + R_RETURN(InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, + ThreadType::User, system.GetCpuManager().GetGuestThreadFunc())); } void KThread::PostDestroy(uintptr_t arg) { @@ -542,7 +543,7 @@ Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { *out_ideal_core = virtual_ideal_core_id; *out_affinity_mask = virtual_affinity_mask; - return ResultSuccess; + R_SUCCEED(); } Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { @@ -558,7 +559,7 @@ Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask(); } - return ResultSuccess; + R_SUCCEED(); } Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { @@ -670,7 +671,7 @@ Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { } while (retry_update); } - return ResultSuccess; + R_SUCCEED(); } void KThread::SetBasePriority(s32 value) { @@ -843,7 +844,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) { } while (thread_is_current); } - return ResultSuccess; + R_SUCCEED(); } Result KThread::GetThreadContext3(std::vector& out) { @@ -878,7 +879,7 @@ Result KThread::GetThreadContext3(std::vector& out) { } } - return ResultSuccess; + R_SUCCEED(); } void KThread::AddWaiterImpl(KThread* thread) { @@ -1042,7 +1043,7 @@ Result KThread::Run() { // Set our state and finish. SetState(ThreadState::Runnable); - return ResultSuccess; + R_SUCCEED(); } } @@ -1089,7 +1090,7 @@ Result KThread::Terminate() { Svc::WaitInfinite)); } - return ResultSuccess; + R_SUCCEED(); } ThreadState KThread::RequestTerminate() { @@ -1162,7 +1163,7 @@ Result KThread::Sleep(s64 timeout) { // Check if the thread should terminate. if (this->IsTerminationRequested()) { slp.CancelSleep(); - return ResultTerminationRequested; + R_THROW(ResultTerminationRequested); } // Wait for the sleep to end. @@ -1170,7 +1171,7 @@ Result KThread::Sleep(s64 timeout) { SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); } - return ResultSuccess; + R_SUCCEED(); } void KThread::IfDummyThreadTryWait() { diff --git a/src/core/hle/result.h b/src/core/hle/result.h index d714dea38e..ef4b2d4173 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -470,9 +470,6 @@ constexpr inline Result __TmpCurrentResultReference = ResultSuccess; #define R_UNLESS(expr, res) \ { \ if (!(expr)) { \ - if (res.IsError()) { \ - LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ - } \ R_THROW(res); \ } \ } From 11f85ea7130a5245bd8d2090f0dd76ba65f15d23 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 14 Oct 2022 23:37:02 -0700 Subject: [PATCH 177/245] core: core_timing: Remove unused IsHostTiming. --- src/core/core_timing.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 7996b529fe..bd21dd904c 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -69,11 +69,6 @@ public: is_multicore = is_multicore_; } - /// Check if it's using host timing. - bool IsHostTiming() const { - return is_multicore; - } - /// Pauses/Unpauses the execution of the timer thread. void Pause(bool is_paused); From 638fa6170a8a4c36ffa644055e683a7e50aa7ae5 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 15 Oct 2022 00:48:28 -0700 Subject: [PATCH 178/245] core: core_timing: Re-initialize if single/multicore state changes. --- src/core/core.cpp | 25 ++++++++++++++++++++----- src/core/core_timing.cpp | 23 ++++++++++++++--------- src/core/core_timing.h | 2 ++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 2c4c0dbe40..622a20510c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -155,6 +155,24 @@ struct System::Impl { // Create default implementations of applets if one is not provided. applet_manager.SetDefaultAppletsIfMissing(); + + is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); + + kernel.SetMulticore(is_multicore); + cpu_manager.SetMulticore(is_multicore); + cpu_manager.SetAsyncGpu(is_async_gpu); + } + + void ReinitializeIfNecessary(System& system) { + if (is_multicore == Settings::values.use_multi_core.GetValue()) { + return; + } + + LOG_DEBUG(Kernel, "Re-initializing"); + + is_multicore = Settings::values.use_multi_core.GetValue(); + + Initialize(system); } SystemResultStatus Run() { @@ -205,11 +223,8 @@ struct System::Impl { SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(Core, "initialized OK"); - is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); - - kernel.SetMulticore(is_multicore); - cpu_manager.SetMulticore(is_multicore); - cpu_manager.SetAsyncGpu(is_async_gpu); + // Setting changes may require a full system reinitialization (e.g., disabling multicore). + ReinitializeIfNecessary(system); kernel.Initialize(); cpu_manager.Initialize(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 2afb2696cc..0e7b5f9436 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -41,15 +41,7 @@ CoreTiming::CoreTiming() : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} CoreTiming::~CoreTiming() { - paused = true; - shutting_down = true; - pause_event.Set(); - event.Set(); - if (timer_thread) { - timer_thread->join(); - } - timer_thread.reset(); - has_started = false; + Reset(); } void CoreTiming::ThreadEntry(CoreTiming& instance) { @@ -63,6 +55,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { } void CoreTiming::Initialize(std::function&& on_thread_init_) { + Reset(); on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; shutting_down = false; @@ -304,6 +297,18 @@ void CoreTiming::ThreadLoop() { } } +void CoreTiming::Reset() { + paused = true; + shutting_down = true; + pause_event.Set(); + event.Set(); + if (timer_thread) { + timer_thread->join(); + } + timer_thread.reset(); + has_started = false; +} + std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { if (is_multicore) { return clock->GetTimeNS(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index bd21dd904c..b5925193c7 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -134,6 +134,8 @@ private: static void ThreadEntry(CoreTiming& instance); void ThreadLoop(); + void Reset(); + std::unique_ptr clock; s64 global_timer = 0; From a264b54022d97824a5889c711f4977bc4ecdbca3 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 18 Oct 2022 19:12:18 -0700 Subject: [PATCH 179/245] core: Initialize: Add missing braces. --- src/core/core.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 622a20510c..7fb8bc0195 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -148,10 +148,12 @@ struct System::Impl { Settings::values.custom_rtc.value_or(current_time) - current_time; // Create a default fs if one doesn't already exist. - if (virtual_filesystem == nullptr) + if (virtual_filesystem == nullptr) { virtual_filesystem = std::make_shared(); - if (content_provider == nullptr) + } + if (content_provider == nullptr) { content_provider = std::make_unique(); + } // Create default implementations of applets if one is not provided. applet_manager.SetDefaultAppletsIfMissing(); From 3cb44981420fb7d493e2b1ff9ee1e5670fae2486 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 19 Oct 2022 06:21:51 +0200 Subject: [PATCH 180/245] Maxwell3D/Puller: Fix regressions and syncing issues. --- src/video_core/engines/maxwell_3d.cpp | 17 +++++++---------- src/video_core/engines/puller.cpp | 5 ++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 84c1abf3d0..fdf470913e 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -473,9 +473,7 @@ void Maxwell3D::ProcessQueryGet() { switch (regs.report_semaphore.query.operation) { case Regs::ReportSemaphore::Operation::Release: - if (regs.report_semaphore.query.release == - Regs::ReportSemaphore::Release::AfterAllPreceedingWrites || - regs.report_semaphore.query.short_query != 0) { + if (regs.report_semaphore.query.short_query != 0) { const GPUVAddr sequence_address{regs.report_semaphore.Address()}; const u32 payload = regs.report_semaphore.payload; std::function operation([this, sequence_address, payload] { @@ -489,11 +487,10 @@ void Maxwell3D::ProcessQueryGet() { }; const GPUVAddr sequence_address{regs.report_semaphore.Address()}; const u32 payload = regs.report_semaphore.payload; - std::function operation([this, sequence_address, payload] { + [this, sequence_address, payload] { memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); memory_manager.Write(sequence_address, payload); - }); - rasterizer->SyncOperation(std::move(operation)); + }(); } break; case Regs::ReportSemaphore::Operation::Acquire: @@ -569,11 +566,11 @@ void Maxwell3D::ProcessCounterReset() { void Maxwell3D::ProcessSyncPoint() { const u32 sync_point = regs.sync_info.sync_point.Value(); - const auto condition = regs.sync_info.condition.Value(); - [[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value(); - if (condition == Regs::SyncInfo::Condition::RopWritesDone) { - rasterizer->SignalSyncPoint(sync_point); + const u32 cache_flush = regs.sync_info.clean_l2.Value(); + if (cache_flush != 0) { + rasterizer->InvalidateGPUCache(); } + rasterizer->SignalSyncPoint(sync_point); } void Maxwell3D::DrawArrays() { diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index cca890792a..3977bb0fb0 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -75,11 +75,10 @@ void Puller::ProcessSemaphoreTriggerMethod() { if (op == GpuSemaphoreOperation::WriteLong) { const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const u32 payload = regs.semaphore_sequence; - std::function operation([this, sequence_address, payload] { + [this, sequence_address, payload] { memory_manager.Write(sequence_address + sizeof(u64), gpu.GetTicks()); memory_manager.Write(sequence_address, payload); - }); - rasterizer->SignalFence(std::move(operation)); + }(); } else { do { const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; From 7bd3930939dfafc463b87b6df965b8b4391f1f56 Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Wed, 19 Oct 2022 05:38:12 +0100 Subject: [PATCH 181/245] Update audio_core for firmware 15.0.0 --- src/audio_core/renderer/system.cpp | 85 ++++++++++++------- src/audio_core/renderer/system.h | 16 ++++ .../renderer/voice/voice_context.cpp | 4 +- src/core/hle/service/audio/audctl.cpp | 16 ++++ src/core/hle/service/audio/audren_u.cpp | 26 ++++++ 5 files changed, 114 insertions(+), 33 deletions(-) diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index bde794cd1c..4fac30c7c8 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} Result System::Initialize(const AudioRendererParameterInternal& params, - Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, - const u32 process_handle_, const u64 applet_resource_user_id_, - const s32 session_id_) { + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, + u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) { if (!CheckValidRevision(params.revision)) { return Service::Audio::ERR_INVALID_REVISION; } @@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params, render_time_limit_percent = 100; drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto; + drop_voice_param = 1.0f; + num_voices_dropped = 0; allocator.Align(0x40); command_workbuffer_size = allocator.GetRemainingSize(); @@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const { return render_time_limit_percent; } -void System::SetRenderingTimeLimit(const u32 limit) { +void System::SetRenderingTimeLimit(u32 limit) { render_time_limit_percent = limit; } @@ -635,7 +636,7 @@ void System::SendCommandToDsp() { } u64 System::GenerateCommand(std::span in_command_buffer, - [[maybe_unused]] const u64 command_buffer_size_) { + [[maybe_unused]] u64 command_buffer_size_) { PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); const auto start_time{core.CoreTiming().GetClockTicks()}; @@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span in_command_buffer, voice_context.SortInfo(); - const auto start_estimated_time{command_buffer.estimated_process_time}; + const auto start_estimated_time{drop_voice_param * + static_cast(command_buffer.estimated_process_time)}; command_generator.GenerateVoiceCommands(); command_generator.GenerateSubMixCommands(); @@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span in_command_buffer, render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported(); time_limit_percent = 70.0f; } + + const auto end_estimated_time{drop_voice_param * + static_cast(command_buffer.estimated_process_time)}; + const auto estimated_time{start_estimated_time - end_estimated_time}; + const auto time_limit{static_cast( - static_cast(start_estimated_time - command_buffer.estimated_process_time) + - (((time_limit_percent / 100.0f) * 2'880'000.0) * - (static_cast(render_time_limit_percent) / 100.0f)))}; - num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit); + estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) * + (static_cast(render_time_limit_percent) / 100.0f)))}; + num_voices_dropped = + DropVoices(command_buffer, static_cast(start_estimated_time), time_limit); } command_list_header->buffer_size = command_buffer.size; @@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span in_command_buffer, return command_buffer.size; } -u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time, - const u32 time_limit) { +f32 System::GetVoiceDropParameter() const { + return drop_voice_param; +} + +void System::SetVoiceDropParameter(f32 voice_drop_) { + drop_voice_param = voice_drop_; +} + +u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) { u32 i{0}; auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)}; - ICommand* cmd{}; + ICommand* cmd{nullptr}; - for (; i < command_buffer.count; i++) { + // Find a first valid voice to drop + while (i < command_buffer.count) { cmd = reinterpret_cast(command_list); - if (cmd->type != CommandId::Performance && - cmd->type != CommandId::DataSourcePcmInt16Version1 && - cmd->type != CommandId::DataSourcePcmInt16Version2 && - cmd->type != CommandId::DataSourcePcmFloatVersion1 && - cmd->type != CommandId::DataSourcePcmFloatVersion2 && - cmd->type != CommandId::DataSourceAdpcmVersion1 && - cmd->type != CommandId::DataSourceAdpcmVersion2) { + if (cmd->type == CommandId::Performance || + cmd->type == CommandId::DataSourcePcmInt16Version1 || + cmd->type == CommandId::DataSourcePcmInt16Version2 || + cmd->type == CommandId::DataSourcePcmFloatVersion1 || + cmd->type == CommandId::DataSourcePcmFloatVersion2 || + cmd->type == CommandId::DataSourceAdpcmVersion1 || + cmd->type == CommandId::DataSourceAdpcmVersion2) { break; } command_list += cmd->size; + i++; } if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) { @@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces const auto node_id_type{cmd->node_id >> 28}; const auto node_id_base{cmd->node_id & 0xFFF}; + // If the new estimated process time falls below the limit, we're done dropping. if (estimated_process_time <= time_limit) { break; } @@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces break; } + // Don't drop voices marked with the highest priority. auto& voice_info{voice_context.GetInfo(node_id_base)}; if (voice_info.priority == HighestVoicePriority) { break; @@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces voices_dropped++; voice_info.voice_dropped = true; - if (i < command_buffer.count) { - while (cmd->node_id == node_id) { - if (cmd->type == CommandId::DepopPrepare) { - cmd->enabled = true; - } else if (cmd->type == CommandId::Performance || !cmd->enabled) { - cmd->enabled = false; - } - i++; - command_list += cmd->size; - cmd = reinterpret_cast(command_list); + // First iteration should drop the voice, and then iterate through all of the commands tied + // to the voice. We don't need reverb on a voice which we've just removed, for example. + // Depops can't be removed otherwise we'll introduce audio popping, and we don't + // remove perf commands. Lower the estimated time for each command dropped. + while (i < command_buffer.count && cmd->node_id == node_id) { + if (cmd->type == CommandId::DepopPrepare) { + cmd->enabled = true; + } else if (cmd->enabled && cmd->type != CommandId::Performance) { + cmd->enabled = false; + estimated_process_time -= static_cast( + drop_voice_param * static_cast(cmd->estimated_process_time)); } + command_list += cmd->size; + cmd = reinterpret_cast(command_list); + i++; } + i++; } return voices_dropped; } diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h index bcbe65b070..429196e41d 100644 --- a/src/audio_core/renderer/system.h +++ b/src/audio_core/renderer/system.h @@ -196,6 +196,20 @@ public: */ u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit); + /** + * Get the current voice drop parameter. + * + * @return The current voice drop. + */ + f32 GetVoiceDropParameter() const; + + /** + * Set the voice drop parameter. + * + * @param The new voice drop. + */ + void SetVoiceDropParameter(f32 voice_drop); + private: /// Core system Core::System& core; @@ -301,6 +315,8 @@ private: u32 num_voices_dropped{}; /// Tick that rendering started u64 render_start_tick{}; + /// Parameter to control the threshold for dropping voices if the audio graph gets too large + f32 drop_voice_param{1.0f}; }; } // namespace AudioRenderer diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp index eafb51b011..a501a677d8 100644 --- a/src/audio_core/renderer/voice/voice_context.cpp +++ b/src/audio_core/renderer/voice/voice_context.cpp @@ -74,8 +74,8 @@ void VoiceContext::SortInfo() { } std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) { - return a->priority != b->priority ? a->priority < b->priority - : a->sort_order < b->sort_order; + return a->priority != b->priority ? a->priority > b->priority + : a->sort_order > b->sort_order; }); } diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 4a2ae5f88a..5abf22ba49 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -45,9 +45,25 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { {32, nullptr, "GetActiveOutputTarget"}, {33, nullptr, "GetTargetDeviceInfo"}, {34, nullptr, "AcquireTargetNotification"}, + {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, + {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, + {37, nullptr, "SetHearingProtectionSafeguardEnabled"}, + {38, nullptr, "IsHearingProtectionSafeguardEnabled"}, + {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"}, + {40, nullptr, "GetSystemInformationForDebug"}, + {41, nullptr, "SetVolumeButtonLongPressTime"}, + {42, nullptr, "SetNativeVolumeForDebug"}, {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, + {10100, nullptr, "GetAudioVolumeDataForPlayReport"}, + {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"}, + {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"}, + {10103, nullptr, "GetAudioOutputTargetForPlayReport"}, + {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"}, + {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, + {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, + {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 60c30cd5bc..13423dca67 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -52,6 +52,8 @@ public: {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, + {12, &IAudioRenderer::SetVoiceDropParameter, "SetVoiceDropParameter"}, + {13, &IAudioRenderer::GetVoiceDropParameter, "GetVoiceDropParameter"}, }; // clang-format on RegisterHandlers(functions); @@ -205,6 +207,30 @@ private: LOG_DEBUG(Service_Audio, "called"); } + void SetVoiceDropParameter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::RequestParser rp{ctx}; + auto voice_drop_param{rp.Pop()}; + + auto& system_ = impl->GetSystem(); + system_.SetVoiceDropParameter(voice_drop_param); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetVoiceDropParameter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& system_ = impl->GetSystem(); + auto voice_drop_param{system_.GetVoiceDropParameter()}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(voice_drop_param); + } + KernelHelpers::ServiceContext service_context; Kernel::KEvent* rendered_event; Manager& manager; From 470e89a8edb73f6710ce652e2ecfe25049307109 Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Wed, 19 Oct 2022 03:51:51 -0700 Subject: [PATCH 182/245] UI: Add option to hide the compatibility list Option is added directly below the option for the addons column Defaulting to hide compatibility list. Changing default works properly. Co-authored-by: Piplup --- src/yuzu/configuration/config.cpp | 2 ++ src/yuzu/configuration/configure_ui.cpp | 3 +++ src/yuzu/configuration/configure_ui.ui | 7 +++++++ src/yuzu/game_list.cpp | 2 ++ src/yuzu/uisettings.h | 3 +++ 5 files changed, 17 insertions(+) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 195074bf25..927dd1069e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -819,6 +819,7 @@ void Config::ReadUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); ReadBasicSetting(UISettings::values.show_add_ons); + ReadBasicSetting(UISettings::values.show_compat); ReadBasicSetting(UISettings::values.game_icon_size); ReadBasicSetting(UISettings::values.folder_icon_size); ReadBasicSetting(UISettings::values.row_1_text_id); @@ -1414,6 +1415,7 @@ void Config::SaveUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); WriteBasicSetting(UISettings::values.show_add_ons); + WriteBasicSetting(UISettings::values.show_compat); WriteBasicSetting(UISettings::values.game_icon_size); WriteBasicSetting(UISettings::values.folder_icon_size); WriteBasicSetting(UISettings::values.row_1_text_id); diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 48f71b53c6..92e6da6ee8 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -72,6 +72,7 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent) // Force game list reload if any of the relevant settings are changed. connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); + connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); connect(ui->game_icon_size_combobox, QOverload::of(&QComboBox::currentIndexChanged), this, &ConfigureUi::RequestGameListUpdate); connect(ui->folder_icon_size_combobox, QOverload::of(&QComboBox::currentIndexChanged), @@ -109,6 +110,7 @@ void ConfigureUi::ApplyConfiguration() { UISettings::values.theme = ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); + UISettings::values.show_compat = ui->show_compat->isChecked(); UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); @@ -129,6 +131,7 @@ void ConfigureUi::SetConfiguration() { ui->language_combobox->setCurrentIndex( ui->language_combobox->findData(UISettings::values.language)); ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); + ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); ui->game_icon_size_combobox->setCurrentIndex( ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); ui->folder_icon_size_combobox->setCurrentIndex( diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index a50df7f6f8..f0b719ba31 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -76,6 +76,13 @@ + + + + Show Compatibility List + + + diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b127badc25..d6adfca16a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -335,6 +335,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid RetranslateUI(); tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); + tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); item_model->setSortRole(GameListItemPath::SortRole); connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); @@ -786,6 +787,7 @@ void GameList::PopulateAsync(QVector& game_dirs) { // Update the columns in case UISettings has changed tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); + tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); // Delete any rows that might already exist if we're repopulating item_model->removeRows(0, item_model->rowCount()); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 753797efc4..4f5b2a99d4 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -129,6 +129,9 @@ struct Values { Settings::Setting favorites_expanded{true, "favorites_expanded"}; QVector favorited_ids; + // Compatibility List + Settings::Setting show_compat{false, "show_compat"}; + bool configuration_applied; bool reset_to_defaults; Settings::Setting disable_web_applet{true, "disable_web_applet"}; From 97879faea43c1fad6cbb0b63573c75644705e2e9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 18 Oct 2022 19:13:20 -0700 Subject: [PATCH 183/245] core: hle: kernel: Migrate ProcessState to enum class. --- src/core/hle/kernel/k_process.h | 16 ++++++++-------- src/core/hle/kernel/svc_types.h | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 788faec1d5..2e0cc3d0bc 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -72,14 +72,14 @@ public: ~KProcess() override; enum class State { - Created = Svc::ProcessState_Created, - CreatedAttached = Svc::ProcessState_CreatedAttached, - Running = Svc::ProcessState_Running, - Crashed = Svc::ProcessState_Crashed, - RunningAttached = Svc::ProcessState_RunningAttached, - Terminating = Svc::ProcessState_Terminating, - Terminated = Svc::ProcessState_Terminated, - DebugBreak = Svc::ProcessState_DebugBreak, + Created = static_cast(Svc::ProcessState::Created), + CreatedAttached = static_cast(Svc::ProcessState::CreatedAttached), + Running = static_cast(Svc::ProcessState::Running), + Crashed = static_cast(Svc::ProcessState::Crashed), + RunningAttached = static_cast(Svc::ProcessState::RunningAttached), + Terminating = static_cast(Svc::ProcessState::Terminating), + Terminated = static_cast(Svc::ProcessState::Terminated), + DebugBreak = static_cast(Svc::ProcessState::DebugBreak), }; enum : u64 { diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index bb4f7b004b..abb9847fe8 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -97,15 +97,15 @@ constexpr inline s32 HighestThreadPriority = 0; constexpr inline s32 SystemThreadPriorityHighest = 16; -enum ProcessState : u32 { - ProcessState_Created = 0, - ProcessState_CreatedAttached = 1, - ProcessState_Running = 2, - ProcessState_Crashed = 3, - ProcessState_RunningAttached = 4, - ProcessState_Terminating = 5, - ProcessState_Terminated = 6, - ProcessState_DebugBreak = 7, +enum class ProcessState : u32 { + Created = 0, + CreatedAttached = 1, + Running = 2, + Crashed = 3, + RunningAttached = 4, + Terminating = 5, + Terminated = 6, + DebugBreak = 7, }; constexpr inline size_t ThreadLocalRegionSize = 0x200; From 3efb8eb2dc8bf14eecb7e731a61712e0290d9f5d Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 14 Oct 2022 21:24:25 -0400 Subject: [PATCH 184/245] kernel: add KSessionRequest --- src/core/CMakeLists.txt | 2 + src/core/hle/kernel/init/init_slab_setup.cpp | 2 + src/core/hle/kernel/k_client_session.cpp | 15 +- src/core/hle/kernel/k_linked_list.h | 1 + src/core/hle/kernel/k_page_buffer.h | 1 + src/core/hle/kernel/k_server_session.cpp | 141 +++++---- src/core/hle/kernel/k_server_session.h | 8 +- src/core/hle/kernel/k_session_request.cpp | 61 ++++ src/core/hle/kernel/k_session_request.h | 307 +++++++++++++++++++ src/core/hle/kernel/k_shared_memory_info.h | 3 +- src/core/hle/kernel/k_thread_local_page.h | 2 +- src/core/hle/kernel/kernel.h | 4 + src/core/hle/kernel/slab_helpers.h | 2 +- 13 files changed, 488 insertions(+), 61 deletions(-) create mode 100644 src/core/hle/kernel/k_session_request.cpp create mode 100644 src/core/hle/kernel/k_session_request.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e7fe675cbf..055bea6419 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -243,6 +243,8 @@ add_library(core STATIC hle/kernel/k_server_session.h hle/kernel/k_session.cpp hle/kernel/k_session.h + hle/kernel/k_session_request.cpp + hle/kernel/k_session_request.h hle/kernel/k_shared_memory.cpp hle/kernel/k_shared_memory.h hle/kernel/k_shared_memory_info.h diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index c84d36c8c2..477e4e4075 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -18,6 +18,7 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_session.h" +#include "core/hle/kernel/k_session_request.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory_info.h" #include "core/hle/kernel/k_system_control.h" @@ -34,6 +35,7 @@ namespace Kernel::Init { HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ + HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \ HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index 8892c5b7cb..b4197a8d56 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/scope_exit.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_server_session.h" @@ -10,6 +11,8 @@ namespace Kernel { +static constexpr u32 MessageBufferSize = 0x100; + KClientSession::KClientSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} KClientSession::~KClientSession() = default; @@ -22,8 +25,16 @@ void KClientSession::Destroy() { void KClientSession::OnServerClosed() {} Result KClientSession::SendSyncRequest() { - // Signal the server session that new data is available - return parent->GetServerSession().OnRequest(); + // Create a session request. + KSessionRequest* request = KSessionRequest::Create(kernel); + R_UNLESS(request != nullptr, ResultOutOfResource); + SCOPE_EXIT({ request->Close(); }); + + // Initialize the request. + request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize); + + // Send the request. + return parent->GetServerSession().OnRequest(request); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h index 78859ced30..29ebd16b77 100644 --- a/src/core/hle/kernel/k_linked_list.h +++ b/src/core/hle/kernel/k_linked_list.h @@ -16,6 +16,7 @@ class KLinkedListNode : public boost::intrusive::list_base_hook<>, public KSlabAllocated { public: + explicit KLinkedListNode(KernelCore&) {} KLinkedListNode() = default; void Initialize(void* it) { diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h index 7e50dc1d18..aef06e2130 100644 --- a/src/core/hle/kernel/k_page_buffer.h +++ b/src/core/hle/kernel/k_page_buffer.h @@ -13,6 +13,7 @@ namespace Kernel { class KPageBuffer final : public KSlabAllocated { public: + explicit KPageBuffer(KernelCore&) {} KPageBuffer() = default; static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 4252c9adbd..685a2a6e67 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -29,8 +29,6 @@ namespace Kernel { using ThreadQueueImplForKServerSessionRequest = KThreadQueue; -static constexpr u32 MessageBufferSize = 0x100; - KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_}, m_lock{kernel_} {} @@ -73,7 +71,7 @@ bool KServerSession::IsSignaled() const { } // Otherwise, we're signaled if we have a request and aren't handling one. - return !m_thread_request_list.empty() && m_current_thread_request == nullptr; + return !m_request_list.empty() && m_current_request == nullptr; } void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { @@ -178,7 +176,7 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { return result; } -Result KServerSession::OnRequest() { +Result KServerSession::OnRequest(KSessionRequest* request) { // Create the wait queue. ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; @@ -198,14 +196,13 @@ Result KServerSession::OnRequest() { this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); } else { // Non-HLE request. - auto* thread{GetCurrentThreadPointer(kernel)}; // Get whether we're empty. - const bool was_empty = m_thread_request_list.empty(); + const bool was_empty = m_request_list.empty(); - // Add the thread to the list. - thread->Open(); - m_thread_request_list.push_back(thread); + // Add the request to the list. + request->Open(); + m_request_list.push_back(*request); // If we were empty, signal. if (was_empty) { @@ -213,6 +210,9 @@ Result KServerSession::OnRequest() { } } + // If we have a request event, this is asynchronous, and we don't need to wait. + R_SUCCEED_IF(request->GetEvent() != nullptr); + // This is a synchronous request, so we should wait for our request to complete. GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); GetCurrentThread(kernel).BeginWait(&wait_queue); @@ -223,32 +223,32 @@ Result KServerSession::OnRequest() { Result KServerSession::SendReply() { // Lock the session. - KScopedLightLock lk(m_lock); + KScopedLightLock lk{m_lock}; // Get the request. - KThread* client_thread; + KSessionRequest* request; { KScopedSchedulerLock sl{kernel}; // Get the current request. - client_thread = m_current_thread_request; - R_UNLESS(client_thread != nullptr, ResultInvalidState); + request = m_current_request; + R_UNLESS(request != nullptr, ResultInvalidState); // Clear the current request, since we're processing it. - m_current_thread_request = nullptr; - if (!m_thread_request_list.empty()) { + m_current_request = nullptr; + if (!m_request_list.empty()) { this->NotifyAvailable(); } } // Close reference to the request once we're done processing it. - SCOPE_EXIT({ client_thread->Close(); }); + SCOPE_EXIT({ request->Close(); }); // Extract relevant information from the request. - // const uintptr_t client_message = request->GetAddress(); - // const size_t client_buffer_size = request->GetSize(); - // KThread *client_thread = request->GetThread(); - // KEvent *event = request->GetEvent(); + const uintptr_t client_message = request->GetAddress(); + const size_t client_buffer_size = request->GetSize(); + KThread* client_thread = request->GetThread(); + KEvent* event = request->GetEvent(); // Check whether we're closed. const bool closed = (client_thread == nullptr || parent->IsClientClosed()); @@ -261,8 +261,8 @@ Result KServerSession::SendReply() { UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); - std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); + auto* dst_msg_buffer = memory.GetPointer(client_message); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); } else { result = ResultSessionClosed; } @@ -278,11 +278,30 @@ Result KServerSession::SendReply() { // If there's a client thread, update it. if (client_thread != nullptr) { - // End the client thread's wait. - KScopedSchedulerLock sl{kernel}; + if (event != nullptr) { + // // Get the client process/page table. + // KProcess *client_process = client_thread->GetOwnerProcess(); + // KPageTable *client_page_table = &client_process->PageTable(); - if (!client_thread->IsTerminationRequested()) { - client_thread->EndWait(client_result); + // // If we need to, reply with an async error. + // if (R_FAILED(client_result)) { + // ReplyAsyncError(client_process, client_message, client_buffer_size, + // client_result); + // } + + // // Unlock the client buffer. + // // NOTE: Nintendo does not check the result of this. + // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); + + // Signal the event. + event->Signal(); + } else { + // End the client thread's wait. + KScopedSchedulerLock sl{kernel}; + + if (!client_thread->IsTerminationRequested()) { + client_thread->EndWait(client_result); + } } } @@ -291,10 +310,10 @@ Result KServerSession::SendReply() { Result KServerSession::ReceiveRequest() { // Lock the session. - KScopedLightLock lk(m_lock); + KScopedLightLock lk{m_lock}; // Get the request and client thread. - // KSessionRequest *request; + KSessionRequest* request; KThread* client_thread; { @@ -304,35 +323,41 @@ Result KServerSession::ReceiveRequest() { R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed); // Ensure we aren't already servicing a request. - R_UNLESS(m_current_thread_request == nullptr, ResultNotFound); + R_UNLESS(m_current_request == nullptr, ResultNotFound); // Ensure we have a request to service. - R_UNLESS(!m_thread_request_list.empty(), ResultNotFound); + R_UNLESS(!m_request_list.empty(), ResultNotFound); // Pop the first request from the list. - client_thread = m_thread_request_list.front(); - m_thread_request_list.pop_front(); + request = &m_request_list.front(); + m_request_list.pop_front(); // Get the thread for the request. + client_thread = request->GetThread(); R_UNLESS(client_thread != nullptr, ResultSessionClosed); // Open the client thread. client_thread->Open(); } - // SCOPE_EXIT({ client_thread->Close(); }); + SCOPE_EXIT({ client_thread->Close(); }); // Set the request as our current. - m_current_thread_request = client_thread; + m_current_request = request; + + // Get the client address. + uintptr_t client_message = request->GetAddress(); + size_t client_buffer_size = request->GetSize(); + // bool recv_list_broken = false; // Receive the message. Core::Memory::Memory& memory{kernel.System().Memory()}; KThread* server_thread{GetCurrentThreadPointer(kernel)}; UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); + auto* src_msg_buffer = memory.GetPointer(client_message); auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); // We succeeded. return ResultSuccess; @@ -344,35 +369,34 @@ void KServerSession::CleanupRequests() { // Clean up any pending requests. while (true) { // Get the next request. - // KSessionRequest *request = nullptr; - KThread* client_thread = nullptr; + KSessionRequest* request = nullptr; { KScopedSchedulerLock sl{kernel}; - if (m_current_thread_request) { + if (m_current_request) { // Choose the current request if we have one. - client_thread = m_current_thread_request; - m_current_thread_request = nullptr; - } else if (!m_thread_request_list.empty()) { + request = m_current_request; + m_current_request = nullptr; + } else if (!m_request_list.empty()) { // Pop the request from the front of the list. - client_thread = m_thread_request_list.front(); - m_thread_request_list.pop_front(); + request = &m_request_list.front(); + m_request_list.pop_front(); } } // If there's no request, we're done. - if (client_thread == nullptr) { + if (request == nullptr) { break; } // Close a reference to the request once it's cleaned up. - SCOPE_EXIT({ client_thread->Close(); }); + SCOPE_EXIT({ request->Close(); }); // Extract relevant information from the request. // const uintptr_t client_message = request->GetAddress(); // const size_t client_buffer_size = request->GetSize(); - // KThread *client_thread = request->GetThread(); - // KEvent *event = request->GetEvent(); + KThread* client_thread = request->GetThread(); + KEvent* event = request->GetEvent(); // KProcess *server_process = request->GetServerProcess(); // KProcess *client_process = (client_thread != nullptr) ? @@ -385,11 +409,24 @@ void KServerSession::CleanupRequests() { // If there's a client thread, update it. if (client_thread != nullptr) { - // End the client thread's wait. - KScopedSchedulerLock sl{kernel}; + if (event != nullptr) { + // // We need to reply async. + // ReplyAsyncError(client_process, client_message, client_buffer_size, + // (R_SUCCEEDED(result) ? ResultSessionClosed : result)); - if (!client_thread->IsTerminationRequested()) { - client_thread->EndWait(ResultSessionClosed); + // // Unlock the client buffer. + // NOTE: Nintendo does not check the result of this. + // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); + + // Signal the event. + event->Signal(); + } else { + // End the client thread's wait. + KScopedSchedulerLock sl{kernel}; + + if (!client_thread->IsTerminationRequested()) { + client_thread->EndWait(ResultSessionClosed); + } } } } diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 748d52826c..c40ff4aaf7 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -12,6 +12,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/k_session_request.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" @@ -94,7 +95,7 @@ public: } /// TODO: flesh these out to match the real kernel - Result OnRequest(); + Result OnRequest(KSessionRequest* request); Result SendReply(); Result ReceiveRequest(); @@ -122,9 +123,8 @@ private: KSession* parent{}; /// List of threads which are pending a reply. - /// FIXME: KSessionRequest - std::list m_thread_request_list; - KThread* m_current_thread_request{}; + boost::intrusive::list m_request_list; + KSessionRequest* m_current_request; KLightLock m_lock; }; diff --git a/src/core/hle/kernel/k_session_request.cpp b/src/core/hle/kernel/k_session_request.cpp new file mode 100644 index 0000000000..520da6aa74 --- /dev/null +++ b/src/core/hle/kernel/k_session_request.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_page_buffer.h" +#include "core/hle/kernel/k_session_request.h" + +namespace Kernel { + +Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size, + KMemoryState state, size_t index) { + // At most 15 buffers of each type (4-bit descriptor counts). + ASSERT(index < ((1ul << 4) - 1) * 3); + + // Get the mapping. + Mapping* mapping; + if (index < NumStaticMappings) { + mapping = &m_static_mappings[index]; + } else { + // Allocate a page for the extra mappings. + if (m_mappings == nullptr) { + KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel); + R_UNLESS(page_buffer != nullptr, ResultOutOfMemory); + + m_mappings = reinterpret_cast(page_buffer); + } + + mapping = &m_mappings[index - NumStaticMappings]; + } + + // Set the mapping. + mapping->Set(client, server, size, state); + + return ResultSuccess; +} + +Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size, + KMemoryState state) { + ASSERT(m_num_recv == 0); + ASSERT(m_num_exch == 0); + return this->PushMap(client, server, size, state, m_num_send++); +} + +Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size, + KMemoryState state) { + ASSERT(m_num_exch == 0); + return this->PushMap(client, server, size, state, m_num_send + m_num_recv++); +} + +Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size, + KMemoryState state) { + return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++); +} + +void KSessionRequest::SessionMappings::Finalize() { + if (m_mappings) { + KPageBuffer::Free(kernel, reinterpret_cast(m_mappings)); + m_mappings = nullptr; + } +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h new file mode 100644 index 0000000000..fcf5215975 --- /dev/null +++ b/src/core/hle/kernel/k_session_request.h @@ -0,0 +1,307 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +class KSessionRequest final : public KSlabAllocated, + public KAutoObject, + public boost::intrusive::list_base_hook<> { + KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject); + +public: + class SessionMappings { + private: + static constexpr size_t NumStaticMappings = 8; + + class Mapping { + public: + constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) { + m_client_address = c; + m_server_address = s; + m_size = sz; + m_state = st; + } + + constexpr VAddr GetClientAddress() const { + return m_client_address; + } + constexpr VAddr GetServerAddress() const { + return m_server_address; + } + constexpr size_t GetSize() const { + return m_size; + } + constexpr KMemoryState GetMemoryState() const { + return m_state; + } + + private: + VAddr m_client_address; + VAddr m_server_address; + size_t m_size; + KMemoryState m_state; + }; + + public: + explicit SessionMappings(KernelCore& kernel_) + : kernel(kernel_), m_mappings(nullptr), m_num_send(), m_num_recv(), m_num_exch() {} + + void Initialize() {} + void Finalize(); + + size_t GetSendCount() const { + return m_num_send; + } + size_t GetReceiveCount() const { + return m_num_recv; + } + size_t GetExchangeCount() const { + return m_num_exch; + } + + Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state); + Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state); + Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state); + + VAddr GetSendClientAddress(size_t i) const { + return GetSendMapping(i).GetClientAddress(); + } + VAddr GetSendServerAddress(size_t i) const { + return GetSendMapping(i).GetServerAddress(); + } + size_t GetSendSize(size_t i) const { + return GetSendMapping(i).GetSize(); + } + KMemoryState GetSendMemoryState(size_t i) const { + return GetSendMapping(i).GetMemoryState(); + } + + VAddr GetReceiveClientAddress(size_t i) const { + return GetReceiveMapping(i).GetClientAddress(); + } + VAddr GetReceiveServerAddress(size_t i) const { + return GetReceiveMapping(i).GetServerAddress(); + } + size_t GetReceiveSize(size_t i) const { + return GetReceiveMapping(i).GetSize(); + } + KMemoryState GetReceiveMemoryState(size_t i) const { + return GetReceiveMapping(i).GetMemoryState(); + } + + VAddr GetExchangeClientAddress(size_t i) const { + return GetExchangeMapping(i).GetClientAddress(); + } + VAddr GetExchangeServerAddress(size_t i) const { + return GetExchangeMapping(i).GetServerAddress(); + } + size_t GetExchangeSize(size_t i) const { + return GetExchangeMapping(i).GetSize(); + } + KMemoryState GetExchangeMemoryState(size_t i) const { + return GetExchangeMapping(i).GetMemoryState(); + } + + private: + Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index); + + const Mapping& GetSendMapping(size_t i) const { + ASSERT(i < m_num_send); + + const size_t index = i; + if (index < NumStaticMappings) { + return m_static_mappings[index]; + } else { + return m_mappings[index - NumStaticMappings]; + } + } + + const Mapping& GetReceiveMapping(size_t i) const { + ASSERT(i < m_num_recv); + + const size_t index = m_num_send + i; + if (index < NumStaticMappings) { + return m_static_mappings[index]; + } else { + return m_mappings[index - NumStaticMappings]; + } + } + + const Mapping& GetExchangeMapping(size_t i) const { + ASSERT(i < m_num_exch); + + const size_t index = m_num_send + m_num_recv + i; + if (index < NumStaticMappings) { + return m_static_mappings[index]; + } else { + return m_mappings[index - NumStaticMappings]; + } + } + + private: + KernelCore& kernel; + Mapping m_static_mappings[NumStaticMappings]; + Mapping* m_mappings; + u8 m_num_send; + u8 m_num_recv; + u8 m_num_exch; + }; + +public: + explicit KSessionRequest(KernelCore& kernel_) + : KAutoObject(kernel_), m_mappings(kernel_), m_thread(nullptr), m_server(nullptr), + m_event(nullptr) {} + + static KSessionRequest* Create(KernelCore& kernel) { + KSessionRequest* req = KSessionRequest::Allocate(kernel); + if (req != nullptr) [[likely]] { + KAutoObject::Create(req); + } + return req; + } + + void Destroy() override { + this->Finalize(); + KSessionRequest::Free(kernel, this); + } + + void Initialize(KEvent* event, uintptr_t address, size_t size) { + m_mappings.Initialize(); + + m_thread = GetCurrentThreadPointer(kernel); + m_event = event; + m_address = address; + m_size = size; + + m_thread->Open(); + if (m_event != nullptr) { + m_event->Open(); + } + } + + static void PostDestroy(uintptr_t arg) {} + + KThread* GetThread() const { + return m_thread; + } + KEvent* GetEvent() const { + return m_event; + } + uintptr_t GetAddress() const { + return m_address; + } + size_t GetSize() const { + return m_size; + } + KProcess* GetServerProcess() const { + return m_server; + } + + void SetServerProcess(KProcess* process) { + m_server = process; + m_server->Open(); + } + + void ClearThread() { + m_thread = nullptr; + } + void ClearEvent() { + m_event = nullptr; + } + + size_t GetSendCount() const { + return m_mappings.GetSendCount(); + } + size_t GetReceiveCount() const { + return m_mappings.GetReceiveCount(); + } + size_t GetExchangeCount() const { + return m_mappings.GetExchangeCount(); + } + + Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) { + return m_mappings.PushSend(client, server, size, state); + } + + Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) { + return m_mappings.PushReceive(client, server, size, state); + } + + Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) { + return m_mappings.PushExchange(client, server, size, state); + } + + VAddr GetSendClientAddress(size_t i) const { + return m_mappings.GetSendClientAddress(i); + } + VAddr GetSendServerAddress(size_t i) const { + return m_mappings.GetSendServerAddress(i); + } + size_t GetSendSize(size_t i) const { + return m_mappings.GetSendSize(i); + } + KMemoryState GetSendMemoryState(size_t i) const { + return m_mappings.GetSendMemoryState(i); + } + + VAddr GetReceiveClientAddress(size_t i) const { + return m_mappings.GetReceiveClientAddress(i); + } + VAddr GetReceiveServerAddress(size_t i) const { + return m_mappings.GetReceiveServerAddress(i); + } + size_t GetReceiveSize(size_t i) const { + return m_mappings.GetReceiveSize(i); + } + KMemoryState GetReceiveMemoryState(size_t i) const { + return m_mappings.GetReceiveMemoryState(i); + } + + VAddr GetExchangeClientAddress(size_t i) const { + return m_mappings.GetExchangeClientAddress(i); + } + VAddr GetExchangeServerAddress(size_t i) const { + return m_mappings.GetExchangeServerAddress(i); + } + size_t GetExchangeSize(size_t i) const { + return m_mappings.GetExchangeSize(i); + } + KMemoryState GetExchangeMemoryState(size_t i) const { + return m_mappings.GetExchangeMemoryState(i); + } + +private: + // NOTE: This is public and virtual in Nintendo's kernel. + void Finalize() { + m_mappings.Finalize(); + + if (m_thread) { + m_thread->Close(); + } + if (m_event) { + m_event->Close(); + } + if (m_server) { + m_server->Close(); + } + } + +private: + SessionMappings m_mappings; + KThread* m_thread; + KProcess* m_server; + KEvent* m_event; + uintptr_t m_address; + size_t m_size; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h index e43db8515b..2bb6b6d088 100644 --- a/src/core/hle/kernel/k_shared_memory_info.h +++ b/src/core/hle/kernel/k_shared_memory_info.h @@ -15,7 +15,8 @@ class KSharedMemoryInfo final : public KSlabAllocated, public boost::intrusive::list_base_hook<> { public: - explicit KSharedMemoryInfo() = default; + explicit KSharedMemoryInfo(KernelCore&) {} + KSharedMemoryInfo() = default; constexpr void Initialize(KSharedMemory* shmem) { shared_memory = shmem; diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h index 0a7f226807..5d466ace7a 100644 --- a/src/core/hle/kernel/k_thread_local_page.h +++ b/src/core/hle/kernel/k_thread_local_page.h @@ -26,7 +26,7 @@ public: static_assert(RegionsPerPage > 0); public: - constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { + constexpr explicit KThreadLocalPage(KernelCore&, VAddr addr = {}) : m_virt_addr(addr) { m_is_region_free.fill(true); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 6eded95393..266be2bc4f 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -47,6 +47,7 @@ class KResourceLimit; class KScheduler; class KServerSession; class KSession; +class KSessionRequest; class KSharedMemory; class KSharedMemoryInfo; class KThread; @@ -360,6 +361,8 @@ public: return slab_heap_container->page_buffer; } else if constexpr (std::is_same_v) { return slab_heap_container->thread_local_page; + } else if constexpr (std::is_same_v) { + return slab_heap_container->session_request; } } @@ -422,6 +425,7 @@ private: KSlabHeap code_memory; KSlabHeap page_buffer; KSlabHeap thread_local_page; + KSlabHeap session_request; }; std::unique_ptr slab_heap_container; diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index 299a981a8a..06b51e9199 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h @@ -24,7 +24,7 @@ public: } static Derived* Allocate(KernelCore& kernel) { - return kernel.SlabHeap().Allocate(); + return kernel.SlabHeap().Allocate(kernel); } static void Free(KernelCore& kernel, Derived* obj) { From fca195b4fb1255c20579fd25d8565f0ae4759b6e Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 15 Oct 2022 21:57:40 -0400 Subject: [PATCH 185/245] kernel: remove most SessionRequestManager handling from KServerSession --- src/core/hle/ipc_helpers.h | 11 ++- src/core/hle/kernel/hle_ipc.cpp | 110 +++++++++++++++++++--- src/core/hle/kernel/hle_ipc.h | 9 ++ src/core/hle/kernel/k_server_session.cpp | 89 +---------------- src/core/hle/kernel/k_server_session.h | 33 ------- src/core/hle/service/sm/sm_controller.cpp | 5 +- 6 files changed, 119 insertions(+), 138 deletions(-) diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 0cc26a2118..aa27be7678 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -86,13 +86,13 @@ public: u32 num_domain_objects{}; const bool always_move_handles{ (static_cast(flags) & static_cast(Flags::AlwaysMoveHandles)) != 0}; - if (!ctx.Session()->IsDomain() || always_move_handles) { + if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { num_handles_to_move = num_objects_to_move; } else { num_domain_objects = num_objects_to_move; } - if (ctx.Session()->IsDomain()) { + if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { raw_data_size += static_cast(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); ctx.write_size += num_domain_objects; @@ -125,7 +125,8 @@ public: if (!ctx.IsTipc()) { AlignWithPadding(); - if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) { + if (ctx.Session()->GetSessionRequestManager()->IsDomain() && + ctx.HasDomainMessageHeader()) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); @@ -145,7 +146,7 @@ public: template void PushIpcInterface(std::shared_ptr iface) { - if (context->Session()->IsDomain()) { + if (context->Session()->GetSessionRequestManager()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( @@ -386,7 +387,7 @@ public: template std::weak_ptr PopIpcInterface() { - ASSERT(context->Session()->IsDomain()); + ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler(Pop() - 1); } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5b3feec662..e4f43a0537 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -19,6 +19,7 @@ #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/service_thread.h" #include "core/memory.h" namespace Kernel { @@ -56,16 +57,103 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co } } +Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session, + HLERequestContext& context) { + Result result = ResultSuccess; + + // If the session has been converted to a domain, handle the domain request + if (this->HasSessionRequestHandler(context)) { + if (IsDomain() && context.HasDomainMessageHeader()) { + result = HandleDomainSyncRequest(server_session, context); + // If there is no domain header, the regular session handler is used + } else if (this->HasSessionHandler()) { + // If this manager has an associated HLE handler, forward the request to it. + result = this->SessionHandler().HandleSyncRequest(*server_session, context); + } + } else { + ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); + IPC::ResponseBuilder rb(context, 2); + rb.Push(ResultSuccess); + } + + if (convert_to_domain) { + ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); + this->ConvertToDomain(); + convert_to_domain = false; + } + + return result; +} + +Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session, + HLERequestContext& context) { + if (!context.HasDomainMessageHeader()) { + return ResultSuccess; + } + + // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs + context.SetSessionRequestManager(server_session->GetSessionRequestManager()); + + // If there is a DomainMessageHeader, then this is CommandType "Request" + const auto& domain_message_header = context.GetDomainMessageHeader(); + const u32 object_id{domain_message_header.object_id}; + switch (domain_message_header.command) { + case IPC::DomainMessageHeader::CommandType::SendMessage: + if (object_id > this->DomainHandlerCount()) { + LOG_CRITICAL(IPC, + "object_id {} is too big! This probably means a recent service call " + "needed to return a new interface!", + object_id); + ASSERT(false); + return ResultSuccess; // Ignore error if asserts are off + } + if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) { + return strong_ptr->HandleSyncRequest(*server_session, context); + } else { + ASSERT(false); + return ResultSuccess; + } + + case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { + LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); + + this->CloseDomainHandler(object_id - 1); + + IPC::ResponseBuilder rb{context, 2}; + rb.Push(ResultSuccess); + return ResultSuccess; + } + } + + LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); + ASSERT(false); + return ResultSuccess; +} + +Result SessionRequestManager::QueueSyncRequest(KSession* parent, + std::shared_ptr&& context) { + // Ensure we have a session request handler + if (this->HasSessionRequestHandler(*context)) { + if (auto strong_ptr = this->GetServiceThread().lock()) { + strong_ptr->QueueSyncRequest(*parent, std::move(context)); + } else { + ASSERT_MSG(false, "strong_ptr is nullptr!"); + } + } else { + ASSERT_MSG(false, "handler is invalid!"); + } + + return ResultSuccess; +} + void SessionRequestHandler::ClientConnected(KServerSession* session) { - session->ClientConnected(shared_from_this()); + session->GetSessionRequestManager()->SetSessionHandler(shared_from_this()); // Ensure our server session is tracked globally. kernel.RegisterServerObject(session); } -void SessionRequestHandler::ClientDisconnected(KServerSession* session) { - session->ClientDisconnected(); -} +void SessionRequestHandler::ClientDisconnected(KServerSession* session) {} HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, KServerSession* server_session_, KThread* thread_) @@ -126,7 +214,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 // Padding to align to 16 bytes rp.AlignWithPadding(); - if (Session()->IsDomain() && + if (Session()->GetSessionRequestManager()->IsDomain() && ((command_header->type == IPC::CommandType::Request || command_header->type == IPC::CommandType::RequestWithContext) || !incoming)) { @@ -135,7 +223,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 if (incoming || domain_message_header) { domain_message_header = rp.PopRaw(); } else { - if (Session()->IsDomain()) { + if (Session()->GetSessionRequestManager()->IsDomain()) { LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } @@ -228,12 +316,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. - if (Session()->IsDomain()) { + if (server_session->GetSessionRequestManager()->IsDomain()) { current_offset = domain_offset - static_cast(outgoing_domain_objects.size()); - for (const auto& object : outgoing_domain_objects) { - server_session->AppendDomainHandler(object); - cmd_buf[current_offset++] = - static_cast(server_session->NumDomainRequestHandlers()); + for (auto& object : outgoing_domain_objects) { + server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); + cmd_buf[current_offset++] = static_cast( + server_session->GetSessionRequestManager()->DomainHandlerCount()); } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index e258e2cdfe..a0522bca0b 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -121,6 +121,10 @@ public: is_domain = true; } + void ConvertToDomainOnRequestEnd() { + convert_to_domain = true; + } + std::size_t DomainHandlerCount() const { return domain_handlers.size(); } @@ -164,7 +168,12 @@ public: bool HasSessionRequestHandler(const HLERequestContext& context) const; + Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); + Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); + Result QueueSyncRequest(KSession* parent, std::shared_ptr&& context); + private: + bool convert_to_domain{}; bool is_domain{}; SessionRequestHandlerPtr session_handler; std::vector domain_handlers; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 685a2a6e67..faf03fcc89 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -22,7 +22,6 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/service_thread.h" #include "core/memory.h" namespace Kernel { @@ -74,101 +73,17 @@ bool KServerSession::IsSignaled() const { return !m_request_list.empty() && m_current_request == nullptr; } -void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { - manager->AppendDomainHandler(std::move(handler)); -} - -std::size_t KServerSession::NumDomainRequestHandlers() const { - return manager->DomainHandlerCount(); -} - -Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { - if (!context.HasDomainMessageHeader()) { - return ResultSuccess; - } - - // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs - context.SetSessionRequestManager(manager); - - // If there is a DomainMessageHeader, then this is CommandType "Request" - const auto& domain_message_header = context.GetDomainMessageHeader(); - const u32 object_id{domain_message_header.object_id}; - switch (domain_message_header.command) { - case IPC::DomainMessageHeader::CommandType::SendMessage: - if (object_id > manager->DomainHandlerCount()) { - LOG_CRITICAL(IPC, - "object_id {} is too big! This probably means a recent service call " - "to {} needed to return a new interface!", - object_id, name); - ASSERT(false); - return ResultSuccess; // Ignore error if asserts are off - } - if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { - return strong_ptr->HandleSyncRequest(*this, context); - } else { - ASSERT(false); - return ResultSuccess; - } - - case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); - - manager->CloseDomainHandler(object_id - 1); - - IPC::ResponseBuilder rb{context, 2}; - rb.Push(ResultSuccess); - return ResultSuccess; - } - } - - LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); - ASSERT(false); - return ResultSuccess; -} - Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { u32* cmd_buf{reinterpret_cast(memory.GetPointer(thread->GetTLSAddress()))}; auto context = std::make_shared(kernel, memory, this, thread); context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); - // Ensure we have a session request handler - if (manager->HasSessionRequestHandler(*context)) { - if (auto strong_ptr = manager->GetServiceThread().lock()) { - strong_ptr->QueueSyncRequest(*parent, std::move(context)); - } else { - ASSERT_MSG(false, "strong_ptr is nullptr!"); - } - } else { - ASSERT_MSG(false, "handler is invalid!"); - } - - return ResultSuccess; + return manager->QueueSyncRequest(parent, std::move(context)); } Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { - Result result = ResultSuccess; - - // If the session has been converted to a domain, handle the domain request - if (manager->HasSessionRequestHandler(context)) { - if (IsDomain() && context.HasDomainMessageHeader()) { - result = HandleDomainSyncRequest(context); - // If there is no domain header, the regular session handler is used - } else if (manager->HasSessionHandler()) { - // If this ServerSession has an associated HLE handler, forward the request to it. - result = manager->SessionHandler().HandleSyncRequest(*this, context); - } - } else { - ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); - IPC::ResponseBuilder rb(context, 2); - rb.Push(ResultSuccess); - } - - if (convert_to_domain) { - ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); - manager->ConvertToDomain(); - convert_to_domain = false; - } + Result result = manager->CompleteSyncRequest(this, context); // The calling thread is waiting for this request to complete, so wake it up. context.GetThread().EndWait(result); diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index c40ff4aaf7..32135473b7 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -58,37 +58,8 @@ public: } bool IsSignaled() const override; - void OnClientClosed(); - void ClientConnected(SessionRequestHandlerPtr handler) { - if (manager) { - manager->SetSessionHandler(std::move(handler)); - } - } - - void ClientDisconnected() { - manager = nullptr; - } - - /// Adds a new domain request handler to the collection of request handlers within - /// this ServerSession instance. - void AppendDomainHandler(SessionRequestHandlerPtr handler); - - /// Retrieves the total number of domain request handlers that have been - /// appended to this ServerSession instance. - std::size_t NumDomainRequestHandlers() const; - - /// Returns true if the session has been converted to a domain, otherwise False - bool IsDomain() const { - return manager && manager->IsDomain(); - } - - /// Converts the session to a domain at the end of the current command - void ConvertToDomain() { - convert_to_domain = true; - } - /// Gets the session request manager, which forwards requests to the underlying service std::shared_ptr& GetSessionRequestManager() { return manager; @@ -109,10 +80,6 @@ private: /// Completes a sync request from the emulated application. Result CompleteSyncRequest(HLERequestContext& context); - /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an - /// object handle. - Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); - /// This session's HLE request handlers; if nullptr, this is not an HLE server std::shared_ptr manager; diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 2a4bd64ab2..273f795684 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -15,9 +15,10 @@ namespace Service::SM { void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { - ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); + ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), + "Session is already a domain"); LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); - ctx.Session()->ConvertToDomain(); + ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); From 9524e28d206f32898df9e3a02ed67ba1b5c5b20b Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 19 Oct 2022 18:45:49 -0400 Subject: [PATCH 186/245] video_core: don't build ASTC decoder shader unless requested --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 6 ++---- src/video_core/renderer_vulkan/vk_rasterizer.h | 1 - .../renderer_vulkan/vk_texture_cache.cpp | 15 ++++++++++----- src/video_core/renderer_vulkan/vk_texture_cache.h | 11 +++++++---- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 47dfb45a1d..6d5ea99dde 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -157,12 +157,10 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), update_descriptor_queue(device, scheduler), blit_image(device, scheduler, state_tracker, descriptor_pool), - astc_decoder_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue, - memory_allocator), render_pass_cache(device), texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, - blit_image, astc_decoder_pass, - render_pass_cache}, + blit_image, render_pass_cache, + descriptor_pool, update_descriptor_queue}, texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, update_descriptor_queue, descriptor_pool), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 4cde3c983a..e7cdeaed1f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -153,7 +153,6 @@ private: DescriptorPool descriptor_pool; UpdateDescriptorQueue update_descriptor_queue; BlitImageHelper blit_image; - ASTCDecoderPass astc_decoder_pass; RenderPassCache render_pass_cache; TextureCacheRuntime texture_cache_runtime; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 305ad8aeee..20e56b0d11 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -791,12 +791,17 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched MemoryAllocator& memory_allocator_, StagingBufferPool& staging_buffer_pool_, BlitImageHelper& blit_image_helper_, - ASTCDecoderPass& astc_decoder_pass_, - RenderPassCache& render_pass_cache_) + RenderPassCache& render_pass_cache_, + DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue) : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_}, staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_}, - astc_decoder_pass{astc_decoder_pass_}, render_pass_cache{render_pass_cache_}, - resolution{Settings::values.resolution_info} {} + render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} { + if (Settings::values.accelerate_astc) { + astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, + update_descriptor_queue, memory_allocator); + } +} void TextureCacheRuntime::Finish() { scheduler.Finish(); @@ -1845,7 +1850,7 @@ void TextureCacheRuntime::AccelerateImageUpload( Image& image, const StagingBufferRef& map, std::span swizzles) { if (IsPixelFormatASTC(image.info.format)) { - return astc_decoder_pass.Assemble(image, map, swizzles); + return astc_decoder_pass->Assemble(image, map, swizzles); } ASSERT(false); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 0b7ac0df16..7ec0df1340 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -6,6 +6,7 @@ #include #include "shader_recompiler/shader_info.h" +#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/texture_cache/image_view_base.h" #include "video_core/texture_cache/texture_cache_base.h" @@ -25,14 +26,15 @@ using VideoCommon::RenderTargets; using VideoCommon::SlotVector; using VideoCore::Surface::PixelFormat; -class ASTCDecoderPass; class BlitImageHelper; +class DescriptorPool; class Device; class Image; class ImageView; class Framebuffer; class RenderPassCache; class StagingBufferPool; +class UpdateDescriptorQueue; class Scheduler; class TextureCacheRuntime { @@ -41,8 +43,9 @@ public: MemoryAllocator& memory_allocator_, StagingBufferPool& staging_buffer_pool_, BlitImageHelper& blit_image_helper_, - ASTCDecoderPass& astc_decoder_pass_, - RenderPassCache& render_pass_cache_); + RenderPassCache& render_pass_cache_, + DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue); void Finish(); @@ -97,8 +100,8 @@ public: MemoryAllocator& memory_allocator; StagingBufferPool& staging_buffer_pool; BlitImageHelper& blit_image_helper; - ASTCDecoderPass& astc_decoder_pass; RenderPassCache& render_pass_cache; + std::optional astc_decoder_pass; const Settings::ResolutionScalingInfo& resolution; constexpr static size_t indexing_slots = 8 * sizeof(size_t); From d4c0b7b43739e67af1dddf5ec4210f6af809628e Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Thu, 20 Oct 2022 06:55:23 -0700 Subject: [PATCH 187/245] Controller Applet had instance of Undocked, make Handheld Remember that time we renamed the Undocked option to Handheld in the status bar, and then later remembered the Controller Configuration? Scrolling through Transifex I noticed that we still have one instance of "Undocked" in the text. --- src/yuzu/applets/qt_controller.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui index c8cb6bcf38..f5eccba70b 100644 --- a/src/yuzu/applets/qt_controller.ui +++ b/src/yuzu/applets/qt_controller.ui @@ -2300,7 +2300,7 @@ - Undocked + Handheld From 0b181eeef4f56a814ea6c9b86d789be3ab8d1ae5 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 21 Oct 2022 00:05:39 -0400 Subject: [PATCH 188/245] hid/npad: Fix copy size in GetSupportedNpadIdTypes Previously this was passing the size of the vector into memcpy rather than the size in bytes to copy, which would result in a partial read. Thankfully, this function isn't used yet, so this gets rid of a bug before it's able to do anything. --- src/core/hle/service/hid/controllers/npad.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 98e4f2af7f..ba8a1f7866 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -745,8 +745,9 @@ void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { - ASSERT(max_length < supported_npad_id_types.size()); - std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); + const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); + ASSERT(max_length <= copy_amount); + std::memcpy(data, supported_npad_id_types.data(), copy_amount); } std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { From 7f66050f0c383a5c7d82c5c58098f819d4e1e0bc Mon Sep 17 00:00:00 2001 From: german77 Date: Fri, 21 Oct 2022 00:23:12 -0500 Subject: [PATCH 189/245] input_common: cache vibration tests --- src/common/input.h | 5 +- src/core/hid/emulated_controller.cpp | 45 +++---------- src/core/hid/emulated_controller.h | 5 +- src/core/hle/service/hid/controllers/npad.cpp | 4 +- src/input_common/drivers/gc_adapter.cpp | 6 +- src/input_common/drivers/gc_adapter.h | 4 +- src/input_common/drivers/sdl_driver.cpp | 64 ++++++++++++++++--- src/input_common/drivers/sdl_driver.h | 4 +- src/input_common/input_engine.h | 7 +- src/input_common/input_poller.cpp | 6 +- 10 files changed, 93 insertions(+), 57 deletions(-) diff --git a/src/common/input.h b/src/common/input.h index b533f3844c..cb30b72547 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -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; } diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 025f1c78e4..06c2081a98 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -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) { @@ -1234,12 +1211,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; diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 319226bf82..d004ca56ad 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -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 diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 98e4f2af7f..b85831de10 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -867,7 +867,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. @@ -1000,7 +1000,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) { diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index f4dd24e7df..826fa21097 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -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 diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 8682da8470..7f81767f77 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h @@ -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 GetInputDevices() const override; ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index b72e4b3975..ddbe8a8966 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -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 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(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(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; diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fc3a445724..d1b4471cf7 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -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; diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index cfbdb26bd7..d4c264a8eb 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -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, diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ca33fb4eb2..fff9731cee 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -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 { From 3968faec06a24dfc97f0042591e8adc18823a8d8 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 21 Oct 2022 01:52:10 -0400 Subject: [PATCH 190/245] k_session_request: Simplify constructor initialization --- src/core/hle/kernel/k_session_request.h | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h index fcf5215975..5a43933cff 100644 --- a/src/core/hle/kernel/k_session_request.h +++ b/src/core/hle/kernel/k_session_request.h @@ -52,8 +52,7 @@ public: }; public: - explicit SessionMappings(KernelCore& kernel_) - : kernel(kernel_), m_mappings(nullptr), m_num_send(), m_num_recv(), m_num_exch() {} + explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {} void Initialize() {} void Finalize(); @@ -150,16 +149,14 @@ public: private: KernelCore& kernel; Mapping m_static_mappings[NumStaticMappings]; - Mapping* m_mappings; - u8 m_num_send; - u8 m_num_recv; - u8 m_num_exch; + Mapping* m_mappings{}; + u8 m_num_send{}; + u8 m_num_recv{}; + u8 m_num_exch{}; }; public: - explicit KSessionRequest(KernelCore& kernel_) - : KAutoObject(kernel_), m_mappings(kernel_), m_thread(nullptr), m_server(nullptr), - m_event(nullptr) {} + explicit KSessionRequest(KernelCore& kernel_) : KAutoObject(kernel_), m_mappings(kernel_) {} static KSessionRequest* Create(KernelCore& kernel) { KSessionRequest* req = KSessionRequest::Allocate(kernel); @@ -297,11 +294,11 @@ private: private: SessionMappings m_mappings; - KThread* m_thread; - KProcess* m_server; - KEvent* m_event; - uintptr_t m_address; - size_t m_size; + KThread* m_thread{}; + KProcess* m_server{}; + KEvent* m_event{}; + uintptr_t m_address{}; + size_t m_size{}; }; } // namespace Kernel From 969387a79ab5888c39c5ac8bd298205e700819f8 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 21 Oct 2022 01:54:31 -0400 Subject: [PATCH 191/245] k_session_request: Turn C-style array into std::array Makes for stronger typing and allows tooling bounds checks provided by the standard library for debugging purposes. --- src/core/hle/kernel/k_session_request.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h index 5a43933cff..42b1207f08 100644 --- a/src/core/hle/kernel/k_session_request.h +++ b/src/core/hle/kernel/k_session_request.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_memory_block.h" @@ -148,7 +150,7 @@ public: private: KernelCore& kernel; - Mapping m_static_mappings[NumStaticMappings]; + std::array m_static_mappings; Mapping* m_mappings{}; u8 m_num_send{}; u8 m_num_recv{}; From f16db300c6117da0286a5504cd3605e6aceaf40f Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 01:54:44 -0400 Subject: [PATCH 192/245] format_lookup_table: Implement R32_B24G8 with D32_FLOAT_S8_UINT This format is similar to Z32_FLOAT_X24S8_UINT, which is implemented with D32_FLOAT_S8_UINT. Used in Persona 5 Royal --- src/video_core/texture_cache/format_lookup_table.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index ad935d3860..08aa8ca339 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -150,6 +150,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::D24_UNORM_S8_UINT; case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): return PixelFormat::D32_FLOAT_S8_UINT; + case Hash(TextureFormat::R32_B24G8, FLOAT, UINT, UNORM, UNORM, LINEAR): + return PixelFormat::D32_FLOAT_S8_UINT; case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): return PixelFormat::BC1_RGBA_UNORM; case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB): From 93a7058d8ef62e978a5fc73c1c31f60c33866ee1 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 21 Oct 2022 01:56:14 -0400 Subject: [PATCH 193/245] k_session_request: Add missing override specifier --- src/core/hle/kernel/k_session_request.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h index 42b1207f08..e5558bc2c9 100644 --- a/src/core/hle/kernel/k_session_request.h +++ b/src/core/hle/kernel/k_session_request.h @@ -280,7 +280,7 @@ public: private: // NOTE: This is public and virtual in Nintendo's kernel. - void Finalize() { + void Finalize() override { m_mappings.Finalize(); if (m_thread) { From 1f54cd4ac78dd1af48490dcc404bec4adf2876f3 Mon Sep 17 00:00:00 2001 From: FengChen Date: Fri, 21 Oct 2022 15:38:50 +0800 Subject: [PATCH 194/245] video_coare: Reimplementing the maxwell drawing trigger mechanism --- src/video_core/engines/maxwell_3d.cpp | 257 ++++++++---------- src/video_core/engines/maxwell_3d.h | 33 +-- src/video_core/macro/macro_hle.cpp | 47 ++-- src/video_core/macro/macro_interpreter.cpp | 2 +- src/video_core/macro/macro_jit_x64.cpp | 2 +- src/video_core/rasterizer_interface.h | 2 +- .../renderer_opengl/gl_rasterizer.cpp | 5 +- .../renderer_opengl/gl_rasterizer.h | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 11 +- .../renderer_vulkan/vk_rasterizer.h | 2 +- 10 files changed, 139 insertions(+), 224 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 89a9d1f5ac..b41aa6fc1c 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -117,10 +117,15 @@ 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; } void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { @@ -208,25 +213,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 +247,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector& 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 +268,16 @@ 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); + } 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 +318,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 +347,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); } @@ -576,42 +484,6 @@ void Maxwell3D::ProcessSyncPoint() { } } -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 Maxwell3D::GetQueryResult() { switch (regs.report_semaphore.query.report) { case Regs::ReportSemaphore::Report::Payload: @@ -694,4 +566,87 @@ void Maxwell3D::ProcessClearBuffers() { rasterizer->Clear(); } +void Maxwell3D::ProcessDeferredDraw() { + auto method_count = deferred_draw_method.size(); + if (method_count) { + 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 (method_count < 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(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; + } + } + + 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(); + } +} + } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 75e3b868dc..1472e88717 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -3048,8 +3048,6 @@ public: }; std::array shader_stages; - - u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering. }; State state{}; @@ -3064,11 +3062,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 +3074,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::max()>; using Table = std::array; @@ -3164,14 +3142,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 GetQueryResult(); @@ -3184,8 +3158,6 @@ private: /// Start offsets of each macro in macro_memory std::array macro_positions{}; - std::array 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 +3170,9 @@ private: bool execute_on{true}; bool use_topology_override{false}; + + std::array draw_command{}; + std::vector deferred_draw_method; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 8a8adbb42f..f896591bfe 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -22,35 +22,29 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector& maxwell3d.regs.draw.topology.Assign( static_cast(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& 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(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& parameters) { @@ -63,24 +57,21 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& 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(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& 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& 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; } } diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index f670b1bca8..c0d32c112c 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp @@ -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()); diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index a302a9603d..25c1ce798c 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -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) { diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index d2d40884c2..1cbfef0902 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -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; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e5c09a969b..21bac6ebf2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -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(); }); @@ -228,8 +228,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { BeginTransformFeedback(pipeline, primitive_mode); const GLuint base_instance = static_cast(maxwell3d->regs.global_base_instance_index); - const GLsizei num_instances = - static_cast(is_instanced ? maxwell3d->mme_draw.instance_count : 1); + const GLsizei num_instances = static_cast(instance_count); if (is_indexed) { const GLint base_vertex = static_cast(maxwell3d->regs.global_base_vertex_index); const GLsizei num_vertices = static_cast(maxwell3d->regs.index_buffer.count); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 45131b7852..c93ba3b42b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -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; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 47dfb45a1d..9a7d90b2a4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -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(); }); @@ -199,8 +198,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { 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, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 4cde3c983a..b3a182588d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -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; From 82fdfb33ac7cc6da4f24c5a8bd88855287e93fcc Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 21 Oct 2022 21:28:08 -0500 Subject: [PATCH 195/245] service: nfp: remove unnecessary include --- src/core/hle/service/nfp/nfp_device.cpp | 1 + src/core/hle/service/nfp/nfp_device.h | 1 - src/core/hle/service/nfp/nfp_types.h | 5 ----- src/core/hle/service/nfp/nfp_user.cpp | 3 --- src/core/hle/service/nfp/nfp_user.h | 8 ++++++-- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 76f8a267a1..4e6b8e558d 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -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" diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index a5b72cf193..76d0e9ae46 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h @@ -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" diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index c09f9ddb6a..63d5917cb0 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -17,11 +17,6 @@ enum class ServiceType : u32 { System, }; -enum class State : u32 { - NonInitialized, - Initialized, -}; - enum class DeviceState : u32 { Initialized, SearchingForTag, diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 4ed53b5344..33e2ef5183 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -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" diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 68c60ae82e..47aff3695a 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -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); From 3e0aaeba98e3278b26f1d6be5dd013a953ff784f Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 21 Oct 2022 22:20:27 -0500 Subject: [PATCH 196/245] service: nfp: Allow amiibos without keys --- src/core/hle/service/nfp/amiibo_crypto.cpp | 8 +++++++- src/core/hle/service/nfp/amiibo_crypto.h | 3 +++ src/core/hle/service/nfp/nfp_device.cpp | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index c32a6816b7..167e295720 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -9,6 +9,7 @@ #include #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{}; diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index 0175ced919..1fa61174ed 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -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); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 4e6b8e558d..b196725601 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -234,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; From 2f90694797e30088820937855acf613bdcf27247 Mon Sep 17 00:00:00 2001 From: FengChen Date: Fri, 21 Oct 2022 19:14:22 +0800 Subject: [PATCH 197/245] video_core: Implement maxwell inline_index method --- src/video_core/engines/maxwell_3d.cpp | 170 ++++++++++-------- src/video_core/engines/maxwell_3d.h | 13 +- .../renderer_opengl/gl_rasterizer.cpp | 12 ++ .../renderer_opengl/gl_rasterizer.h | 2 + .../renderer_vulkan/vk_rasterizer.cpp | 15 ++ .../renderer_vulkan/vk_rasterizer.h | 2 + 6 files changed, 135 insertions(+), 79 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index b41aa6fc1c..25fcdb1e35 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -126,6 +126,9 @@ void Maxwell3D::InitializeRegisterDefaults() { 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) { @@ -271,6 +274,23 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool 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(argument & 0x000000ff)); + inline_index_draw_indexes.push_back(static_cast((argument & 0x0000ff00) >> 8)); + inline_index_draw_indexes.push_back(static_cast((argument & 0x00ff0000) >> 16)); + inline_index_draw_indexes.push_back(static_cast((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(); @@ -567,86 +587,94 @@ void Maxwell3D::ProcessClearBuffers() { } void Maxwell3D::ProcessDeferredDraw() { - auto method_count = deferred_draw_method.size(); - if (method_count) { - enum class DrawMode { - Undefined, - General, - Instance, - }; - DrawMode draw_mode{DrawMode::Undefined}; - u32 instance_count = 1; + if (deferred_draw_method.empty()) { + return; + } - 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 (method_count < 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; - } + enum class DrawMode { + Undefined, + General, + Instance, + }; + DrawMode draw_mode{DrawMode::Undefined}; + u32 instance_count = 1; - // Drawing will only begin with draw.begin or index_buffer method, other methods directly - // clear - if (draw_mode == DrawMode::Undefined) { - deferred_draw_method.clear(); + 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; + } - if (draw_mode == DrawMode::Instance) { - ASSERT_MSG(deferred_draw_method.size() % 4 == 0, "Instance mode method size error"); - instance_count = static_cast(deferred_draw_method.size()) / 4; + // 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(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 { - 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; + 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(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(); } + + 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 diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 1472e88717..bd23ebc12c 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -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 @@ -3083,6 +3079,8 @@ public: Tables tables{}; } dirty; + std::vector inline_index_draw_indexes; + private: void InitializeRegisterDefaults(); @@ -3377,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); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 21bac6ebf2..1590b21dee 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -222,6 +222,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); + BindInlineIndexBuffer(); + SyncState(); const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); @@ -1128,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(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) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index c93ba3b42b..793e0d608d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -199,6 +199,8 @@ private: /// End a transform feedback void EndTransformFeedback(); + void BindInlineIndexBuffer(); + Tegra::GPU& gpu; const Device& device; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9a7d90b2a4..9f05a7a18c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -193,6 +193,8 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); + BindInlineIndexBuffer(); + BeginTransformFeedback(); UpdateDynamicStates(); @@ -1008,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(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(buffer.offset), data_count); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b3a182588d..e2fdc7611d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -141,6 +141,8 @@ private: void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); + void BindInlineIndexBuffer(); + Tegra::GPU& gpu; ScreenInfo& screen_info; From 496695618a14d8e850fdd38c1b4e5159ee674bf5 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:05 -0400 Subject: [PATCH 198/245] CMakeLists: Treat -Wall and -Wextra as errors --- src/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3575a3cb3e..3b48627373 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,8 +99,9 @@ if (MSVC) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) else() add_compile_options( - -Wall + -Werror=all -Werror=array-bounds + -Werror=extra -Werror=implicit-fallthrough -Werror=missing-declarations -Werror=missing-field-initializers @@ -112,8 +113,7 @@ else() -Werror=unused-function -Werror=unused-result -Werror=unused-variable - -Wextra - -Wmissing-declarations + -Wno-attributes -Wno-invalid-offsetof -Wno-unused-parameter From 91c410c91833c3c4a466ab1ff6ca8ba74c0529cf Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:05 -0400 Subject: [PATCH 199/245] CMakeLists: Consolidate all unused warnings into -Wunused --- src/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b48627373..f774c2791e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -110,13 +110,13 @@ else() -Werror=sign-compare -Werror=switch -Werror=uninitialized - -Werror=unused-function - -Werror=unused-result - -Werror=unused-variable + -Werror=unused -Wno-attributes -Wno-invalid-offsetof -Wno-unused-parameter + + $<$:-Wno-unused-private-field> ) if (ARCHITECTURE_x86_64) From 93297d14d8fc5c8f73e8ccba5ca989590a453c05 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:06 -0400 Subject: [PATCH 200/245] CMakeLists: Remove all redundant warnings These are already explicitly or implicitly set in src/CMakeLists.txt --- src/CMakeLists.txt | 7 ------- src/audio_core/CMakeLists.txt | 10 ---------- src/common/CMakeLists.txt | 2 -- src/core/CMakeLists.txt | 9 ++------- src/input_common/CMakeLists.txt | 5 ----- src/shader_recompiler/CMakeLists.txt | 10 ++-------- src/video_core/CMakeLists.txt | 8 +------- 7 files changed, 5 insertions(+), 46 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f774c2791e..71853eaad2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,16 +100,9 @@ if (MSVC) else() add_compile_options( -Werror=all - -Werror=array-bounds -Werror=extra - -Werror=implicit-fallthrough -Werror=missing-declarations - -Werror=missing-field-initializers - -Werror=reorder -Werror=shadow - -Werror=sign-compare - -Werror=switch - -Werror=uninitialized -Werror=unused -Wno-attributes diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 144f1bab29..01b4ffd888 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -206,20 +206,10 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4456 # Declaration of 'identifier' hides previous local declaration - /we4457 # Declaration of 'identifier' hides function parameter - /we4458 # Declaration of 'identifier' hides class member - /we4459 # Declaration of 'identifier' hides global declaration ) else() target_compile_options(audio_core PRIVATE -Werror=conversion - -Werror=ignored-qualifiers - -Werror=shadow - -Werror=unused-variable - - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> -Wno-sign-conversion ) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 46cf75fdeb..6da547a3fa 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -160,8 +160,6 @@ if (MSVC) ) else() target_compile_options(common PRIVATE - -Werror - $<$:-fsized-deallocation> ) endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 055bea6419..72da9cb56d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -778,15 +778,10 @@ if (MSVC) else() target_compile_options(core PRIVATE -Werror=conversion - -Werror=ignored-qualifiers - - $<$:-Werror=class-memaccess> - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> - - $<$:-fsized-deallocation> -Wno-sign-conversion + + $<$:-fsized-deallocation> ) endif() diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 2cf9eb97f4..7935ac655f 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -48,12 +48,7 @@ if (MSVC) ) else() target_compile_options(input_common PRIVATE - -Werror -Werror=conversion - -Werror=ignored-qualifiers - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> - -Werror=unused-variable ) endif() diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index af8e51fe82..cbc1daf772 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -242,23 +242,17 @@ if (MSVC) target_compile_options(shader_recompiler PRIVATE /W4 /WX - /we4018 # 'expression' : signed/unsigned mismatch + + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data - /we4305 # 'context' : truncation from 'type1' to 'type2' /we4800 # Implicit conversion from 'type' to bool. Possible information loss /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. ) else() target_compile_options(shader_recompiler PRIVATE - -Werror -Werror=conversion - -Werror=ignored-qualifiers - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> - -Werror=unused-variable # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # And this in turns limits the size of a std::array. diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index cb8b46edf0..1069919691 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -279,14 +279,8 @@ if (MSVC) else() target_compile_options(video_core PRIVATE -Werror=conversion - -Wno-error=sign-conversion - -Werror=pessimizing-move - -Werror=redundant-move - -Werror=type-limits - $<$:-Werror=class-memaccess> - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> + -Wno-sign-conversion ) endif() From e6ab1f673b88b1af6bd966886249c7824ec5dbd4 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:06 -0400 Subject: [PATCH 201/245] general: Enforce C4800 everywhere except in video_core --- src/CMakeLists.txt | 1 + src/audio_core/CMakeLists.txt | 1 + src/common/CMakeLists.txt | 6 +++++ src/common/bit_field.h | 15 +++++++++---- src/core/CMakeLists.txt | 1 + src/core/file_sys/program_metadata.cpp | 2 +- src/core/hid/emulated_controller.cpp | 22 +++++++++---------- src/core/hle/kernel/svc.cpp | 4 ++-- src/core/hle/service/am/applets/applets.h | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 20 ++++++++--------- src/input_common/CMakeLists.txt | 1 + src/input_common/drivers/sdl_driver.cpp | 4 ++-- src/input_common/input_poller.cpp | 18 +++++++-------- src/shader_recompiler/CMakeLists.txt | 1 - 14 files changed, 57 insertions(+), 41 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71853eaad2..cfd80886cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -76,6 +76,7 @@ if (MSVC) /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? /we4555 # Expression has no effect; expected expression with side-effect /we4715 # 'function': not all control paths return a value + /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. /we4834 # Discarding return value of function with 'nodiscard' attribute /we5038 # data member 'member1' will be initialized after data member 'member2' /we5245 # 'function': unreferenced function with internal linkage has been removed diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 01b4ffd888..0a1f3bf187 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -206,6 +206,7 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(audio_core PRIVATE diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 6da547a3fa..043f27fb1c 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -157,6 +157,12 @@ if (MSVC) target_compile_options(common PRIVATE /W4 /WX + + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data + /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch + /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(common PRIVATE diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 7e1df62b1c..e4e58ea450 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -141,10 +141,6 @@ public: constexpr BitField(BitField&&) noexcept = default; constexpr BitField& operator=(BitField&&) noexcept = default; - [[nodiscard]] constexpr operator T() const { - return Value(); - } - constexpr void Assign(const T& value) { #ifdef _MSC_VER storage = static_cast((storage & ~mask) | FormatValue(value)); @@ -162,6 +158,17 @@ public: return ExtractValue(storage); } + template + [[nodiscard]] constexpr ConvertedToType As() const { + static_assert(!std::is_same_v, + "Unnecessary cast. Use Value() instead."); + return static_cast(Value()); + } + + [[nodiscard]] constexpr operator T() const { + return Value(); + } + [[nodiscard]] constexpr explicit operator bool() const { return Value() != 0; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 72da9cb56d..113e663b5f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -774,6 +774,7 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(core PRIVATE diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 08d489eab6..f00479bd35 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -127,7 +127,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address } bool ProgramMetadata::Is64BitProgram() const { - return npdm_header.has_64_bit_instructions; + return npdm_header.has_64_bit_instructions.As(); } ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 025f1c78e4..57eff72fec 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1158,27 +1158,27 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { case NpadStyleIndex::ProController: - return supported_style_tag.fullkey; + return supported_style_tag.fullkey.As(); case NpadStyleIndex::Handheld: - return supported_style_tag.handheld; + return supported_style_tag.handheld.As(); case NpadStyleIndex::JoyconDual: - return supported_style_tag.joycon_dual; + return supported_style_tag.joycon_dual.As(); case NpadStyleIndex::JoyconLeft: - return supported_style_tag.joycon_left; + return supported_style_tag.joycon_left.As(); case NpadStyleIndex::JoyconRight: - return supported_style_tag.joycon_right; + return supported_style_tag.joycon_right.As(); case NpadStyleIndex::GameCube: - return supported_style_tag.gamecube; + return supported_style_tag.gamecube.As(); case NpadStyleIndex::Pokeball: - return supported_style_tag.palma; + return supported_style_tag.palma.As(); case NpadStyleIndex::NES: - return supported_style_tag.lark; + return supported_style_tag.lark.As(); case NpadStyleIndex::SNES: - return supported_style_tag.lucia; + return supported_style_tag.lucia.As(); case NpadStyleIndex::N64: - return supported_style_tag.lagoon; + return supported_style_tag.lagoon.As(); case NpadStyleIndex::SegaGenesis: - return supported_style_tag.lager; + return supported_style_tag.lager.As(); default: return false; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index b07ae3f027..4aca5b27dd 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -751,8 +751,8 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { } system.GetReporter().SaveSvcBreakReport( - static_cast(break_reason.break_type.Value()), break_reason.signal_debugger, info1, - info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); + static_cast(break_reason.break_type.Value()), break_reason.signal_debugger.As(), + info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); if (!break_reason.signal_debugger) { LOG_CRITICAL( diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index e78a576576..12c6a5b1a4 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -164,7 +164,7 @@ protected: u32_le size; u32_le library_version; u32_le theme_color; - u8 play_startup_sound; + bool play_startup_sound; u64_le system_tick; }; static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index ba8a1f7866..3b26e96dea 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -1502,25 +1502,25 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller Core::HID::NpadStyleTag style = GetSupportedStyleSet(); switch (controller) { case Core::HID::NpadStyleIndex::ProController: - return style.fullkey; + return style.fullkey.As(); case Core::HID::NpadStyleIndex::JoyconDual: - return style.joycon_dual; + return style.joycon_dual.As(); case Core::HID::NpadStyleIndex::JoyconLeft: - return style.joycon_left; + return style.joycon_left.As(); case Core::HID::NpadStyleIndex::JoyconRight: - return style.joycon_right; + return style.joycon_right.As(); case Core::HID::NpadStyleIndex::GameCube: - return style.gamecube; + return style.gamecube.As(); case Core::HID::NpadStyleIndex::Pokeball: - return style.palma; + return style.palma.As(); case Core::HID::NpadStyleIndex::NES: - return style.lark; + return style.lark.As(); case Core::HID::NpadStyleIndex::SNES: - return style.lucia; + return style.lucia.As(); case Core::HID::NpadStyleIndex::N64: - return style.lagoon; + return style.lagoon.As(); case Core::HID::NpadStyleIndex::SegaGenesis: - return style.lager; + return style.lager.As(); default: return false; } diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7935ac655f..d2730bdc16 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -45,6 +45,7 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(input_common PRIVATE diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index b72e4b3975..c175a88538 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -40,8 +40,8 @@ public: void EnableMotion() { if (sdl_controller) { SDL_GameController* controller = sdl_controller.get(); - has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); - has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); + has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; + has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; if (has_accel) { SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); } diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ca33fb4eb2..ccc3076ca4 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -797,8 +797,8 @@ std::unique_ptr InputFactory::CreateButtonDevice( const auto button_id = params.Get("button", 0); const auto keyboard_key = params.Get("code", 0); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; input_engine->PreSetController(identifier); input_engine->PreSetButton(identifier, button_id); input_engine->PreSetButton(identifier, keyboard_key); @@ -820,8 +820,8 @@ std::unique_ptr InputFactory::CreateHatButtonDevice( const auto button_id = params.Get("hat", 0); const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; input_engine->PreSetController(identifier); input_engine->PreSetHatButton(identifier, button_id); @@ -879,7 +879,7 @@ std::unique_ptr InputFactory::CreateAnalogDevice( .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), .inverted = params.Get("invert", "+") == "-", - .toggle = static_cast(params.Get("toggle", false)), + .toggle = params.Get("toggle", false) != 0, }; input_engine->PreSetController(identifier); input_engine->PreSetAxis(identifier, axis); @@ -895,8 +895,8 @@ std::unique_ptr InputFactory::CreateTriggerDevice( }; const auto button = params.Get("button", 0); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; const auto axis = params.Get("axis", 0); const Common::Input::AnalogProperties properties = { @@ -926,8 +926,8 @@ std::unique_ptr InputFactory::CreateTouchDevice( }; const auto button = params.Get("button", 0); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; const auto axis_x = params.Get("axis_x", 0); const Common::Input::AnalogProperties properties_x = { diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index cbc1daf772..967da07916 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -248,7 +248,6 @@ if (MSVC) /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss - /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. ) else() target_compile_options(shader_recompiler PRIVATE From f3c40f4a208117ceb49396a381702b01c40efdb0 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:06 -0400 Subject: [PATCH 202/245] CMakeLists: Treat MSVC warnings as errors --- src/CMakeLists.txt | 2 ++ src/common/CMakeLists.txt | 1 - src/input_common/CMakeLists.txt | 1 - src/shader_recompiler/CMakeLists.txt | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cfd80886cb..397c1fa23f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,6 +58,8 @@ if (MSVC) # Warnings /W3 + /WX + /we4018 # 'expression': signed/unsigned mismatch /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled /we4101 # 'identifier': unreferenced local variable diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 043f27fb1c..72c406fe11 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -156,7 +156,6 @@ if (MSVC) ) target_compile_options(common PRIVATE /W4 - /WX /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index d2730bdc16..bff75338e4 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -39,7 +39,6 @@ add_library(input_common STATIC if (MSVC) target_compile_options(input_common PRIVATE /W4 - /WX /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 967da07916..bde1c1329a 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -241,7 +241,6 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit) if (MSVC) target_compile_options(shader_recompiler PRIVATE /W4 - /WX /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) From bad30259515d0e3ce438ae8d6236216e393f463e Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:06 -0400 Subject: [PATCH 203/245] decoders: Use 2's complement instead of unary - Resolves C4146 on MSVC --- src/video_core/textures/decoders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 52d067a2dc..fd1a4b9877 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -21,7 +21,7 @@ constexpr u32 pdep(u32 value) { u32 m = mask; for (u32 bit = 1; m; bit += bit) { if (value & bit) - result |= m & -m; + result |= m & (~m + 1); m &= m - 1; } return result; From cae108404a88a37bd767b46b6162a6e711ee805a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:06 -0400 Subject: [PATCH 204/245] CMakeLists: Remove redundant warnings These warnings are already included in /W3. --- src/CMakeLists.txt | 6 ------ src/common/CMakeLists.txt | 2 -- src/input_common/CMakeLists.txt | 2 -- src/shader_recompiler/CMakeLists.txt | 2 -- 4 files changed, 12 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 397c1fa23f..0bec3c53b3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,13 +60,9 @@ if (MSVC) /W3 /WX - /we4018 # 'expression': signed/unsigned mismatch /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled - /we4101 # 'identifier': unreferenced local variable /we4189 # 'identifier': local variable is initialized but not referenced /we4265 # 'class': class has virtual functions, but destructor is not virtual - /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data - /we4305 # 'context': truncation from 'type1' to 'type2' /we4388 # 'expression': signed/unsigned mismatch /we4389 # 'operator': signed/unsigned mismatch /we4456 # Declaration of 'identifier' hides previous local declaration @@ -77,9 +73,7 @@ if (MSVC) /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? /we4555 # Expression has no effect; expected expression with side-effect - /we4715 # 'function': not all control paths return a value /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. - /we4834 # Discarding return value of function with 'nodiscard' attribute /we5038 # data member 'member1' will be initialized after data member 'member2' /we5245 # 'function': unreferenced function with internal linkage has been removed ) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 72c406fe11..c0555f840c 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -158,8 +158,6 @@ if (MSVC) /W4 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data - /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data - /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index bff75338e4..cc6f0ffc0c 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -41,8 +41,6 @@ if (MSVC) /W4 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data - /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data - /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index bde1c1329a..bcdd60db9c 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -243,8 +243,6 @@ if (MSVC) /W4 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data - /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) - /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) From 3822e313232039af810b588646b724e19bc29bfc Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:07 -0400 Subject: [PATCH 205/245] CMakeLists: Disable C4100 and C4324 Disabling C4100 is similar to -Wno-unused-parameter --- CMakeLists.txt | 4 ++-- src/CMakeLists.txt | 3 +++ src/common/bounded_threadsafe_queue.h | 9 --------- .../backend/glasm/emit_glasm_not_implemented.cpp | 4 ---- .../backend/glsl/emit_glsl_not_implemented.cpp | 4 ---- 5 files changed, 5 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 957df54f54..b625743ea7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -541,9 +541,9 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY # Adjustments for MSVC + Ninja if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja") add_compile_options( - /wd4711 # function 'function' selected for automatic inline expansion /wd4464 # relative include path contains '..' - /wd4820 # 'identifier1': '4' bytes padding added after data member 'identifier2' + /wd4711 # function 'function' selected for automatic inline expansion + /wd4820 # 'bytes' bytes padding added after construct 'member_name' ) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0bec3c53b3..c94b78eb70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -76,6 +76,9 @@ if (MSVC) /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. /we5038 # data member 'member1' will be initialized after data member 'member2' /we5245 # 'function': unreferenced function with internal linkage has been removed + + /wd4100 # 'identifier': unreferenced formal parameter + /wd4324 # 'struct_name': structure was padded due to __declspec(align()) ) if (USE_CCACHE) diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index 7e465549bd..21217801e6 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h @@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer constexpr size_t hardware_interference_size = 64; #endif -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4324) -#endif - template class MPSCQueue { public: @@ -160,8 +155,4 @@ private: static_assert(std::is_nothrow_destructible_v, "T must be nothrow destructible"); }; -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } // namespace Common diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp index 7094d8e42d..1f4ffdd624 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp @@ -5,10 +5,6 @@ #include "shader_recompiler/backend/glasm/glasm_emit_context.h" #include "shader_recompiler/frontend/ir/value.h" -#ifdef _MSC_VER -#pragma warning(disable : 4100) -#endif - namespace Shader::Backend::GLASM { #define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp index b03a8ba1e5..9f1ed95a47 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp @@ -7,10 +7,6 @@ #include "shader_recompiler/backend/glsl/glsl_emit_context.h" #include "shader_recompiler/frontend/ir/value.h" -#ifdef _MSC_VER -#pragma warning(disable : 4100) -#endif - namespace Shader::Backend::GLSL { void EmitGetRegister(EmitContext& ctx) { From b02c3f2314a5561803f873ddd2d2f4dc024c1ed4 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:07 -0400 Subject: [PATCH 206/245] CMakeLists: Enforce C5233 on MSVC This is similar to Clang's -Wunused-lambda-capture --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c94b78eb70..9ff96c044d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,7 @@ if (MSVC) /we4555 # Expression has no effect; expected expression with side-effect /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. /we5038 # data member 'member1' will be initialized after data member 'member2' + /we5233 # explicit lambda capture 'identifier' is not used /we5245 # 'function': unreferenced function with internal linkage has been removed /wd4100 # 'identifier': unreferenced formal parameter From 347432524c179af443ef0f377abb0e44a84ec1e7 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:07 -0400 Subject: [PATCH 207/245] ipc_helpers: Ignore GCC compiler warnings only on GCC Clang and ICC for whatever reason also defines __GNUC__. Exclude them from this check. --- src/core/hle/ipc_helpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index aa27be7678..18fde8bd6d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -406,7 +406,7 @@ inline s32 RequestParser::Pop() { } // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif @@ -417,7 +417,7 @@ void RequestParser::PopRaw(T& value) { std::memcpy(&value, cmdbuf + index, sizeof(T)); index += (sizeof(T) + 3) / 4; // round up to word length } -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #pragma GCC diagnostic pop #endif From 6908ea2284cdf2beeb1d623a6600fe2fc22753a0 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:07 -0400 Subject: [PATCH 208/245] general: Resolve -Wclass-memaccess --- src/audio_core/renderer/behavior/info_updater.cpp | 2 +- src/audio_core/renderer/command/effect/biquad_filter.cpp | 2 +- .../renderer/command/effect/multi_tap_biquad_filter.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index c0a307b895..574cf09828 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp @@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context, voice_info.Initialize(); for (u32 channel = 0; channel < in_param.channel_count; channel++) { - std::memset(voice_states[channel], 0, sizeof(VoiceState)); + *voice_states[channel] = {}; } } diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp index 1baae74fd6..edb30ce724 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp @@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { auto state_{reinterpret_cast(state)}; if (needs_init) { - std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState)); + *state_ = {}; } auto input_buffer{ diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp index b3c3ba4ba5..48a7cba8a9 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp @@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc for (u32 i = 0; i < filter_tap_count; i++) { auto state{reinterpret_cast(states[i])}; if (needs_init[i]) { - std::memset(state, 0, sizeof(VoiceState::BiquadFilterState)); + *state = {}; } ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, From c7e079a5d441840e1ae2aabd5be0f45ffb737f1c Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:07 -0400 Subject: [PATCH 209/245] general: Resolve -Wunused-lambda-capture and C5233 --- src/core/file_sys/card_image.cpp | 4 +-- src/core/memory.cpp | 37 +++++++++----------- src/video_core/texture_cache/texture_cache.h | 8 ++--- src/video_core/textures/astc.cpp | 4 +-- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index f23d9373bd..5d02865f43 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -232,8 +232,8 @@ const std::vector>& XCI::GetNCAs() const { std::shared_ptr XCI::GetNCAByType(NCAContentType type) const { const auto program_id = secure_partition->GetProgramTitleID(); - const auto iter = std::find_if( - ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr& nca) { + const auto iter = + std::find_if(ncas.begin(), ncas.end(), [type, program_id](const std::shared_ptr& nca) { return nca->GetType() == type && nca->GetTitleId() == program_id; }); return iter == ncas.end() ? nullptr : *iter; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9637cb5b1e..3ca80c8ff0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -233,18 +233,17 @@ struct Memory::Impl { current_vaddr, src_addr, size); std::memset(dest_buffer, 0, copy_amount); }, - [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { + [&](const std::size_t copy_amount, const u8* const src_ptr) { std::memcpy(dest_buffer, src_ptr, copy_amount); }, - [&system = system, &dest_buffer](const VAddr current_vaddr, - const std::size_t copy_amount, - const u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, + const u8* const host_ptr) { if constexpr (!UNSAFE) { system.GPU().FlushRegion(current_vaddr, copy_amount); } std::memcpy(dest_buffer, host_ptr, copy_amount); }, - [&dest_buffer](const std::size_t copy_amount) { + [&](const std::size_t copy_amount) { dest_buffer = static_cast(dest_buffer) + copy_amount; }); } @@ -267,17 +266,16 @@ struct Memory::Impl { "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, dest_addr, size); }, - [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { + [&](const std::size_t copy_amount, u8* const dest_ptr) { std::memcpy(dest_ptr, src_buffer, copy_amount); }, - [&system = system, &src_buffer](const VAddr current_vaddr, - const std::size_t copy_amount, u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { if constexpr (!UNSAFE) { system.GPU().InvalidateRegion(current_vaddr, copy_amount); } std::memcpy(host_ptr, src_buffer, copy_amount); }, - [&src_buffer](const std::size_t copy_amount) { + [&](const std::size_t copy_amount) { src_buffer = static_cast(src_buffer) + copy_amount; }); } @@ -301,8 +299,7 @@ struct Memory::Impl { [](const std::size_t copy_amount, u8* const dest_ptr) { std::memset(dest_ptr, 0, copy_amount); }, - [&system = system](const VAddr current_vaddr, const std::size_t copy_amount, - u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { system.GPU().InvalidateRegion(current_vaddr, copy_amount); std::memset(host_ptr, 0, copy_amount); }, @@ -313,22 +310,20 @@ struct Memory::Impl { const std::size_t size) { WalkBlock( process, dest_addr, size, - [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount, - const VAddr current_vaddr) { + [&](const std::size_t copy_amount, const VAddr current_vaddr) { LOG_ERROR(HW_Memory, "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, src_addr, size); ZeroBlock(process, dest_addr, copy_amount); }, - [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { + [&](const std::size_t copy_amount, const u8* const src_ptr) { WriteBlockImpl(process, dest_addr, src_ptr, copy_amount); }, - [this, &system = system, &process, &dest_addr]( - const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { system.GPU().FlushRegion(current_vaddr, copy_amount); WriteBlockImpl(process, dest_addr, host_ptr, copy_amount); }, - [&dest_addr, &src_addr](const std::size_t copy_amount) { + [&](const std::size_t copy_amount) { dest_addr += static_cast(copy_amount); src_addr += static_cast(copy_amount); }); @@ -575,7 +570,7 @@ struct Memory::Impl { [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); }, - [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); + [&]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); if (ptr) { std::memcpy(&result, ptr, sizeof(T)); } @@ -599,7 +594,7 @@ struct Memory::Impl { LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, vaddr, static_cast(data)); }, - [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); + [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); if (ptr) { std::memcpy(ptr, &data, sizeof(T)); } @@ -613,7 +608,7 @@ struct Memory::Impl { LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, vaddr, static_cast(data)); }, - [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); + [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); if (ptr) { const auto volatile_pointer = reinterpret_cast(ptr); return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); @@ -628,7 +623,7 @@ struct Memory::Impl { LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", vaddr, static_cast(data[1]), static_cast(data[0])); }, - [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); + [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); if (ptr) { const auto volatile_pointer = reinterpret_cast(ptr); return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 0e0fd410f8..8ef75fe734 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -442,7 +442,7 @@ void TextureCache

    ::WriteMemory(VAddr cpu_addr, size_t size) { template void TextureCache

    ::DownloadMemory(VAddr cpu_addr, size_t size) { std::vector images; - ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { + ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { if (!image.IsSafeDownload()) { return; } @@ -1502,9 +1502,9 @@ void TextureCache

    ::UnregisterImage(ImageId image_id) { image.flags &= ~ImageFlagBits::BadOverlap; lru_cache.Free(image.lru_index); const auto& clear_page_table = - [this, image_id](u64 page, - std::unordered_map, Common::IdentityHash>& - selected_page_table) { + [image_id](u64 page, + std::unordered_map, Common::IdentityHash>& + selected_page_table) { const auto page_it = selected_page_table.find(page); if (page_it == selected_page_table.end()) { ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 15b9d4182b..69a32819a5 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -1661,8 +1661,8 @@ void Decompress(std::span data, uint32_t width, uint32_t height, for (u32 z = 0; z < depth; ++z) { const u32 depth_offset = z * height * width * 4; for (u32 y_index = 0; y_index < rows; ++y_index) { - auto decompress_stride = [data, width, height, depth, block_width, block_height, output, - rows, cols, z, depth_offset, y_index] { + auto decompress_stride = [data, width, height, block_width, block_height, output, rows, + cols, z, depth_offset, y_index] { const u32 y = y_index * block_height; for (u32 x_index = 0; x_index < cols; ++x_index) { const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; From 42c4ef737315ce32596a70635634ad72a33ffaa5 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:07 -0400 Subject: [PATCH 210/245] general: Resolve -Wunused-but-set-variable --- src/video_core/memory_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index d07b21bd67..384350dbdc 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -133,7 +133,7 @@ inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value template GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, PTEKind kind) { - u64 remaining_size{size}; + [[maybe_unused]] u64 remaining_size{size}; if constexpr (entry_type == EntryType::Mapped) { page_table.ReserveRange(gpu_addr, size); } @@ -159,7 +159,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp template GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, PTEKind kind) { - u64 remaining_size{size}; + [[maybe_unused]] u64 remaining_size{size}; for (u64 offset{}; offset < size; offset += big_page_size) { const GPUVAddr current_gpu_addr = gpu_addr + offset; [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); From f86774c1aca0e431248cf320e65e7785ad22cf3c Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:08 -0400 Subject: [PATCH 211/245] startup_checks: Resolve -Wformat --- src/yuzu/startup_checks.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index fc2693f9d7..9bb2a6ef4b 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -49,7 +49,7 @@ bool CheckEnvVars(bool* is_child) { *is_child = true; return false; } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { - std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", + std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n", IS_CHILD_ENV_VAR, GetLastError()); return true; } @@ -62,7 +62,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka // Set the startup variable for child processes const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); if (!env_var_set) { - std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", + std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n", STARTUP_CHECK_ENV_VAR, GetLastError()); return false; } @@ -81,22 +81,22 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka DWORD exit_code = STILL_ACTIVE; const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); if (err == 0) { - std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError()); + std::fprintf(stderr, "GetExitCodeProcess failed with error %lu\n", GetLastError()); } // Vulkan is broken if the child crashed (return value is not zero) *has_broken_vulkan = (exit_code != 0); if (CloseHandle(process_info.hProcess) == 0) { - std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); + std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError()); } if (CloseHandle(process_info.hThread) == 0) { - std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); + std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError()); } } if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { - std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n", + std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %lu\n", STARTUP_CHECK_ENV_VAR, GetLastError()); } @@ -149,7 +149,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { pi // lpProcessInformation ); if (!process_created) { - std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError()); + std::fprintf(stderr, "CreateProcessA failed with error %lu\n", GetLastError()); return false; } From bb31b0f261fbcd6398f644c0070d89f67b92a9ef Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:08 -0400 Subject: [PATCH 212/245] startup_checks: Resolve -Wstringop-truncation Copies up to sizeof(p_name) - 1 in strncpy and null terminates it at p_name[254] --- src/yuzu/startup_checks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 9bb2a6ef4b..6a91212e20 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -135,7 +135,8 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { startup_info.cb = sizeof(startup_info); char p_name[255]; - std::strncpy(p_name, arg0, 255); + std::strncpy(p_name, arg0, 254); + p_name[254] = '\0'; const bool process_created = CreateProcessA(nullptr, // lpApplicationName p_name, // lpCommandLine From f51c71e956cb39c08c605b8a4b1f5886b0563944 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:08 -0400 Subject: [PATCH 213/245] yuzu: Resolve -Wpessimizing-move --- src/yuzu/multiplayer/state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index ae2738ad4c..285bb150d8 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp @@ -268,7 +268,7 @@ bool MultiplayerState::OnCloseRoom() { return true; } // Save ban list - UISettings::values.multiplayer_ban_list = std::move(room->GetBanList()); + UISettings::values.multiplayer_ban_list = room->GetBanList(); room->Destroy(); announce_multiplayer_session->Stop(); From 120cd450e5335bc05ab8c6bf6d78da09374c23c6 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:34:08 -0400 Subject: [PATCH 214/245] CMakeLists: Disable -Wbraced-scalar-init on Clang Clang erroneously emits this warning when using designated initializers. --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ff96c044d..0ac3d254e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -110,6 +110,7 @@ else() -Wno-invalid-offsetof -Wno-unused-parameter + $<$:-Wno-braced-scalar-init> $<$:-Wno-unused-private-field> ) From 2ccbf5abdd6d2b2a7c0d80371077d6585865cea6 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 22 Oct 2022 14:05:00 -0500 Subject: [PATCH 215/245] core: hid: Add handheld to nfc devices --- src/core/hid/emulated_controller.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 025f1c78e4..28c654df5e 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1048,6 +1048,7 @@ bool EmulatedController::HasNfc() const { case NpadStyleIndex::JoyconRight: case NpadStyleIndex::JoyconDual: case NpadStyleIndex::ProController: + case NpadStyleIndex::Handheld: break; default: return false; From 2d90a927c9f7652aa7e259ba57fa22ddbf8bed24 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 23 Oct 2022 05:45:45 -0400 Subject: [PATCH 216/245] core: barrier service thread shutdown --- src/core/core.cpp | 1 + src/core/hle/kernel/kernel.cpp | 12 +++++++++--- src/core/hle/service/nvflinger/nvflinger.cpp | 12 ++++++++---- src/core/hle/service/nvflinger/nvflinger.h | 2 ++ src/core/hle/service/service.cpp | 4 ++++ src/core/hle/service/service.h | 2 ++ 6 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 7fb8bc0195..40a6104353 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -384,6 +384,7 @@ struct System::Impl { kernel.ShutdownCores(); cpu_manager.Shutdown(); debugger.reset(); + services->KillNVNFlinger(); kernel.CloseServices(); services.reset(); service_manager.reset(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index eed2dc9f3e..fdc774e307 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -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> service_threads; std::weak_ptr default_service_thread; Common::ThreadWorker service_threads_manager; + Common::Barrier service_thread_barrier; std::array shutdown_threads; std::array, Core::Hardware::NUM_CPU_CORES> schedulers{}; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index aa14d2cbcf..dad93b38e2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -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 instance) { diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 99509bc5bf..b8191c5954 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -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 instance); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index dadaf897f1..5db6588e48 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -303,4 +303,8 @@ Services::Services(std::shared_ptr& sm, Core::System& system Services::~Services() = default; +void Services::KillNVNFlinger() { + nv_flinger->ShutdownLayers(); +} + } // namespace Service diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 5bf197c519..ec9deeee43 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -238,6 +238,8 @@ public: explicit Services(std::shared_ptr& sm, Core::System& system); ~Services(); + void KillNVNFlinger(); + private: std::unique_ptr hos_binder_driver_server; std::unique_ptr nv_flinger; From 05f2673648115e53de9e04791f1d30163f90e5ee Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 23 Oct 2022 19:25:57 -0400 Subject: [PATCH 217/245] nvdrv: fix container destruction order --- src/core/hle/service/nvdrv/nvdrv.cpp | 2 +- src/core/hle/service/nvdrv/nvdrv.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 9d99243959..9f4c7c99a8 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -53,7 +53,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { + : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} { builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { std::shared_ptr device = std::make_shared(system, *this, container); diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 146d046a93..f3c81bd882 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -97,6 +97,9 @@ private: friend class EventInterface; friend class Service::NVFlinger::NVFlinger; + /// Manages syncpoints on the host + NvCore::Container container; + /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; @@ -108,9 +111,6 @@ private: EventInterface events_interface; - /// Manages syncpoints on the host - NvCore::Container container; - std::unordered_map> builders; }; From 1689e0a71f5d0518e91d82226612cc5336fe6d4f Mon Sep 17 00:00:00 2001 From: FengChen Date: Sat, 22 Oct 2022 21:27:34 +0800 Subject: [PATCH 218/245] file_sys: Priority display of game titles in the current language --- src/core/file_sys/control_metadata.cpp | 43 ++++++++++++++++++++------ src/core/file_sys/control_metadata.h | 6 ++-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index be25da2f6d..50f44f598f 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/settings.h" #include "common/string_util.h" #include "common/swap.h" #include "core/file_sys/control_metadata.h" @@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const { developer_name.size()); } +constexpr std::array language_to_codes = {{ + Language::Japanese, + Language::AmericanEnglish, + Language::French, + Language::German, + Language::Italian, + Language::Spanish, + Language::Chinese, + Language::Korean, + Language::Dutch, + Language::Portuguese, + Language::Russian, + Language::Taiwanese, + Language::BritishEnglish, + Language::CanadianFrench, + Language::LatinAmericanSpanish, + Language::Chinese, + Language::Taiwanese, + Language::BrazilianPortuguese, +}}; + NACP::NACP() = default; NACP::NACP(VirtualFile file) { @@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) { NACP::~NACP() = default; -const LanguageEntry& NACP::GetLanguageEntry(Language language) const { - if (language != Language::Default) { - return raw.language_entries.at(static_cast(language)); +const LanguageEntry& NACP::GetLanguageEntry() const { + Language language = language_to_codes[Settings::values.language_index.GetValue()]; + + { + const auto& language_entry = raw.language_entries.at(static_cast(language)); + if (!language_entry.GetApplicationName().empty()) + return language_entry; } for (const auto& language_entry : raw.language_entries) { @@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const { return language_entry; } - // Fallback to English - return GetLanguageEntry(Language::AmericanEnglish); + return raw.language_entries.at(static_cast(Language::AmericanEnglish)); } -std::string NACP::GetApplicationName(Language language) const { - return GetLanguageEntry(language).GetApplicationName(); +std::string NACP::GetApplicationName() const { + return GetLanguageEntry().GetApplicationName(); } -std::string NACP::GetDeveloperName(Language language) const { - return GetLanguageEntry(language).GetDeveloperName(); +std::string NACP::GetDeveloperName() const { + return GetLanguageEntry().GetDeveloperName(); } u64 NACP::GetTitleId() const { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 75295519c7..6a81873b15 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -101,9 +101,9 @@ public: explicit NACP(VirtualFile file); ~NACP(); - const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; - std::string GetApplicationName(Language language = Language::Default) const; - std::string GetDeveloperName(Language language = Language::Default) const; + const LanguageEntry& GetLanguageEntry() const; + std::string GetApplicationName() const; + std::string GetDeveloperName() const; u64 GetTitleId() const; u64 GetDLCBaseTitleId() const; std::string GetVersionString() const; From 1a378a776902e1142b907110c6dd3a3a1647d328 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 23 Oct 2022 05:24:38 -0400 Subject: [PATCH 219/245] kernel: refactor dummy thread wakeups --- .../hle/kernel/global_scheduler_context.cpp | 22 ++++++++++ .../hle/kernel/global_scheduler_context.h | 8 ++++ src/core/hle/kernel/k_scheduler.cpp | 26 ++++++++++- src/core/hle/kernel/k_thread.cpp | 44 +++++++++---------- src/core/hle/kernel/k_thread.h | 8 ++-- 5 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 65576b8c4b..fd911a3a5e 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const { return scheduler_lock.IsLockedByCurrentThread(); } +void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) { + ASSERT(IsLocked()); + + woken_dummy_threads.insert(thread); +} + +void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) { + ASSERT(IsLocked()); + + woken_dummy_threads.erase(thread); +} + +void GlobalSchedulerContext::WakeupWaitingDummyThreads() { + ASSERT(IsLocked()); + + for (auto* thread : woken_dummy_threads) { + thread->DummyThreadEndWait(); + } + + woken_dummy_threads.clear(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 67bb9852da..220ed61922 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "common/common_types.h" @@ -58,6 +59,10 @@ public: /// Returns true if the global scheduler lock is acquired bool IsLocked() const; + void UnregisterDummyThreadForWakeup(KThread* thread); + void RegisterDummyThreadForWakeup(KThread* thread); + void WakeupWaitingDummyThreads(); + [[nodiscard]] LockType& SchedulerLock() { return scheduler_lock; } @@ -76,6 +81,9 @@ private: KSchedulerPriorityQueue priority_queue; LockType scheduler_lock; + /// Lists dummy threads pending wakeup on lock release + std::set woken_dummy_threads; + /// Lists all thread ids that aren't deleted/etc. std::vector thread_list; std::mutex global_list_guard; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c34ce7a178..b1cabbca06 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -81,8 +81,8 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { // HACK: we cannot schedule from this thread, it is not a core thread ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); - // Special case to ensure dummy threads that are waiting block - GetCurrentThread(kernel).IfDummyThreadTryWait(); + // Ensure dummy threads that are waiting block. + GetCurrentThread(kernel).DummyThreadBeginWait(); ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); GetCurrentThread(kernel).EnableDispatch(); @@ -314,6 +314,16 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { idle_cores &= ~(1ULL << core_id); } + // HACK: any waiting dummy threads can wake up now. + kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads(); + + // HACK: if we are a dummy thread, and we need to go sleep, indicate + // that for when the lock is released. + KThread* const cur_thread = GetCurrentThreadPointer(kernel); + if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) { + cur_thread->RequestDummyThreadWait(); + } + return cores_needing_scheduling; } @@ -531,11 +541,23 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa GetPriorityQueue(kernel).Remove(thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); + + if (thread->IsDummyThread()) { + // HACK: if this is a dummy thread, it should no longer wake up when the + // scheduler lock is released. + kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread); + } } else if (cur_state == ThreadState::Runnable) { // If we're now runnable, then we weren't previously, and we should add. GetPriorityQueue(kernel).PushBack(thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); + + if (thread->IsDummyThread()) { + // HACK: if this is a dummy thread, it should wake up when the scheduler + // lock is released. + kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread); + } } } diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index b7bfcdce31..d57b42fdf7 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -148,7 +148,9 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack physical_affinity_mask.SetAffinity(phys_core, true); // Set the thread state. - thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; + thread_state = (type == ThreadType::Main || type == ThreadType::Dummy) + ? ThreadState::Runnable + : ThreadState::Initialized; // Set TLS address. tls_address = 0; @@ -1174,30 +1176,29 @@ Result KThread::Sleep(s64 timeout) { R_SUCCEED(); } -void KThread::IfDummyThreadTryWait() { - if (!IsDummyThread()) { - return; - } +void KThread::RequestDummyThreadWait() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + ASSERT(this->IsDummyThread()); - if (GetState() != ThreadState::Waiting) { - return; - } - - ASSERT(!kernel.IsPhantomModeForSingleCore()); - - // Block until we are no longer waiting. - std::unique_lock lk(dummy_wait_lock); - dummy_wait_cv.wait( - lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); }); + // We will block when the scheduler lock is released. + dummy_thread_runnable.store(false); } -void KThread::IfDummyThreadEndWait() { - if (!IsDummyThread()) { - return; - } +void KThread::DummyThreadBeginWait() { + ASSERT(this->IsDummyThread()); + ASSERT(!kernel.IsPhantomModeForSingleCore()); + + // Block until runnable is no longer false. + dummy_thread_runnable.wait(false); +} + +void KThread::DummyThreadEndWait() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + ASSERT(this->IsDummyThread()); // Wake up the waiting thread. - dummy_wait_cv.notify_one(); + dummy_thread_runnable.store(true); + dummy_thread_runnable.notify_one(); } void KThread::BeginWait(KThreadQueue* queue) { @@ -1231,9 +1232,6 @@ void KThread::EndWait(Result wait_result_) { } wait_queue->EndWait(this, wait_result_); - - // Special case for dummy threads to wakeup if necessary. - IfDummyThreadEndWait(); } } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index e2a27d6036..30aa10c9a8 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -643,8 +643,9 @@ public: // therefore will not block on guest kernel synchronization primitives. These methods handle // blocking as needed. - void IfDummyThreadTryWait(); - void IfDummyThreadEndWait(); + void RequestDummyThreadWait(); + void DummyThreadBeginWait(); + void DummyThreadEndWait(); [[nodiscard]] uintptr_t GetArgument() const { return argument; @@ -777,8 +778,7 @@ private: bool is_single_core{}; ThreadType thread_type{}; StepState step_state{}; - std::mutex dummy_wait_lock; - std::condition_variable dummy_wait_cv; + std::atomic dummy_thread_runnable{true}; // For debugging std::vector wait_objects_for_debugging; From 165bce3c2d265760190ea848a064f0d6538710bb Mon Sep 17 00:00:00 2001 From: Feng Chen Date: Tue, 25 Oct 2022 12:57:25 +0800 Subject: [PATCH 220/245] Revert "shader_recompiler/dead_code_elimination: Add DeadBranchElimination pass" --- .../frontend/ir/microinstruction.cpp | 5 - src/shader_recompiler/frontend/ir/value.h | 4 - .../ir_opt/dead_code_elimination_pass.cpp | 100 ++---------------- 3 files changed, 10 insertions(+), 99 deletions(-) diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 468782eb17..84417980bc 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp @@ -325,11 +325,6 @@ 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(index)}; - phi_args.erase(operand_it); -} - void Inst::OrderPhiArgs() { if (op != Opcode::Phi) { throw LogicError("{} is not a Phi instruction", op); diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 1a2e4ccb61..6a673ca05a 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -178,13 +178,9 @@ 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(); diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp index 9a7d47344d..1bd8afd6f6 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp @@ -1,104 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include - -#include - #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 { -namespace { -template -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. - 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 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 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(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); + // 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) { - DeadInstElimination(block); + auto it{block->end()}; + while (it != block->begin()) { + --it; + if (!it->HasUses() && !it->MayHaveSideEffects()) { + it->Invalidate(); + it = block->Instructions().erase(it); + } + } } } From 0ec1801bc1e07e0630e9be55ef123294c8155b6a Mon Sep 17 00:00:00 2001 From: FengChen Date: Tue, 25 Oct 2022 22:39:29 +0800 Subject: [PATCH 221/245] video_core: Catch vulkan clear op not all channel need clear --- .../renderer_vulkan/vk_rasterizer.cpp | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9f05a7a18c..6ab68892c0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -305,14 +305,19 @@ void RasterizerVulkan::Clear() { } } - scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { - const VkClearAttachment attachment{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .colorAttachment = color_attachment, - .clearValue = clear_value, - }; - cmdbuf.ClearAttachments(attachment, clear_rect); - }); + if (regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B && + regs.clear_surface.A) { + scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { + const VkClearAttachment attachment{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = color_attachment, + .clearValue = clear_value, + }; + cmdbuf.ClearAttachments(attachment, clear_rect); + }); + } else { + UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); + } } if (!use_depth && !use_stencil) { From fa9b7db76f4c179e2af2f6f1974f92858586d533 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Tue, 25 Oct 2022 15:20:23 +0000 Subject: [PATCH 222/245] tests: fix for -Wall Fix #9123 --- src/tests/video_core/buffer_base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index 71121e42a0..f7236afabf 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp @@ -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; From 8b4d5aeb4f01348312ca38555d7951ff54b23fc3 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 26 Oct 2022 00:41:54 -0400 Subject: [PATCH 223/245] 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. --- src/common/concepts.h | 16 +++------------- src/common/fs/file.h | 12 ++++++------ src/core/hle/kernel/hle_ipc.h | 2 +- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/common/concepts.h b/src/common/concepts.h index e8ce30dfed..a9acff3e79 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h @@ -3,24 +3,14 @@ #pragma once +#include #include namespace Common { -// Check if type is like an STL container +// Check if type satisfies the ContiguousContainer named requirement. template -concept IsSTLContainer = requires(T t) { - typename T::value_type; - typename T::iterator; - typename T::const_iterator; - // TODO(ogniK): Replace below is std::same_as when MSVC supports it. - t.begin(); - t.end(); - t.cbegin(); - t.cend(); - t.data(); - t.size(); -}; +concept IsContiguousContainer = std::contiguous_iterator; // TODO: Replace with std::derived_from when the header // is available on all supported platforms. diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 69b53384c5..167c4d8261 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -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 [[nodiscard]] size_t Read(T& data) const { - if constexpr (IsSTLContainer) { + if constexpr (IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "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 [[nodiscard]] size_t Write(const T& data) const { - if constexpr (IsSTLContainer) { + if constexpr (IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index a0522bca0b..1083638a9e 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -304,7 +304,7 @@ public: */ template >> std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { - if constexpr (Common::IsSTLContainer) { + if constexpr (Common::IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Container to WriteBuffer must contain trivially copyable objects"); From e0ec9ffc36892319a5cd847dfd42f6aba4671685 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 26 Oct 2022 11:15:25 -0400 Subject: [PATCH 224/245] 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. --- src/audio_core/in/audio_in_system.cpp | 2 +- src/audio_core/in/audio_in_system.h | 2 +- src/audio_core/out/audio_out_system.cpp | 4 ++-- src/audio_core/out/audio_out_system.h | 4 ++-- src/core/hle/service/audio/audin_u.cpp | 2 +- src/core/hle/service/audio/audout_u.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 6b7e6715cf..4324cafd84 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -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()) { diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h index b9dc0e60ff..1c51546381 100644 --- a/src/audio_core/in/audio_in_system.h +++ b/src/audio_core/in/audio_in_system.h @@ -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); /** diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 48a8019232..a66208ed9c 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -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; diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h index 0817b2f371..b95cb91bec 100644 --- a/src/audio_core/out/audio_out_system.h +++ b/src/audio_core/out/audio_out_system.h @@ -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. diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 48a9a73a0e..608925dfcd 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn; class IAudioIn final : public ServiceFramework { 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")}, diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 49c0923016..122290c6a6 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut; class IAudioOut final : public ServiceFramework { 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( From f6e7cae62c3a3df41fe86566d4b786454f90b48a Mon Sep 17 00:00:00 2001 From: FengChen Date: Thu, 27 Oct 2022 13:21:47 +0800 Subject: [PATCH 225/245] video_core: Fix drawing trigger mechanism regression --- src/video_core/engines/maxwell_3d.cpp | 141 ++++++++++++++------------ src/video_core/engines/maxwell_3d.h | 2 + 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 5208bea75b..f9794dfe43 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -123,9 +123,6 @@ void Maxwell3D::InitializeRegisterDefaults() { 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; @@ -216,6 +213,21 @@ 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(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 ProcessDraw(); + 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 ProcessDraw(); + 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; + return ProcessDraw(); case MAXWELL3D_REG_INDEX(topology_override): use_topology_override = true; return; @@ -583,70 +595,7 @@ 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(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(inline_index_draw_indexes.size() / 4); - regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; - } - } - } - +void Maxwell3D::ProcessDraw(u32 instance_count) { LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), regs.vertex_buffer.count); @@ -669,6 +618,64 @@ void Maxwell3D::ProcessDeferredDraw() { } else { regs.vertex_buffer.count = 0; } +} + +void Maxwell3D::ProcessDeferredDraw() { + if (deferred_draw_method.empty()) { + return; + } + + enum class DrawMode { + Undefined, + General, + Instance, + }; + DrawMode draw_mode{DrawMode::Undefined}; + u32 instance_count = 1; + + u32 index = 0; + u32 method = 0; + u32 method_count = static_cast(deferred_draw_method.size()); + for (; index < method_count && + (method = deferred_draw_method[index]) != MAXWELL3D_REG_INDEX(draw.begin); + ++index) + ; + + if (MAXWELL3D_REG_INDEX(draw.begin) != method) { + return; + } + + // The minimum number of methods for drawing must be greater than or equal to + // 3[draw.begin->vertex(index)count(first)->draw.end] to avoid errors in index mode drawing + if ((method_count - index) < 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; + + // 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(method_count - index) / 4; + } else { + method = deferred_draw_method[index + 1]; + if (MAXWELL3D_REG_INDEX(draw_inline_index) == method || + MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method || + MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) { + regs.index_buffer.count = static_cast(inline_index_draw_indexes.size() / 4); + regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; + } + } + + ProcessDraw(instance_count); deferred_draw_method.clear(); inline_index_draw_indexes.clear(); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index bd23ebc12c..a948fcb14d 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -3143,6 +3143,8 @@ private: /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) void ProcessTopologyOverride(); + void ProcessDraw(u32 instance_count = 1); + void ProcessDeferredDraw(); /// Returns a query's value or an empty object if the value will be deferred through a cache. From cdb9fe978ff29b1de2256f0d0cece550195f3fef Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 25 Oct 2022 17:42:02 -0400 Subject: [PATCH 226/245] vi: implement CloseDisplay --- src/core/hle/service/nvflinger/nvflinger.cpp | 13 +++++++++++++ src/core/hle/service/nvflinger/nvflinger.h | 5 +++++ src/core/hle/service/vi/display/vi_display.h | 6 ++++++ src/core/hle/service/vi/vi.cpp | 8 ++++---- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index dad93b38e2..c3af12c90a 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -138,6 +138,19 @@ std::optional NVFlinger::OpenDisplay(std::string_view name) { return itr->GetID(); } +bool NVFlinger::CloseDisplay(u64 display_id) { + const auto lock_guard = Lock(); + auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return false; + } + + display->Reset(); + + return true; +} + std::optional NVFlinger::CreateLayer(u64 display_id) { const auto lock_guard = Lock(); auto* const display = FindDisplay(display_id); diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index b8191c5954..460bef976d 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -58,6 +58,11 @@ public: /// If an invalid display name is provided, then an empty optional is returned. [[nodiscard]] std::optional OpenDisplay(std::string_view name); + /// Closes the specified display by its ID. + /// + /// Returns false if an invalid display ID is provided. + [[nodiscard]] bool CloseDisplay(u64 display_id); + /// Creates a layer on the specified display and returns the layer ID. /// /// If an invalid display ID is specified, then an empty optional is returned. diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 33d5f398cd..0b65a65da9 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -106,6 +106,12 @@ public: /// void CloseLayer(u64 layer_id); + /// Resets the display for a new connection. + void Reset() { + layers.clear(); + got_vsync_event = false; + } + /// Attempts to find a layer with the given ID. /// /// @param layer_id The layer ID. diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 9c917cacfe..bb283e74e3 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -324,10 +324,10 @@ private: IPC::RequestParser rp{ctx}; const u64 display = rp.Pop(); - LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display); + const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(rc); } void CreateManagedLayer(Kernel::HLERequestContext& ctx) { @@ -508,10 +508,10 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop(); - LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(rc); } // This literally does nothing internally in the actual service itself, From 2cdfbbc07d74548527fbefd738f860ce66f52e34 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 26 Oct 2022 21:35:19 -0400 Subject: [PATCH 227/245] nvnflinger: release queued handles immediately on disconnection --- src/core/hle/service/nvdrv/core/nvmap.cpp | 5 +++-- src/core/hle/service/nvdrv/core/nvmap.h | 1 + src/core/hle/service/nvdrv/devices/nvmap.cpp | 10 ++++++---- .../hle/service/nvflinger/buffer_queue_producer.cpp | 7 +++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index fbd8a74a55..a51ca54447 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -255,15 +255,16 @@ std::optional NvMap::FreeHandle(Handle::Id handle, bool interna .address = handle_description->address, .size = handle_description->size, .was_uncached = handle_description->flags.map_uncached.Value() != 0, + .can_unlock = true, }; } else { return std::nullopt; } - // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed + // If the handle hasn't been freed from memory, mark that if (!hWeak.expired()) { LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); - freeInfo.address = 0; + freeInfo.can_unlock = false; } return freeInfo; diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index b9dd3801f4..a8e5738909 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -105,6 +105,7 @@ public: u64 address; //!< Address the handle referred to before deletion u64 size; //!< Page-aligned handle size bool was_uncached; //!< If the handle was allocated as uncached + bool can_unlock; //!< If the address region is ready to be unlocked }; explicit NvMap(Tegra::Host1x::Host1x& host1x); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index b606790217..44388655d3 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -251,10 +251,12 @@ NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { } if (auto freeInfo{file.FreeHandle(params.handle, false)}) { - ASSERT(system.CurrentProcess() - ->PageTable() - .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) - .IsSuccess()); + if (freeInfo->can_unlock) { + ASSERT(system.CurrentProcess() + ->PageTable() + .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) + .IsSuccess()); + } params.address = freeInfo->address; params.size = static_cast(freeInfo->size); params.flags.raw = 0; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 77ddbb6efb..41ba44b215 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -742,6 +742,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { return Status::NoError; } + // HACK: We are not Android. Remove handle for items in queue, and clear queue. + // Allows synchronous destruction of nvmap handles. + for (auto& item : core->queue) { + nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); + } + core->queue.clear(); + switch (api) { case NativeWindowApi::Egl: case NativeWindowApi::Cpu: From 3e6840a74cf2203223058d4fa1e0a0e4784cbd70 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 25 Oct 2022 17:47:18 -0400 Subject: [PATCH 228/245] arm_interface: curb infinite recursion in stacktrace generation --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index d1e70f19d6..287ba102e6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -450,7 +450,7 @@ std::vector ARM_Dynarmic_32::GetBacktrace(Core::S // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+4 : value of lr for frame - while (true) { + for (size_t i = 0; i < 256; i++) { out.push_back({"", 0, lr, 0, ""}); if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { break; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 22b5d56568..afb7fb3a00 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -517,7 +517,7 @@ std::vector ARM_Dynarmic_64::GetBacktrace(Core::S // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+8 : value of lr for frame - while (true) { + for (size_t i = 0; i < 256; i++) { out.push_back({"", 0, lr, 0, ""}); if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { break; From dce242858addfb0cba54b80e7297992ee0f9d0e0 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Fri, 28 Oct 2022 03:42:43 +0200 Subject: [PATCH 229/245] vk_scheduler: Remove recorded_counts --- src/video_core/renderer_vulkan/vk_scheduler.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index c04aad08f4..9292167497 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -144,7 +144,6 @@ private: using FuncType = TypedCommand; static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); - recorded_counts++; command_offset = Common::AlignUp(command_offset, alignof(FuncType)); if (command_offset > sizeof(data) - sizeof(FuncType)) { return false; @@ -166,7 +165,7 @@ private: } bool Empty() const { - return recorded_counts == 0; + return command_offset == 0; } bool HasSubmit() const { @@ -177,7 +176,6 @@ private: Command* first = nullptr; Command* last = nullptr; - size_t recorded_counts = 0; size_t command_offset = 0; bool submit = false; alignas(std::max_align_t) std::array data{}; From d867ae5ab6d958c2a3eb20ba4a9a2aafe9c1a033 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 29 Oct 2022 23:05:56 -0400 Subject: [PATCH 230/245] k_server_session: fix crashes --- src/core/hle/kernel/k_server_session.h | 2 +- src/core/hle/service/sm/sm.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 32135473b7..188aef4af2 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -91,7 +91,7 @@ private: /// List of threads which are pending a reply. boost::intrusive::list m_request_list; - KSessionRequest* m_current_request; + KSessionRequest* m_current_request{}; KLightLock m_lock; }; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 48e70f93c7..e2b8d87206 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -80,7 +80,6 @@ ResultVal ServiceManager::GetServicePort(const std::string& name } auto* port = Kernel::KPort::Create(kernel); - SCOPE_EXIT({ port->Close(); }); port->Initialize(ServerSessionCountMax, false, name); auto handler = it->second; From 6f0f7f1547cc9b238fe526813d7408ba3e2ce409 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 30 Oct 2022 00:34:22 -0500 Subject: [PATCH 231/245] service: am: Stub SetRecordVolumeMuted Used by bayonetta 3 --- src/core/hle/service/am/am.cpp | 13 ++++++++++++- src/core/hle/service/am/am.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index e55233054f..8ea7fd7608 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -299,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, {110, nullptr, "SetApplicationAlbumUserData"}, {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, - {130, nullptr, "SetRecordVolumeMuted"}, + {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"}, {1000, nullptr, "GetDebugStorageChannel"}, }; // clang-format on @@ -597,6 +597,17 @@ void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void ISelfController::SetRecordVolumeMuted(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto is_record_volume_muted = rp.Pop(); + + LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + AppletMessageQueue::AppletMessageQueue(Core::System& system) : service_context{system, "AppletMessageQueue"} { on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index bb75c62817..a0fbfcfc5f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -182,6 +182,7 @@ private: void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); + void SetRecordVolumeMuted(Kernel::HLERequestContext& ctx); enum class ScreenshotPermission : u32 { Inherit = 0, From 8f00c59462354b66cec6cc05ed6d9259297a19f6 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 11:01:22 -0400 Subject: [PATCH 232/245] kernel: reinitialize after dram layout change --- src/core/core.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 40a6104353..d8934be528 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -137,6 +137,7 @@ struct System::Impl { device_memory = std::make_unique(); is_multicore = Settings::values.use_multi_core.GetValue(); + extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); @@ -166,13 +167,18 @@ struct System::Impl { } void ReinitializeIfNecessary(System& system) { - if (is_multicore == Settings::values.use_multi_core.GetValue()) { + const bool must_reinitialize = + is_multicore != Settings::values.use_multi_core.GetValue() || + extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue(); + + if (!must_reinitialize) { return; } LOG_DEBUG(Kernel, "Re-initializing"); is_multicore = Settings::values.use_multi_core.GetValue(); + extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); Initialize(system); } @@ -521,6 +527,7 @@ struct System::Impl { bool is_multicore{}; bool is_async_gpu{}; + bool extended_memory_layout{}; ExecuteProgramCallback execute_program_callback; ExitCallback exit_callback; From 808e22984f303789a520a1838efd236069159057 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 15:04:16 -0400 Subject: [PATCH 233/245] vk_blit_screen: recreate swapchain images on guest format change --- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 6 +++++- src/video_core/renderer_vulkan/vk_blit_screen.h | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index cb7fa20784..89426121fd 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -480,11 +480,15 @@ void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { fsr.reset(); } - if (framebuffer.width == raw_width && framebuffer.height == raw_height && !raw_images.empty()) { + if (framebuffer.width == raw_width && framebuffer.height == raw_height && + framebuffer.pixel_format == pixel_format && !raw_images.empty()) { return; } + raw_width = framebuffer.width; raw_height = framebuffer.height; + pixel_format = framebuffer.pixel_format; + ReleaseRawImages(); CreateStagingBuffer(framebuffer); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 29e2ea925e..a2b73ec546 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -28,6 +28,10 @@ namespace VideoCore { class RasterizerInterface; } +namespace Service::android { +enum class PixelFormat : u32; +} + namespace Vulkan { struct ScreenInfo; @@ -156,6 +160,7 @@ private: u32 raw_width = 0; u32 raw_height = 0; + Service::android::PixelFormat pixel_format{}; std::unique_ptr fsr; }; From 67e0d3815257d081ac35b5e5f98b173a493222c7 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 30 Oct 2022 20:47:34 +0100 Subject: [PATCH 234/245] Vulkan: Fix regression caused by limiting render area to width/height of rendef targets. --- src/video_core/renderer_vulkan/vk_texture_cache.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 305ad8aeee..6ad7efbdf0 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1782,17 +1782,17 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, const auto& resolution = runtime.resolution; - u32 width = 0; - u32 height = 0; + u32 width = std::numeric_limits::max(); + u32 height = std::numeric_limits::max(); for (size_t index = 0; index < NUM_RT; ++index) { const ImageView* const color_buffer = color_buffers[index]; if (!color_buffer) { renderpass_key.color_formats[index] = PixelFormat::Invalid; continue; } - width = std::max(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) + width = std::min(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) : color_buffer->size.width); - height = std::max(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) + height = std::min(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) : color_buffer->size.height); attachments.push_back(color_buffer->RenderTarget()); renderpass_key.color_formats[index] = color_buffer->format; @@ -1804,9 +1804,9 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } const size_t num_colors = attachments.size(); if (depth_buffer) { - width = std::max(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) + width = std::min(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) : depth_buffer->size.width); - height = std::max(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) + height = std::min(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) : depth_buffer->size.height); attachments.push_back(depth_buffer->RenderTarget()); renderpass_key.depth_format = depth_buffer->format; From eec3184bb0151396e720ff876244aed3829173e2 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 18:44:29 -0400 Subject: [PATCH 235/245] k_thread: fix single core --- src/core/hle/kernel/k_thread.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index d57b42fdf7..cc88d08f03 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -1185,8 +1185,10 @@ void KThread::RequestDummyThreadWait() { } void KThread::DummyThreadBeginWait() { - ASSERT(this->IsDummyThread()); - ASSERT(!kernel.IsPhantomModeForSingleCore()); + if (!this->IsDummyThread() || kernel.IsPhantomModeForSingleCore()) { + // Occurs in single core mode. + return; + } // Block until runnable is no longer false. dummy_thread_runnable.wait(false); From 4e9adae5da95219b85f11309919944bc07c4043d Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 20:58:20 -0400 Subject: [PATCH 236/245] kernel: more complete fix for KPort reference counting --- src/core/hle/service/sm/sm.cpp | 7 ++--- src/core/hle/service/sm/sm_controller.cpp | 33 ++++++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index e2b8d87206..cb6c0e96f9 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -149,9 +149,10 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& return port_result.Code(); } auto& port = port_result.Unwrap(); - SCOPE_EXIT({ port->GetClientPort().Close(); }); - - kernel.RegisterServerObject(&port->GetServerPort()); + SCOPE_EXIT({ + port->GetClientPort().Close(); + port->GetServerPort().Close(); + }); // Create a new session. Kernel::KClientSession* session{}; diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 273f795684..46a8439d8e 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -28,23 +28,36 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service, "called"); + auto& process = *ctx.GetThread().GetOwnerProcess(); auto& parent_session = *ctx.Session()->GetParent(); - auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); + auto& session_handler = session_manager->SessionHandler(); - // Create a session. - Kernel::KClientSession* session{}; - const Result result = parent_port.CreateSession(std::addressof(session), session_manager); - if (result.IsError()) { - LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } + // FIXME: this is duplicated from the SVC, it should just call it instead + // once this is a proper process + + // Reserve a new session from the process resource limit. + Kernel::KScopedResourceReservation session_reservation(&process, + Kernel::LimitableResource::Sessions); + ASSERT(session_reservation.Succeeded()); + + // Create the session. + Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); + ASSERT(session != nullptr); + + // Initialize the session. + session->Initialize(nullptr, parent_session.GetName(), session_manager); + + // Commit the session reservation. + session_reservation.Commit(); + + // Register the session. + session_handler.ClientConnected(&session->GetServerSession()); // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(ResultSuccess); - rb.PushMoveObjects(session); + rb.PushMoveObjects(session->GetClientSession()); } void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { From 983f2b70741f17f30fe2321451f10cabecc013d2 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 16 Oct 2022 01:53:56 -0400 Subject: [PATCH 237/245] kernel: invert session request handling flow --- src/core/hle/ipc_helpers.h | 17 +- src/core/hle/kernel/hle_ipc.cpp | 55 ++--- src/core/hle/kernel/hle_ipc.h | 29 ++- src/core/hle/kernel/k_client_port.cpp | 5 +- src/core/hle/kernel/k_client_port.h | 3 +- src/core/hle/kernel/k_port.cpp | 6 - src/core/hle/kernel/k_server_port.cpp | 5 +- src/core/hle/kernel/k_server_port.h | 19 -- src/core/hle/kernel/k_server_session.cpp | 187 +++++++++++------ src/core/hle/kernel/k_server_session.h | 37 +--- src/core/hle/kernel/k_session.cpp | 5 +- src/core/hle/kernel/k_session.h | 3 +- src/core/hle/kernel/kernel.cpp | 24 ++- src/core/hle/kernel/kernel.h | 9 + src/core/hle/kernel/service_thread.cpp | 238 +++++++++++++++------- src/core/hle/kernel/service_thread.h | 6 +- src/core/hle/kernel/svc.cpp | 6 +- src/core/hle/service/service.cpp | 13 +- src/core/hle/service/service.h | 4 + src/core/hle/service/sm/sm.cpp | 13 +- src/core/hle/service/sm/sm.h | 1 + src/core/hle/service/sm/sm_controller.cpp | 21 +- 22 files changed, 424 insertions(+), 282 deletions(-) diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 18fde8bd6d..3bb1117483 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -86,13 +86,13 @@ public: u32 num_domain_objects{}; const bool always_move_handles{ (static_cast(flags) & static_cast(Flags::AlwaysMoveHandles)) != 0}; - if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { + if (!ctx.GetManager()->IsDomain() || always_move_handles) { num_handles_to_move = num_objects_to_move; } else { num_domain_objects = num_objects_to_move; } - if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { + if (ctx.GetManager()->IsDomain()) { raw_data_size += static_cast(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); ctx.write_size += num_domain_objects; @@ -125,8 +125,7 @@ public: if (!ctx.IsTipc()) { AlignWithPadding(); - if (ctx.Session()->GetSessionRequestManager()->IsDomain() && - ctx.HasDomainMessageHeader()) { + if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); @@ -146,18 +145,18 @@ public: template void PushIpcInterface(std::shared_ptr iface) { - if (context->Session()->GetSessionRequestManager()->IsDomain()) { + if (context->GetManager()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( Kernel::LimitableResource::Sessions, 1); auto* session = Kernel::KSession::Create(kernel); - session->Initialize(nullptr, iface->GetServiceName(), - std::make_shared(kernel)); + session->Initialize(nullptr, iface->GetServiceName()); + iface->RegisterSession(&session->GetServerSession(), + std::make_shared(kernel)); context->AddMoveObject(&session->GetClientSession()); - iface->ClientConnected(&session->GetServerSession()); } } @@ -387,7 +386,7 @@ public: template std::weak_ptr PopIpcInterface() { - ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); + ASSERT(context->GetManager()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler(Pop() - 1); } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e4f43a0537..fd354d4840 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -35,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se } SessionRequestHandler::~SessionRequestHandler() { - kernel.ReleaseServiceThread(service_thread); + kernel.ReleaseServiceThread(service_thread.lock()); +} + +void SessionRequestHandler::AcceptSession(KServerPort* server_port) { + auto* server_session = server_port->AcceptSession(); + ASSERT(server_session != nullptr); + + RegisterSession(server_session, std::make_shared(kernel)); +} + +void SessionRequestHandler::RegisterSession(KServerSession* server_session, + std::shared_ptr manager) { + manager->SetSessionHandler(shared_from_this()); + service_thread.lock()->RegisterServerSession(server_session, manager); + server_session->Close(); } SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} @@ -92,7 +107,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses } // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs - context.SetSessionRequestManager(server_session->GetSessionRequestManager()); + ASSERT(context.GetManager().get() == this); // If there is a DomainMessageHeader, then this is CommandType "Request" const auto& domain_message_header = context.GetDomainMessageHeader(); @@ -130,31 +145,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses return ResultSuccess; } -Result SessionRequestManager::QueueSyncRequest(KSession* parent, - std::shared_ptr&& context) { - // Ensure we have a session request handler - if (this->HasSessionRequestHandler(*context)) { - if (auto strong_ptr = this->GetServiceThread().lock()) { - strong_ptr->QueueSyncRequest(*parent, std::move(context)); - } else { - ASSERT_MSG(false, "strong_ptr is nullptr!"); - } - } else { - ASSERT_MSG(false, "handler is invalid!"); - } - - return ResultSuccess; -} - -void SessionRequestHandler::ClientConnected(KServerSession* session) { - session->GetSessionRequestManager()->SetSessionHandler(shared_from_this()); - - // Ensure our server session is tracked globally. - kernel.RegisterServerObject(session); -} - -void SessionRequestHandler::ClientDisconnected(KServerSession* session) {} - HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, KServerSession* server_session_, KThread* thread_) : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} { @@ -214,7 +204,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 // Padding to align to 16 bytes rp.AlignWithPadding(); - if (Session()->GetSessionRequestManager()->IsDomain() && + if (GetManager()->IsDomain() && ((command_header->type == IPC::CommandType::Request || command_header->type == IPC::CommandType::RequestWithContext) || !incoming)) { @@ -223,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 if (incoming || domain_message_header) { domain_message_header = rp.PopRaw(); } else { - if (Session()->GetSessionRequestManager()->IsDomain()) { + if (GetManager()->IsDomain()) { LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } @@ -316,12 +306,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. - if (server_session->GetSessionRequestManager()->IsDomain()) { + if (GetManager()->IsDomain()) { current_offset = domain_offset - static_cast(outgoing_domain_objects.size()); for (auto& object : outgoing_domain_objects) { - server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); - cmd_buf[current_offset++] = static_cast( - server_session->GetSessionRequestManager()->DomainHandlerCount()); + GetManager()->AppendDomainHandler(std::move(object)); + cmd_buf[current_offset++] = static_cast(GetManager()->DomainHandlerCount()); } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 1083638a9e..67da8e7e10 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -45,11 +45,13 @@ class KAutoObject; class KernelCore; class KEvent; class KHandleTable; +class KServerPort; class KProcess; class KServerSession; class KThread; class KReadableEvent; class KSession; +class SessionRequestManager; class ServiceThread; enum class ThreadWakeupReason; @@ -76,19 +78,9 @@ public: virtual Result HandleSyncRequest(Kernel::KServerSession& session, Kernel::HLERequestContext& context) = 0; - /** - * Signals that a client has just connected to this HLE handler and keeps the - * associated ServerSession alive for the duration of the connection. - * @param server_session Owning pointer to the ServerSession associated with the connection. - */ - void ClientConnected(KServerSession* session); - - /** - * Signals that a client has just disconnected from this HLE handler and releases the - * associated ServerSession. - * @param server_session ServerSession associated with the connection. - */ - void ClientDisconnected(KServerSession* session); + void AcceptSession(KServerPort* server_port); + void RegisterSession(KServerSession* server_session, + std::shared_ptr manager); std::weak_ptr GetServiceThread() const { return service_thread; @@ -170,7 +162,6 @@ public: Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); - Result QueueSyncRequest(KSession* parent, std::shared_ptr&& context); private: bool convert_to_domain{}; @@ -350,11 +341,11 @@ public: template std::shared_ptr GetDomainHandler(std::size_t index) const { - return std::static_pointer_cast(manager.lock()->DomainHandler(index).lock()); + return std::static_pointer_cast(GetManager()->DomainHandler(index).lock()); } void SetSessionRequestManager(std::weak_ptr manager_) { - manager = std::move(manager_); + manager = manager_; } std::string Description() const; @@ -363,6 +354,10 @@ public: return *thread; } + std::shared_ptr GetManager() const { + return manager.lock(); + } + private: friend class IPC::ResponseBuilder; @@ -396,7 +391,7 @@ private: u32 handles_offset{}; u32 domain_offset{}; - std::weak_ptr manager; + std::weak_ptr manager{}; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 3cb22ff4db..eaa2e094c9 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -58,8 +58,7 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -Result KClientPort::CreateSession(KClientSession** out, - std::shared_ptr session_manager) { +Result KClientPort::CreateSession(KClientSession** out) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), LimitableResource::Sessions); @@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out, } // Initialize the session. - session->Initialize(this, parent->GetName(), session_manager); + session->Initialize(this, parent->GetName()); // Commit the session reservation. session_reservation.Commit(); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index e17eff28f8..81046fb860 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -52,8 +52,7 @@ public: void Destroy() override; bool IsSignaled() const override; - Result CreateSession(KClientSession** out, - std::shared_ptr session_manager = nullptr); + Result CreateSession(KClientSession** out); private: std::atomic num_sessions{}; diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index 7a5a9dc2a2..77d00ae2c4 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) { server.EnqueueSession(session); - if (auto session_ptr = server.GetSessionRequestHandler().lock()) { - session_ptr->ClientConnected(server.AcceptSession()); - } else { - ASSERT(false); - } - return ResultSuccess; } diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index e968f26ad4..12e0c3ffb8 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -19,6 +19,8 @@ void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) { // Set member variables. parent = parent_port_; name = std::move(name_); + + kernel.RegisterServerObject(this); } bool KServerPort::IsLight() const { @@ -62,9 +64,6 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); - // Release host emulation members. - session_handler.reset(); - // Ensure that the global list tracking server objects does not hold on to a reference. kernel.UnregisterServerObject(this); } diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index fd4f4bd20e..5fc7ee6832 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -27,24 +27,6 @@ public: void Initialize(KPort* parent_port_, std::string&& name_); - /// Whether or not this server port has an HLE handler available. - bool HasSessionRequestHandler() const { - return !session_handler.expired(); - } - - /// Gets the HLE handler for this port. - SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { - return session_handler; - } - - /** - * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port - * will inherit a reference to this handler. - */ - void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { - session_handler = std::move(handler); - } - void EnqueueSession(KServerSession* pending_session); KServerSession* AcceptSession(); @@ -65,7 +47,6 @@ private: void CleanupSessions(); SessionList session_list; - SessionRequestHandlerWeakPtr session_handler; KPort* parent{}; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index faf03fcc89..aa1941f012 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_) KServerSession::~KServerSession() = default; -void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr manager_) { +void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) { // Set member variables. parent = parent_session_; name = std::move(name_); - manager = manager_; } void KServerSession::Destroy() { @@ -47,18 +45,99 @@ void KServerSession::Destroy() { this->CleanupRequests(); parent->Close(); - - // Release host emulation members. - manager.reset(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } void KServerSession::OnClientClosed() { - if (manager && manager->HasSessionHandler()) { - manager->SessionHandler().ClientDisconnected(this); + KScopedLightLock lk{m_lock}; + + // Handle any pending requests. + KSessionRequest* prev_request = nullptr; + while (true) { + // Declare variables for processing the request. + KSessionRequest* request = nullptr; + KEvent* event = nullptr; + KThread* thread = nullptr; + bool cur_request = false; + bool terminate = false; + + // Get the next request. + { + KScopedSchedulerLock sl{kernel}; + + if (m_current_request != nullptr && m_current_request != prev_request) { + // Set the request, open a reference as we process it. + request = m_current_request; + request->Open(); + cur_request = true; + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + + // If the thread is terminating, handle that. + if (thread->IsTerminationRequested()) { + request->ClearThread(); + request->ClearEvent(); + terminate = true; + } + + prev_request = request; + } else if (!m_request_list.empty()) { + // Pop the request from the front of the list. + request = std::addressof(m_request_list.front()); + m_request_list.pop_front(); + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + } + } + + // If there are no requests, we're done. + if (request == nullptr) { + break; + } + + // All requests must have threads. + ASSERT(thread != nullptr); + + // Ensure that we close the request when done. + SCOPE_EXIT({ request->Close(); }); + + // If we're terminating, close a reference to the thread and event. + if (terminate) { + thread->Close(); + if (event != nullptr) { + event->Close(); + } + } + + // If we need to, reply. + if (event != nullptr && !cur_request) { + // There must be no mappings. + ASSERT(request->GetSendCount() == 0); + ASSERT(request->GetReceiveCount() == 0); + ASSERT(request->GetExchangeCount() == 0); + + // // Get the process and page table. + // KProcess *client_process = thread->GetOwnerProcess(); + // auto &client_pt = client_process->GetPageTable(); + + // // Reply to the request. + // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(), + // ResultSessionClosed); + + // // Unlock the buffer. + // // NOTE: Nintendo does not check the result of this. + // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize()); + + // Signal the event. + event->Signal(); + } } + + // Notify. + this->NotifyAvailable(ResultSessionClosed); } bool KServerSession::IsSignaled() const { @@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const { return !m_request_list.empty() && m_current_request == nullptr; } -Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { - u32* cmd_buf{reinterpret_cast(memory.GetPointer(thread->GetTLSAddress()))}; - auto context = std::make_shared(kernel, memory, this, thread); - - context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); - - return manager->QueueSyncRequest(parent, std::move(context)); -} - -Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { - Result result = manager->CompleteSyncRequest(this, context); - - // The calling thread is waiting for this request to complete, so wake it up. - context.GetThread().EndWait(result); - - return result; -} - Result KServerSession::OnRequest(KSessionRequest* request) { // Create the wait queue. ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; @@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) { // Check that we're not terminating. R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); - if (manager) { - // HLE request. - auto& memory{kernel.System().Memory()}; - this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); - } else { - // Non-HLE request. + // Get whether we're empty. + const bool was_empty = m_request_list.empty(); - // Get whether we're empty. - const bool was_empty = m_request_list.empty(); + // Add the request to the list. + request->Open(); + m_request_list.push_back(*request); - // Add the request to the list. - request->Open(); - m_request_list.push_back(*request); - - // If we were empty, signal. - if (was_empty) { - this->NotifyAvailable(); - } + // If we were empty, signal. + if (was_empty) { + this->NotifyAvailable(); } // If we have a request event, this is asynchronous, and we don't need to wait. @@ -136,7 +189,7 @@ Result KServerSession::OnRequest(KSessionRequest* request) { return GetCurrentThread(kernel).GetWaitResult(); } -Result KServerSession::SendReply() { +Result KServerSession::SendReply(bool is_hle) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -171,13 +224,18 @@ Result KServerSession::SendReply() { Result result = ResultSuccess; if (!closed) { // If we're not closed, send the reply. - Core::Memory::Memory& memory{kernel.System().Memory()}; - KThread* server_thread{GetCurrentThreadPointer(kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + if (is_hle) { + // HLE servers write directly to a pointer to the thread command buffer. Therefore + // the reply has already been written in this case. + } else { + Core::Memory::Memory& memory{kernel.System().Memory()}; + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - auto* dst_msg_buffer = memory.GetPointer(client_message); - std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + auto* dst_msg_buffer = memory.GetPointer(client_message); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } } else { result = ResultSessionClosed; } @@ -223,7 +281,8 @@ Result KServerSession::SendReply() { return result; } -Result KServerSession::ReceiveRequest() { +Result KServerSession::ReceiveRequest(std::shared_ptr* out_context, + std::weak_ptr manager) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() { // Receive the message. Core::Memory::Memory& memory{kernel.System().Memory()}; - KThread* server_thread{GetCurrentThreadPointer(kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + if (out_context != nullptr) { + // HLE request. + u32* cmd_buf{reinterpret_cast(memory.GetPointer(client_message))}; + *out_context = std::make_shared(kernel, memory, this, client_thread); + (*out_context)->SetSessionRequestManager(manager); + (*out_context) + ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), + cmd_buf); + } else { + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(client_message); - auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + auto* src_msg_buffer = memory.GetPointer(client_message); + auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } // We succeeded. return ResultSuccess; diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 188aef4af2..e4698d3f52 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,21 +16,11 @@ #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" -namespace Core::Memory { -class Memory; -} - -namespace Core::Timing { -class CoreTiming; -struct EventType; -} // namespace Core::Timing - namespace Kernel { class HLERequestContext; class KernelCore; class KSession; -class SessionRequestHandler; class SessionRequestManager; class KThread; @@ -46,8 +36,7 @@ public: void Destroy() override; - void Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr manager_); + void Initialize(KSession* parent_session_, std::string&& name_); KSession* GetParent() { return parent; @@ -60,32 +49,16 @@ public: bool IsSignaled() const override; void OnClientClosed(); - /// Gets the session request manager, which forwards requests to the underlying service - std::shared_ptr& GetSessionRequestManager() { - return manager; - } - /// TODO: flesh these out to match the real kernel Result OnRequest(KSessionRequest* request); - Result SendReply(); - Result ReceiveRequest(); + Result SendReply(bool is_hle = false); + Result ReceiveRequest(std::shared_ptr* out_context = nullptr, + std::weak_ptr manager = {}); private: /// Frees up waiting client sessions when this server session is about to die void CleanupRequests(); - /// Queues a sync request from the emulated application. - Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); - - /// Completes a sync request from the emulated application. - Result CompleteSyncRequest(HLERequestContext& context); - - /// This session's HLE request handlers; if nullptr, this is not an HLE server - std::shared_ptr manager; - - /// When set to True, converts the session to a domain at the end of the command - bool convert_to_domain{}; - /// KSession that owns this KServerSession KSession* parent{}; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index ee05aa2825..7a6534ac39 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} KSession::~KSession() = default; -void KSession::Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr manager_) { +void KSession::Initialize(KClientPort* port_, const std::string& name_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed @@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_, KAutoObject::Create(std::addressof(client)); // Initialize our sub sessions. - server.Initialize(this, name_ + ":Server", manager_); + server.Initialize(this, name_ + ":Server"); client.Initialize(this, name_ + ":Client"); // Set state and name. diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index c6ead403b6..93e5e6f71a 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -21,8 +21,7 @@ public: explicit KSession(KernelCore& kernel_); ~KSession() override; - void Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr manager_ = nullptr); + void Initialize(KClientPort* port_, const std::string& name_); void Finalize() override; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index fdc774e307..29e122dfdd 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -60,7 +60,6 @@ struct KernelCore::Impl { global_scheduler_context = std::make_unique(kernel); global_handle_table = std::make_unique(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); - default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; @@ -86,6 +85,8 @@ struct KernelCore::Impl { } RegisterHostThread(); + + default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); } void InitializeCores() { @@ -703,6 +704,15 @@ struct KernelCore::Impl { return port; } + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + auto search = service_interface_handlers.find(name); + if (search == service_interface_handlers.end()) { + return; + } + + search->second(system.ServiceManager(), server_port); + } + void RegisterServerObject(KAutoObject* server_object) { std::scoped_lock lk(server_objects_lock); server_objects.insert(server_object); @@ -715,7 +725,7 @@ struct KernelCore::Impl { std::weak_ptr CreateServiceThread(KernelCore& kernel, const std::string& name) { - auto service_thread = std::make_shared(kernel, 1, name); + auto service_thread = std::make_shared(kernel, name); service_threads_manager.QueueWork( [this, service_thread]() { service_threads.emplace(service_thread); }); @@ -774,6 +784,7 @@ struct KernelCore::Impl { /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map service_interface_factory; + std::unordered_map service_interface_handlers; NamedPortTable named_ports; std::unordered_set server_objects; std::unordered_set registered_objects; @@ -981,10 +992,19 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory& impl->service_interface_factory.emplace(std::move(name), factory); } +void KernelCore::RegisterInterfaceForNamedService(std::string name, + ServiceInterfaceHandlerFn&& handler) { + impl->service_interface_handlers.emplace(std::move(name), handler); +} + KClientPort* KernelCore::CreateNamedServicePort(std::string name) { return impl->CreateNamedServicePort(std::move(name)); } +void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + impl->RegisterNamedServiceHandler(std::move(name), server_port); +} + void KernelCore::RegisterServerObject(KAutoObject* server_object) { impl->RegisterServerObject(server_object); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 266be2bc4f..670f93ee38 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -45,6 +45,7 @@ class KPort; class KProcess; class KResourceLimit; class KScheduler; +class KServerPort; class KServerSession; class KSession; class KSessionRequest; @@ -63,6 +64,8 @@ class TimeManager; using ServiceInterfaceFactory = std::function; +using ServiceInterfaceHandlerFn = std::function; + namespace Init { struct KSlabResourceCounts; } @@ -192,9 +195,15 @@ public: /// Registers a named HLE service, passing a factory used to open a port to that service. void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); + /// Registers a setup function for the named HLE service. + void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler); + /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); + /// Accepts a session on a port created by CreateNamedServicePort. + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port); + /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. /// This is necessary because we do not emulate processes for HLE sessions and ports. void RegisterServerObject(KAutoObject* server_object); diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index d23d767068..1fc2edf521 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -1,15 +1,17 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include #include #include -#include #include "common/scope_exit.h" #include "common/thread.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -19,101 +21,201 @@ namespace Kernel { class ServiceThread::Impl final { public: - explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit Impl(KernelCore& kernel, const std::string& service_name); ~Impl(); - void QueueSyncRequest(KSession& session, std::shared_ptr&& context); + void WaitAndProcessImpl(); + void SessionClosed(KServerSession* server_session, + std::shared_ptr manager); + void LoopProcess(); + + void RegisterServerSession(KServerSession* session, + std::shared_ptr manager); private: - std::vector threads; - std::queue> requests; - std::mutex queue_mutex; - std::condition_variable_any condition; - const std::string service_name; + KernelCore& kernel; + + std::jthread m_thread; + std::mutex m_session_mutex; + std::vector m_sessions; + std::vector> m_managers; + KEvent* m_wakeup_event; + KProcess* m_process; + std::atomic m_shutdown_requested; + const std::string m_service_name; }; -ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : service_name{name} { - for (std::size_t i = 0; i < num_threads; ++i) { - threads.emplace_back([this, &kernel](std::stop_token stop_token) { - Common::SetCurrentThreadName(std::string{service_name}.c_str()); +void ServiceThread::Impl::WaitAndProcessImpl() { + // Create local list of waitable sessions. + std::vector objs; + std::vector> managers; - // Wait for first request before trying to acquire a render context - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); - } + { + // Lock to get the list. + std::scoped_lock lk{m_session_mutex}; - if (stop_token.stop_requested()) { - return; - } + // Resize to the needed quantity. + objs.resize(m_sessions.size() + 1); + managers.resize(m_managers.size()); - // Allocate a dummy guest thread for this host thread. - kernel.RegisterHostThread(); + // Copy to our local list. + std::copy(m_sessions.begin(), m_sessions.end(), objs.begin()); + std::copy(m_managers.begin(), m_managers.end(), managers.begin()); - while (true) { - std::function task; + // Insert the wakeup event at the end. + objs.back() = &m_wakeup_event->GetReadableEvent(); + } - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + // Wait on the list of sessions. + s32 index{-1}; + Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast(objs.size()), -1); + ASSERT(!rc.IsFailure()); - if (stop_token.stop_requested()) { - return; - } + // If this was the wakeup event, clear it and finish. + if (index >= static_cast(objs.size() - 1)) { + m_wakeup_event->Clear(); + return; + } - if (requests.empty()) { - continue; - } + // This event is from a server session. + auto* server_session = static_cast(objs[index]); + auto& manager = managers[index]; - task = std::move(requests.front()); - requests.pop(); - } + // Fetch the HLE request context. + std::shared_ptr context; + rc = server_session->ReceiveRequest(&context, manager); - task(); - } - }); + // If the session was closed, handle that. + if (rc == ResultSessionClosed) { + SessionClosed(server_session, manager); + + // Finish. + return; + } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + + // Perform the request. + Result service_rc = manager->CompleteSyncRequest(server_session, *context); + + // Reply to the client. + rc = server_session->SendReply(true); + + if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { + SessionClosed(server_session, manager); + return; + } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + ASSERT(service_rc == ResultSuccess); +} + +void ServiceThread::Impl::SessionClosed(KServerSession* server_session, + std::shared_ptr manager) { + { + // Lock to get the list. + std::scoped_lock lk{m_session_mutex}; + + // Get the index of the session. + const auto index = + std::find(m_sessions.begin(), m_sessions.end(), server_session) - m_sessions.begin(); + ASSERT(index < static_cast(m_sessions.size())); + + // Remove the session and its manager. + m_sessions.erase(m_sessions.begin() + index); + m_managers.erase(m_managers.begin() + index); + } + + // Close our reference to the server session. + server_session->Close(); +} + +void ServiceThread::Impl::LoopProcess() { + Common::SetCurrentThreadName(m_service_name.c_str()); + + kernel.RegisterHostThread(); + + while (!m_shutdown_requested.load()) { + WaitAndProcessImpl(); } } -void ServiceThread::Impl::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { +void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, + std::shared_ptr manager) { + // Open the server session. + server_session->Open(); + { - std::unique_lock lock{queue_mutex}; + // Lock to get the list. + std::scoped_lock lk{m_session_mutex}; - auto* server_session{&session.GetServerSession()}; - - // Open a reference to the session to ensure it is not closes while the service request - // completes asynchronously. - server_session->Open(); - - requests.emplace([server_session, context{std::move(context)}]() { - // Close the reference. - SCOPE_EXIT({ server_session->Close(); }); - - // Complete the service request. - server_session->CompleteSyncRequest(*context); - }); + // Insert the session and manager. + m_sessions.push_back(server_session); + m_managers.push_back(manager); } - condition.notify_one(); + + // Signal the wakeup event. + m_wakeup_event->Signal(); } ServiceThread::Impl::~Impl() { - condition.notify_all(); - for (auto& thread : threads) { - thread.request_stop(); - thread.join(); + // Shut down the processing thread. + m_shutdown_requested.store(true); + m_wakeup_event->Signal(); + m_thread.join(); + + // Lock mutex. + m_session_mutex.lock(); + + // Close all remaining sessions. + for (size_t i = 0; i < m_sessions.size(); i++) { + m_sessions[i]->Close(); } + + // Close event. + m_wakeup_event->GetReadableEvent().Close(); + m_wakeup_event->Close(); + + // Close process. + m_process->Close(); } -ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : impl{std::make_unique(kernel, num_threads, name)} {} +ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) + : kernel{kernel_}, m_service_name{service_name} { + // Initialize process. + m_process = KProcess::Create(kernel); + KProcess::Initialize(m_process, kernel.System(), service_name, + KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); + + // Reserve a new event from the process resource limit + KScopedResourceReservation event_reservation(m_process, LimitableResource::Events); + ASSERT(event_reservation.Succeeded()); + + // Initialize event. + m_wakeup_event = KEvent::Create(kernel); + m_wakeup_event->Initialize(m_process); + + // Commit the event reservation. + event_reservation.Commit(); + + // Register the event. + KEvent::Register(kernel, m_wakeup_event); + + // Start thread. + m_thread = std::jthread([this] { LoopProcess(); }); +} + +ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) + : impl{std::make_unique(kernel, name)} {} ServiceThread::~ServiceThread() = default; -void ServiceThread::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { - impl->QueueSyncRequest(session, std::move(context)); +void ServiceThread::RegisterServerSession(KServerSession* session, + std::shared_ptr manager) { + impl->RegisterServerSession(session, manager); } } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h index c5896f2bda..fb43255317 100644 --- a/src/core/hle/kernel/service_thread.h +++ b/src/core/hle/kernel/service_thread.h @@ -11,13 +11,15 @@ namespace Kernel { class HLERequestContext; class KernelCore; class KSession; +class SessionRequestManager; class ServiceThread final { public: - explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit ServiceThread(KernelCore& kernel, const std::string& name); ~ServiceThread(); - void QueueSyncRequest(KSession& session, std::shared_ptr&& context); + void RegisterServerSession(KServerSession* session, + std::shared_ptr manager); private: class Impl; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4aca5b27dd..8d2c7d6b72 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -24,6 +24,7 @@ #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" @@ -382,10 +383,11 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; - R_TRY(port->CreateSession(std::addressof(session), - std::make_shared(kernel))); + R_TRY(port->CreateSession(std::addressof(session))); port->Close(); + kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); + // Register the session in the table, close the extra reference. handle_table.Register(*out, session); session->Close(); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5db6588e48..0913a80656 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -99,6 +99,10 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying const auto guard = LockService(); + + if (named_port != nullptr) { + named_port->Close(); + } } void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { @@ -115,13 +119,12 @@ Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { ASSERT(!service_registered); - auto* port = Kernel::KPort::Create(kernel); - port->Initialize(max_sessions, false, service_name); - port->GetServerPort().SetSessionHandler(shared_from_this()); + named_port = Kernel::KPort::Create(kernel); + named_port->Initialize(max_sessions, false, service_name); service_registered = true; - return port->GetClientPort(); + return named_port->GetClientPort(); } void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { @@ -199,7 +202,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, switch (ctx.GetCommandType()) { case IPC::CommandType::Close: case IPC::CommandType::TIPC_Close: { - session.Close(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); result = IPC::ERR_REMOTE_PROCESS_DEAD; @@ -244,6 +246,7 @@ Services::Services(std::shared_ptr& sm, Core::System& system system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); + system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler); Account::InstallInterfaces(system); AM::InstallInterfaces(*sm, *nv_flinger, system); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index ec9deeee43..22e2119d72 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,6 +20,7 @@ class System; namespace Kernel { class HLERequestContext; class KClientPort; +class KPort; class KServerSession; class ServiceThread; } // namespace Kernel @@ -98,6 +99,9 @@ protected: /// Identifier string used to connect to the service. std::string service_name; + /// Port used by ManageNamedPort. + Kernel::KPort* named_port{}; + private: template friend class ServiceFramework; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index cb6c0e96f9..c1f535d714 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -43,6 +43,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core return self.sm_interface->CreatePort(); } +void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) { + self.sm_interface->AcceptSession(server_port); +} + Result ServiceManager::RegisterService(std::string name, u32 max_sessions, Kernel::SessionRequestHandlerPtr handler) { @@ -83,7 +87,6 @@ ResultVal ServiceManager::GetServicePort(const std::string& name port->Initialize(ServerSessionCountMax, false, name); auto handler = it->second; - port->GetServerPort().SetSessionHandler(std::move(handler)); return port; } @@ -144,7 +147,8 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); - if (port_result.Failed()) { + auto service = service_manager.GetService(name); + if (port_result.Failed() || !service) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } @@ -156,12 +160,11 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port->GetClientPort().CreateSession( - std::addressof(session), std::make_shared(kernel)); - result.IsError()) { + if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } + service->AcceptSession(&port->GetServerPort()); LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 878decc6f0..cfe3706526 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -51,6 +51,7 @@ private: class ServiceManager { public: static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); + static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port); explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 46a8439d8e..940c334785 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -15,10 +15,9 @@ namespace Service::SM { void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { - ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), - "Session is already a domain"); + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain"); LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); - ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); + ctx.GetManager()->ConvertToDomainOnRequestEnd(); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -29,30 +28,32 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service, "called"); auto& process = *ctx.GetThread().GetOwnerProcess(); - auto& parent_session = *ctx.Session()->GetParent(); - auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); - auto& session_handler = session_manager->SessionHandler(); + auto session_manager = ctx.GetManager(); // FIXME: this is duplicated from the SVC, it should just call it instead // once this is a proper process + // Declare the session we're going to allocate. + Kernel::KSession* session; + // Reserve a new session from the process resource limit. Kernel::KScopedResourceReservation session_reservation(&process, Kernel::LimitableResource::Sessions); ASSERT(session_reservation.Succeeded()); // Create the session. - Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); + session = Kernel::KSession::Create(system.Kernel()); ASSERT(session != nullptr); // Initialize the session. - session->Initialize(nullptr, parent_session.GetName(), session_manager); + session->Initialize(nullptr, ""); // Commit the session reservation. session_reservation.Commit(); - // Register the session. - session_handler.ClientConnected(&session->GetServerSession()); + // Register with manager. + session_manager->SessionHandler().RegisterSession(&session->GetServerSession(), + session_manager); // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; From 7837185f0ab54f76aa57c65676b10179465fe42d Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 24 Oct 2022 21:23:53 -0400 Subject: [PATCH 238/245] service_thread: convert to map for session management --- src/core/hle/kernel/service_thread.cpp | 44 ++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 1fc2edf521..1d87755045 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -37,8 +38,7 @@ private: std::jthread m_thread; std::mutex m_session_mutex; - std::vector m_sessions; - std::vector> m_managers; + std::map> m_sessions; KEvent* m_wakeup_event; KProcess* m_process; std::atomic m_shutdown_requested; @@ -51,19 +51,21 @@ void ServiceThread::Impl::WaitAndProcessImpl() { std::vector> managers; { - // Lock to get the list. + // Lock to get the set. std::scoped_lock lk{m_session_mutex}; - // Resize to the needed quantity. - objs.resize(m_sessions.size() + 1); - managers.resize(m_managers.size()); + // Reserve the needed quantity. + objs.reserve(m_sessions.size() + 1); + managers.reserve(m_sessions.size()); // Copy to our local list. - std::copy(m_sessions.begin(), m_sessions.end(), objs.begin()); - std::copy(m_managers.begin(), m_managers.end(), managers.begin()); + for (const auto& [session, manager] : m_sessions) { + objs.push_back(session); + managers.push_back(manager); + } // Insert the wakeup event at the end. - objs.back() = &m_wakeup_event->GetReadableEvent(); + objs.push_back(&m_wakeup_event->GetReadableEvent()); } // Wait on the list of sessions. @@ -116,17 +118,11 @@ void ServiceThread::Impl::WaitAndProcessImpl() { void ServiceThread::Impl::SessionClosed(KServerSession* server_session, std::shared_ptr manager) { { - // Lock to get the list. + // Lock to get the set. std::scoped_lock lk{m_session_mutex}; - // Get the index of the session. - const auto index = - std::find(m_sessions.begin(), m_sessions.end(), server_session) - m_sessions.begin(); - ASSERT(index < static_cast(m_sessions.size())); - - // Remove the session and its manager. - m_sessions.erase(m_sessions.begin() + index); - m_managers.erase(m_managers.begin() + index); + // Erase the session. + ASSERT(m_sessions.erase(server_session) == 1); } // Close our reference to the server session. @@ -149,12 +145,11 @@ void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, server_session->Open(); { - // Lock to get the list. + // Lock to get the set. std::scoped_lock lk{m_session_mutex}; // Insert the session and manager. - m_sessions.push_back(server_session); - m_managers.push_back(manager); + m_sessions[server_session] = manager; } // Signal the wakeup event. @@ -171,10 +166,13 @@ ServiceThread::Impl::~Impl() { m_session_mutex.lock(); // Close all remaining sessions. - for (size_t i = 0; i < m_sessions.size(); i++) { - m_sessions[i]->Close(); + for (const auto& [server_session, manager] : m_sessions) { + server_session->Close(); } + // Destroy remaining managers. + m_sessions.clear(); + // Close event. m_wakeup_event->GetReadableEvent().Close(); m_wakeup_event->Close(); From 7aa91c8d9ceb21e631e52c7ac30e47af2ec5a089 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 26 Oct 2022 17:32:14 -0400 Subject: [PATCH 239/245] k_server_session: add SendReplyHLE --- src/core/hle/kernel/k_server_session.h | 4 ++++ src/core/hle/kernel/service_thread.cpp | 2 +- src/core/hle/service/sm/sm_controller.cpp | 5 +---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index e4698d3f52..6e189af8b4 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -55,6 +55,10 @@ public: Result ReceiveRequest(std::shared_ptr* out_context = nullptr, std::weak_ptr manager = {}); + Result SendReplyHLE() { + return SendReply(true); + } + private: /// Frees up waiting client sessions when this server session is about to die void CleanupRequests(); diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 1d87755045..c8fe42537e 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -103,7 +103,7 @@ void ServiceThread::Impl::WaitAndProcessImpl() { Result service_rc = manager->CompleteSyncRequest(server_session, *context); // Reply to the client. - rc = server_session->SendReply(true); + rc = server_session->SendReplyHLE(); if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { SessionClosed(server_session, manager); diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 940c334785..69e0fe8084 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -33,16 +33,13 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // FIXME: this is duplicated from the SVC, it should just call it instead // once this is a proper process - // Declare the session we're going to allocate. - Kernel::KSession* session; - // Reserve a new session from the process resource limit. Kernel::KScopedResourceReservation session_reservation(&process, Kernel::LimitableResource::Sessions); ASSERT(session_reservation.Succeeded()); // Create the session. - session = Kernel::KSession::Create(system.Kernel()); + Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); ASSERT(session != nullptr); // Initialize the session. From 222838332209db34cd70687caaa6997c31de085b Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 19:20:55 -0400 Subject: [PATCH 240/245] kernel: fix port tracking --- src/core/hle/kernel/k_server_port.cpp | 5 ---- src/core/hle/kernel/kernel.cpp | 35 +-------------------------- src/core/hle/kernel/kernel.h | 8 ------ src/core/hle/kernel/svc.cpp | 1 - src/core/hle/service/service.cpp | 4 ++- 5 files changed, 4 insertions(+), 49 deletions(-) diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 12e0c3ffb8..16968ba976 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -19,8 +19,6 @@ void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) { // Set member variables. parent = parent_port_; name = std::move(name_); - - kernel.RegisterServerObject(this); } bool KServerPort::IsLight() const { @@ -63,9 +61,6 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } bool KServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 29e122dfdd..054898638e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -185,17 +185,6 @@ struct KernelCore::Impl { } void CloseServices() { - // Close all open server sessions and ports. - std::unordered_set server_objects_; - { - std::scoped_lock lk(server_objects_lock); - server_objects_ = server_objects; - server_objects.clear(); - } - for (auto* server_object : server_objects_) { - server_object->Close(); - } - // Ensures all service threads gracefully shutdown. ClearServiceThreads(); } @@ -699,9 +688,7 @@ struct KernelCore::Impl { return {}; } - KClientPort* port = &search->second(system.ServiceManager(), system); - RegisterServerObject(&port->GetParent()->GetServerPort()); - return port; + return &search->second(system.ServiceManager(), system); } void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { @@ -713,16 +700,6 @@ struct KernelCore::Impl { search->second(system.ServiceManager(), server_port); } - void RegisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.insert(server_object); - } - - void UnregisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.erase(server_object); - } - std::weak_ptr CreateServiceThread(KernelCore& kernel, const std::string& name) { auto service_thread = std::make_shared(kernel, name); @@ -755,7 +732,6 @@ struct KernelCore::Impl { service_thread_barrier.Sync(); } - std::mutex server_objects_lock; std::mutex registered_objects_lock; std::mutex registered_in_use_objects_lock; @@ -786,7 +762,6 @@ struct KernelCore::Impl { std::unordered_map service_interface_factory; std::unordered_map service_interface_handlers; NamedPortTable named_ports; - std::unordered_set server_objects; std::unordered_set registered_objects; std::unordered_set registered_in_use_objects; @@ -1005,14 +980,6 @@ void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* serv impl->RegisterNamedServiceHandler(std::move(name), server_port); } -void KernelCore::RegisterServerObject(KAutoObject* server_object) { - impl->RegisterServerObject(server_object); -} - -void KernelCore::UnregisterServerObject(KAutoObject* server_object) { - impl->UnregisterServerObject(server_object); -} - void KernelCore::RegisterKernelObject(KAutoObject* object) { std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.insert(object); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 670f93ee38..4ae6b39236 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -204,14 +204,6 @@ public: /// Accepts a session on a port created by CreateNamedServicePort. void RegisterNamedServiceHandler(std::string name, KServerPort* server_port); - /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. - /// This is necessary because we do not emulate processes for HLE sessions and ports. - void RegisterServerObject(KAutoObject* server_object); - - /// Unregisters a server session or port previously registered with RegisterServerSession when - /// it was destroyed during the current emulation session. - void UnregisterServerObject(KAutoObject* server_object); - /// Registers all kernel objects with the global emulation state, this is purely for tracking /// leaks after emulation has been shutdown. void RegisterKernelObject(KAutoObject* object); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8d2c7d6b72..4c819f4b66 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -384,7 +384,6 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; R_TRY(port->CreateSession(std::addressof(session))); - port->Close(); kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0913a80656..6a64c60057 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -101,7 +101,9 @@ ServiceFrameworkBase::~ServiceFrameworkBase() { const auto guard = LockService(); if (named_port != nullptr) { - named_port->Close(); + named_port->GetClientPort().Close(); + named_port->GetServerPort().Close(); + named_port = nullptr; } } From 633411c20f9759a9a5f1a3bf15466b387dce9a5a Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 20:02:33 -0400 Subject: [PATCH 241/245] kernel: fix single core for service threads --- src/core/hle/kernel/kernel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 054898638e..09c36ee09d 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -336,6 +336,8 @@ struct KernelCore::Impl { return this_id; } + static inline thread_local bool is_phantom_mode_for_singlecore{false}; + bool IsPhantomModeForSingleCore() const { return is_phantom_mode_for_singlecore; } @@ -800,7 +802,6 @@ struct KernelCore::Impl { bool is_multicore{}; std::atomic_bool is_shutting_down{}; - bool is_phantom_mode_for_singlecore{}; u32 single_core_thread_id{}; std::array svc_ticks{}; From 77b74f5d95626422f59b508cd2b355135ed256ca Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 22:22:14 -0400 Subject: [PATCH 242/245] sm:: avoid excessive port recreation --- src/core/hle/service/service.cpp | 10 ++++++---- src/core/hle/service/sm/sm.cpp | 31 +++++++++++++++++-------------- src/core/hle/service/sm/sm.h | 1 + 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 6a64c60057..5ab41c0c41 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -119,12 +119,14 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); - ASSERT(!service_registered); + if (named_port == nullptr) { + ASSERT(!service_registered); - named_port = Kernel::KPort::Create(kernel); - named_port->Initialize(max_sessions, false, service_name); + named_port = Kernel::KPort::Create(kernel); + named_port->Initialize(max_sessions, false, service_name); - service_registered = true; + service_registered = true; + } return named_port->GetClientPort(); } diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index c1f535d714..84720094ff 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6); constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} -ServiceManager::~ServiceManager() = default; + +ServiceManager::~ServiceManager() { + for (auto& [name, port] : service_ports) { + port->GetClientPort().Close(); + port->GetServerPort().Close(); + } +} void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); @@ -57,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions, return ERR_ALREADY_REGISTERED; } - registered_services.emplace(std::move(name), handler); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, false, name); + + service_ports.emplace(name, port); + registered_services.emplace(name, handler); return ResultSuccess; } @@ -72,23 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) { } registered_services.erase(iter); + service_ports.erase(name); + return ResultSuccess; } ResultVal ServiceManager::GetServicePort(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); - auto it = registered_services.find(name); - if (it == registered_services.end()) { + auto it = service_ports.find(name); + if (it == service_ports.end()) { LOG_ERROR(Service_SM, "Server is not registered! service={}", name); return ERR_SERVICE_NOT_REGISTERED; } - auto* port = Kernel::KPort::Create(kernel); - - port->Initialize(ServerSessionCountMax, false, name); - auto handler = it->second; - - return port; + return it->second; } /** @@ -153,10 +160,6 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& return port_result.Code(); } auto& port = port_result.Unwrap(); - SCOPE_EXIT({ - port->GetClientPort().Close(); - port->GetServerPort().Close(); - }); // Create a new session. Kernel::KClientSession* session{}; diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index cfe3706526..02a5dde9ea 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -79,6 +79,7 @@ private: /// Map of registered services, retrieved using GetServicePort. std::unordered_map registered_services; + std::unordered_map service_ports; /// Kernel context Kernel::KernelCore& kernel; From 5e4ab2d42c4e45dfce9536940f9cc3dd0b9ddf4a Mon Sep 17 00:00:00 2001 From: Kyle Kienapfel Date: Wed, 2 Nov 2022 02:22:18 -0700 Subject: [PATCH 243/245] Manually import Ukrainian language files I'm not sure if GillianMC and I can claim that yuzu is first emulator translated to Ukrainian until the language files are used in builds. --- dist/languages/uk.ts | 7321 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 7321 insertions(+) create mode 100644 dist/languages/uk.ts diff --git a/dist/languages/uk.ts b/dist/languages/uk.ts new file mode 100644 index 0000000000..66a3ac96e5 --- /dev/null +++ b/dist/languages/uk.ts @@ -0,0 +1,7321 @@ + + + AboutDialog + + + About yuzu + Про yuzu + + + + <html><head/><body><p><span style=" font-size:28pt;">yuzu</span></p></body></html> + <html><head/><body><p><span style=" font-size:28pt;">yuzu</span></p></body></html> + + + + <html><head/><body><p>%1 (%2)</p></body></html> + <html><head/><body><p>%1 (%2)</p></body></html> + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu є експериментальним емулятором Nintendo Switch з відкритим кодом ліцензований під GPLv3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Це програмне забезпечення не слід використовувати для ігор, які ви отримали незаконним шляхом.</span></p></body></html> + + + + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Веб-сайт</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Вихідний код</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Вкладники</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Ліцензія</span></a></p></body></html> + + + + <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.</span></p></body></html> + <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; є торговою маркою Nintendo. yuzu не пов'язаний з Nintendo у будь-якому вигляді.</span></p></body></html> + + + + CalibrationConfigurationDialog + + + Communicating with the server... + Зв'язок із сервером... + + + + Cancel + Скасувати + + + + Touch the top left corner <br>of your touchpad. + Торкніться верхнього лівого кута <br> вашого тачпаду. + + + + Now touch the bottom right corner <br>of your touchpad. + Тепер торкніться правого нижнього кута <br> вашого тачпаду. + + + + Configuration completed! + Налаштування завершено! + + + + OK + ОК + + + + ChatRoom + + + Room Window + Вікно кімнати + + + + Send Chat Message + Надіслати повідомлення в чат + + + + Send Message + Надіслати повідомлення + + + + Members + Члени + + + + %1 has joined + %1 приєднався + + + + %1 has left + %1 вийшов + + + + %1 has been kicked + %1 вигнано + + + + %1 has been banned + %1 заблоковано + + + + %1 has been unbanned + %1 розблоковано + + + + View Profile + Переглянути профіль + + + + + Block Player + Заблокувати гравця + + + + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Коли ви блокуєте гравця, ви більше не отримуватиме від нього повідомлення у чаті. <br><br>Ви впевнені що бажаєте заблокувати %1? + + + + Kick + Вигнати + + + + Ban + Заблокувати + + + + Kick Player + Вигнати гравця + + + + Are you sure you would like to <b>kick</b> %1? + Ви впевнені що бажаєте <b>вигнати</b> %1? + + + + Ban Player + Заблокувати гравця + + + + Are you sure you would like to <b>kick and ban</b> %1? + +This would ban both their forum username and their IP address. + Ви впевнені що бажаєте <b>вигнати і заблокувати</b> %1? + +Ця дія заблокує ім'я користувача на форумі та IP-адресу. + + + + ClientRoom + + + Room Window + Вікно кімнати + + + + Room Description + Опис кімнати + + + + Moderation... + Модерація... + + + + Leave Room + Залишити кімнату + + + + ClientRoomWindow + + + Connected + З'єднано + + + + Disconnected + Роз'єднано + + + + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 члени) - з'єднано + + + + CompatDB + + + Report Compatibility + Повідомити про сумісність + + + + + Report Game Compatibility + Повідомити про сумісність гри + + + + <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of yuzu you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected yuzu account</li></ul></body></html> + <html><head/><body><p><span style=" font-size:10pt;">Якщо ви бажаєте надіслати звіт до </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">списку сумісності yuzu</span></a><span style=" font-size:10pt;">, наступна інформація буде зібрана та відображена на сайті:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Інформація про залізо (ЦП / ГП / Операційна система)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Версія yuzu</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Підключений акаунт yuzu</li></ul></body></html> + + + + Perfect + Ідеально + + + + <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> + <html><head/><body><p>Гра працює ідеально, без звукових чи графічних артефактів.</p></body></html> + + + + Great + Чудово + + + + <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> + <html><head/><body><p>Гра працює з невеликими графічними або звуковими артефактами та може бути пройдена від початку до кінця. Можуть знадобитися обхідні шляхи.</p></body></html> + + + + Okay + Добре + + + + <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> + <html><head/><body><p>Гра працює зі суттєвими графічними або звуковими артефактами, але з обхідними шляхами може бути пройдена.</p></body></html> + + + + Bad + Погано + + + + <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> + <html><head/><body><p>Гра працює, але з суттєвими графічними чи звуковими артефактами. У деяких місцях неможливо пройти навіть із обхідними шляхами.</p></body></html> + + + + Intro/Menu + Вступ/Меню + + + + <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> + <html><head/><body><p>У гру неможливо грати через серйозні графічні або звукові артефакти. Неможливо просунутися далі за стартове меню.</p></body></html> + + + + Won't Boot + Не запускається + + + + <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> + <html><head/><body><p>Гра вилітає при спробі запуску.</p></body></html> + + + + <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> + <html><head/><body><p>Незалежно від швидкості або продуктивності, наскільки добре ця гра працює від початку до кінця у поточній версії yuzu?</p></body></html> + + + + Thank you for your submission! + Дякуємо за ваш звіт! + + + + Submitting + Надсилання + + + + Communication error + Помилка з'єднання + + + + An error occurred while sending the Testcase + Сталася помилка під час надсилання звіту + + + + Next + Далі + + + + ConfigureAudio + + + + Audio + Аудіо + + + + Output Engine: + Рушій виводу: + + + + Output Device + Пристрій виводу + + + + Input Device + Пристрій вводу + + + + Use global volume + Використовувати загальну гучність + + + + Set volume: + Встановити гучність: + + + + Volume: + Гучність + + + + 0 % + 0 % + + + + %1% + Volume percentage (e.g. 50%) + %1% + + + + ConfigureCamera + + + Configure Infrared Camera + Налаштування інфрачервоної камери + + + + Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. + Виберіть, звідки береться зображення емульованої камери. Це може бути віртуальна або реальна камера. + + + + Camera Image Source: + Джерело зображення камери: + + + + Input device: + Пристрій вводу: + + + + Preview + Попередній перегляд + + + + Resolution: 320*240 + Роздільна здатність: 320*240 + + + + Click to preview + Натисніть для попереднього перегляду + + + + Restore Defaults + Значення за замовчуванням + + + + Auto + Авто + + + + ConfigureCpu + + + Form + Форма + + + + CPU + ЦП + + + + General + Загальні + + + + Accuracy: + Точність: + + + + Auto + Авто + + + + Accurate + Точно + + + + Unsafe + Небезпечно + + + + Paranoid (disables most optimizations) + Параноїк (відключає більшість оптимізацій) + + + + We recommend setting accuracy to "Auto". + Ми рекомендуємо встановити точність на "Авто". + + + + Unsafe CPU Optimization Settings + Небезпечні налаштування оптимізації ЦП + + + + These settings reduce accuracy for speed. + Ці налаштування зменшують точність заради швидкості. + + + + + <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> + + + + + + Unfuse FMA (improve performance on CPUs without FMA) + Не використовувати FMA (покращує продуктивність на ЦП без FMA) + + + + + <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> + + + + + + Faster FRSQRTE and FRECPE + Прискорені FRSQRTE та FRECPE + + + + + <div>This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.</div> + + + + + + Faster ASIMD instructions (32 bits only) + Швидші інструкції ASIMD (лише 32 біт) + + + + + <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> + + + + + + Inaccurate NaN handling + Неправильна обробка NaN + + + + + <div>This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.</div> + + + + + + Disable address space checks + + + + + + <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> + + + + + + Ignore global monitor + + + + + CPU settings are available only when game is not running. + Налаштування ЦП недоступні, поки запущена гра. + + + + ConfigureCpuDebug + + + Form + Форма + + + + CPU + ЦП + + + + Toggle CPU Optimizations + Увімкнути оптимізації ЦП + + + + <html><head/><body><p><span style=" font-weight:600;">For debugging only.</span><br/>If you're not sure what these do, keep all of these enabled. <br/>These settings, when disabled, only take effect when CPU Debugging is enabled. </p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Тільки для налагодження.</span><br/>Якщо ви не впевнені в тому, що вони роблять, залиште всі ці параметри увімкненими. <br/>Коли їх вимкнено, ці параметри набувають чинності лише за увімкненого налагодження ЦП. </p></body></html> + + + + + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> + + + + + + Enable inline page tables + + + + + + <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> + + + + + + Enable block linking + + + + + + <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> + + + + + + Enable return stack buffer + + + + + + <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> + + + + + + Enable fast dispatcher + + + + + + <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> + + + + + + Enable context elimination + + + + + + <div>Enables IR optimizations that involve constant propagation.</div> + + + + + + Enable constant propagation + + + + + + <div>Enables miscellaneous IR optimizations.</div> + + + + + + Enable miscellaneous optimizations + Увімкнути різні оптимізації + + + + + <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> + <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> + + + + + + Enable misalignment check reduction + + + + + + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> + + + + + + Enable Host MMU Emulation (general memory instructions) + + + + + + <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> + <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> + + + + + + Enable Host MMU Emulation (exclusive memory instructions) + + + + + + <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> + + + + + + Enable recompilation of exclusive memory instructions + + + + + CPU settings are available only when game is not running. + Налаштування ЦП доступні тільки тоді, коли гру не запущено. + + + + ConfigureDebug + + + Debugger + Налагоджувач + + + + Enable GDB Stub + + + + + Port: + Порт: + + + + Logging + Журналювання + + + + Global Log Filter + Глобальний фільтр журналів + + + + Show Log in Console + Показувати журнал у консолі + + + + Open Log Location + Відкрити папку для журналів + + + + When checked, the max size of the log increases from 100 MB to 1 GB + Якщо увімкнено, максимальний розмір журналу збільшується зі 100 МБ до 1 ГБ + + + + Enable Extended Logging** + Увімкнути розширене ведення журналу** + + + + Homebrew + Homebrew + + + + Arguments String + Рядок аргументів + + + + Graphics + Графіка + + + + When checked, the graphics API enters a slower debugging mode + Якщо увімкнено, графічний API переходить у повільніший режим налагодження + + + + Enable Graphics Debugging + Увімкнути налагодження графіки + + + + When checked, it enables Nsight Aftermath crash dumps + + + + + Enable Nsight Aftermath + + + + + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found + Якщо ввімкнено, буде дампити всі оригінальні шейдери асемблера з кешу шейдерів на диску або гри як знайдені + + + + Dump Game Shaders + Дамп ігрових шейдерів + + + + When checked, it will dump all the macro programs of the GPU + + + + + Dump Maxwell Macros + + + + + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower + + + + + Disable Macro JIT + + + + + When checked, yuzu will log statistics about the compiled pipeline cache + Якщо увімкнено, yuzu записуватиме статистику про скомпільований кеш конвеєра + + + + Enable Shader Feedback + Увімкнути зворотний зв'язок про шейдери + + + + When checked, it executes shaders without loop logic changes + + + + + Disable Loop safety checks + + + + + Debugging + Налагодження + + + + Enable Verbose Reporting Services** + + + + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + + Advanced + Розширені + + + + Kiosk (Quest) Mode + Режим кіоску (Квест) + + + + Enable CPU Debugging + Увімкнути налагодження ЦП + + + + Enable Debug Asserts + + + + + Enable Auto-Stub** + + + + + Enable All Controller Types + Увімкнути всі типи контролерів + + + + Disable Web Applet + Вимкнути веб-аплет + + + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Дозволяє yuzu перевіряти наявність робочого середовища Vulkan під час запуску програми. Вимкніть цю опцію, якщо це викликає проблеми з тим, що зовнішні програми бачать yuzu. + + + + Perform Startup Vulkan Check + Виконувати перевірку Vulkan під час запуску + + + + **This will be reset automatically when yuzu closes. + **Це буде автоматично скинуто після закриття yuzu. + + + + Restart Required + Потрібен перезапуск + + + + yuzu is required to restart in order to apply this setting. + yuzu потрібно перезапустити, щоб застосувати це налаштування. + + + + Web applet not compiled + Веб-аплет не скомпільовано + + + + MiniDump creation not compiled + + + + + ConfigureDebugController + + + Configure Debug Controller + Налаштування налагоджувального контролера + + + + Clear + Очистити + + + + Defaults + За замовчуванням + + + + ConfigureDebugTab + + + Form + Форма + + + + + Debug + Налагодження + + + + CPU + ЦП + + + + ConfigureDialog + + + yuzu Configuration + Налаштування yuzu + + + + + Audio + Аудіо + + + + + CPU + ЦП + + + + Debug + Налагодження + + + + Filesystem + Файлова система + + + + + General + Загальні + + + + + Graphics + Графіка + + + + GraphicsAdvanced + ГрафікаРозширені + + + + Hotkeys + Гарячі клавіші + + + + + Controls + Керування + + + + Profiles + Профілі + + + + Network + Мережа + + + + + System + Система + + + + Game List + Список ігор + + + + Web + Мережа + + + + ConfigureFilesystem + + + Form + Форма + + + + Filesystem + Файлова система + + + + Storage Directories + Каталоги зберігання + + + + NAND + NAND + + + + + + + + ... + ... + + + + SD Card + SD карта + + + + Gamecard + Картридж + + + + Path + Шлях + + + + Inserted + Вставлений + + + + Current Game + Поточна гра + + + + Patch Manager + Керування патчами + + + + Dump Decompressed NSOs + Дамп розпакованих NSO + + + + Dump ExeFS + Дамп ExeFS + + + + Mod Load Root + Папка з модами + + + + Dump Root + Корінь дампу + + + + Caching + Кешування + + + + Cache Game List Metadata + Кешувати метадані списку ігор + + + + + + + Reset Metadata Cache + Скинути кеш метаданих + + + + Select Emulated NAND Directory... + Виберіть папку для емульованого NAND... + + + + Select Emulated SD Directory... + Виберіть папку для емульованого SD... + + + + Select Gamecard Path... + Оберіть папку для картриджів... + + + + Select Dump Directory... + Оберіть папку для дампів... + + + + Select Mod Load Directory... + Оберіть папку для модів... + + + + The metadata cache is already empty. + Кеш метаданих вже порожній. + + + + The operation completed successfully. + Операція завершилася успішно. + + + + The metadata cache couldn't be deleted. It might be in use or non-existent. + Кеш метаданих не можна видалити. Можливо, він використовується або відсутній. + + + + ConfigureGeneral + + + Form + Форма + + + + + General + Загальні + + + + Limit Speed Percent + Обмеження відсотка швидкості + + + + % + % + + + + Multicore CPU Emulation + Багатоядерна емуляція ЦП + + + + Extended memory layout (6GB DRAM) + Розширене компонування пам'яті (6 ГБ DRAM) + + + + Confirm exit while emulation is running + Підтверджувати вихід під час емуляції + + + + Prompt for user on game boot + Запитувати користувача під час запуску гри + + + + Pause emulation when in background + Призупиняти емуляцію у фоновому режимі + + + + Mute audio when in background + Приглушити звук у фоновому режимі + + + + Hide mouse on inactivity + Приховування миші при бездіяльності + + + + Reset All Settings + Скинути всі налаштування + + + + yuzu + yuzu + + + + This reset all settings and remove all per-game configurations. This will not delete game directories, profiles, or input profiles. Proceed? + Це скине всі налаштування і видалить усі конфігурації під окремі ігри. При цьому не будуть видалені шляхи до ігор, профілів або профілів вводу. Продовжити? + + + + ConfigureGraphics + + + Form + Форма + + + + Graphics + Графіка + + + + API Settings + Налаштування API + + + + Shader Backend: + Бекенд шейдерів: + + + + Device: + Пристрій: + + + + API: + API: + + + + Graphics Settings + Налаштування графіки + + + + Use disk pipeline cache + Використовувати кеш конвеєра на диску + + + + Use asynchronous GPU emulation + Використовувати асинхронну емуляцію ГП + + + + Accelerate ASTC texture decoding + Прискорення декодування текстур ASTC + + + + NVDEC emulation: + Емуляція NVDEC: + + + + No Video Output + Відсутність відеовиходу + + + + CPU Video Decoding + Декодування відео на ЦП + + + + GPU Video Decoding (Default) + Декодування відео на ГП (за замовчуванням) + + + + Fullscreen Mode: + Повноекранний режим: + + + + Borderless Windowed + Вікно без рамок + + + + Exclusive Fullscreen + Ексклюзивний повноекранний + + + + Aspect Ratio: + Співвідношення сторін: + + + + Default (16:9) + За замовчуванням (16:9) + + + + Force 4:3 + Змусити 4:3 + + + + Force 21:9 + Змусити 21:9 + + + + Force 16:10 + Змусити 16:10 + + + + Stretch to Window + Розтягнути до вікна + + + + Resolution: + Роздільна здатність: + + + + 0.5X (360p/540p) [EXPERIMENTAL] + 0.5X (360p/540p) [ЕКСПЕРИМЕНТАЛЬНЕ] + + + + 0.75X (540p/810p) [EXPERIMENTAL] + 0.75X (540p/810p) [ЕКСПЕРИМЕНТАЛЬНЕ] + + + + 1X (720p/1080p) + 1X (720p/1080p) + + + + 2X (1440p/2160p) + 2X (1440p/2160p) + + + + 3X (2160p/3240p) + 3X (2160p/3240p) + + + + 4X (2880p/4320p) + 4X (2880p/4320p) + + + + 5X (3600p/5400p) + 5X (3600p/5400p) + + + + 6X (4320p/6480p) + 6X (4320p/6480p) + + + + Window Adapting Filter: + Фільтр адаптації вікна: + + + + Nearest Neighbor + Найближчий сусід + + + + Bilinear + Білінійне + + + + Bicubic + Бікубічне + + + + Gaussian + Гауса + + + + ScaleForce + ScaleForce + + + + AMD FidelityFX™️ Super Resolution (Vulkan Only) + AMD FidelityFX™️ Super Resolution (Лише для Vulkan) + + + + Anti-Aliasing Method: + Метод згладжування: + + + + None + Вимкнено + + + + FXAA + FXAA + + + + + Use global background color + Використовувати глобальний фоновий колір + + + + Set background color: + Встановити фоновий колір: + + + + Background Color: + Фоновий колір: + + + + GLASM (Assembly Shaders, NVIDIA Only) + GLASM (асемблерні шейдери, лише для NVIDIA) + + + + ConfigureGraphicsAdvanced + + + Form + Форма + + + + Advanced + Розширені + + + + Advanced Graphics Settings + Розширені налаштування графіки + + + + Accuracy Level: + Рівень точності: + + + + VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference. + Вертикальна синхронізація запобігає розривам екрана, але деякі відеокарти мають нижчу продуктивність при вертикальній синхронізації. Залишайте увімкненим, якщо ви не помічаєте різниці в продуктивності. + + + + Use VSync + Використувати вертикальну сінхронізацію + + + + Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental. + Вмикає асинхронну компіляцію шейдерів, що зменшить зависання через шейдери. Функція є експериментальною. + + + + Use asynchronous shader building (Hack) + Використовувати асинхронну побудову шейдерів (хак) + + + + Enables Fast GPU Time. This option will force most games to run at their highest native resolution. + Вмикає функцію Fast GPU Time. Цей параметр змусить більшість ігор працювати в максимальній рідній роздільній здатності. + + + + Use Fast GPU Time (Hack) + Увімкнути Fast GPU Time (Хак) + + + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Вмикає песимістичне очищення буферів. Ця опція змушує промивати немодифіковані буфери, що може знизити продуктивність. + + + + Use pessimistic buffer flushes (Hack) + Використовувати песимістичне очищення буферів (Хак) + + + + Anisotropic Filtering: + Анізотропна фільтрація: + + + + Automatic + Автоматично + + + + Default + За замовчуванням + + + + 2x + 2x + + + + 4x + 4x + + + + 8x + 8x + + + + 16x + 16x + + + + ConfigureHotkeys + + + Hotkey Settings + Налаштування гарячих клавіш + + + + Hotkeys + Гарячі клавіші + + + + Double-click on a binding to change it. + Натисніть двічі на прив'язці, щоб змінити її. + + + + Clear All + Очистити все + + + + Restore Defaults + Відновити значення за замовчуванням. + + + + Action + Дія + + + + Hotkey + Гаряча клавіша + + + + Controller Hotkey + Гаряча клавіша контролера + + + + + + Conflicting Key Sequence + Конфліктуюча комбінація клавіш + + + + + The entered key sequence is already assigned to: %1 + Введена комбінація вже призначена до: %1 + + + + Home+%1 + Home+%1 + + + + [waiting] + [очікування] + + + + Invalid + Неприпустимо + + + + Restore Default + Відновити значення за замовчуванням + + + + Clear + Очистити + + + + Conflicting Button Sequence + Конфліктуюче поєднання кнопок + + + + The default button sequence is already assigned to: %1 + Типова комбінація кнопок вже призначена до: %1 + + + + The default key sequence is already assigned to: %1 + Типова комбінація клавіш вже призначена до: %1 + + + + ConfigureInput + + + ConfigureInput + НалаштуванняВводу + + + + + Player 1 + Гравець 1 + + + + + Player 2 + Гравець 2 + + + + + Player 3 + Гравець 3 + + + + + Player 4 + Гравець 4 + + + + + Player 5 + Гравець 5 + + + + + Player 6 + Гравець 6 + + + + + Player 7 + Гравець 7 + + + + + Player 8 + Гравець 8 + + + + + Advanced + Розширені + + + + Console Mode + Режим консолі + + + + Docked + У док-станції + + + + Handheld + Портативний + + + + Vibration + Вібрація + + + + + Configure + Налаштувати + + + + Motion + Рух + + + + Controllers + Контролери + + + + 1 + 1 + + + + 2 + 2 + + + + 3 + 3 + + + + 4 + 4 + + + + 5 + 5 + + + + 6 + 6 + + + + 7 + 7 + + + + 8 + 8 + + + + Connected + З'єднано + + + + Defaults + За замовчуванням + + + + Clear + Очистити + + + + ConfigureInputAdvanced + + + Configure Input + Налаштування вводу + + + + Joycon Colors + Кольори Joy-Con'ів + + + + Player 1 + Гравець 1 + + + + + + + + + + + L Body + Лівий контролер + + + + + + + + + + + L Button + Кнопка L + + + + + + + + + + + R Body + Правий контролер + + + + + + + + + + + R Button + Кнопка R + + + + Player 2 + Гравець 2 + + + + Player 3 + Гравець 3 + + + + Player 4 + Гравець 4 + + + + Player 5 + Гравець 5 + + + + Player 6 + Гравець 6 + + + + Player 7 + Гравець 7 + + + + Player 8 + Гравець 8 + + + + Emulated Devices + Емульовані пристрої + + + + Keyboard + Клавіатура + + + + Mouse + Миша + + + + Touchscreen + Сенсорний екран + + + + Advanced + Розширені + + + + Debug Controller + Налагоджувальний контролер + + + + + + + Configure + Налаштувати + + + + Ring Controller + Контролер Ring + + + + Infrared Camera + Інфрачервона камера + + + + Other + Інше + + + + Emulate Analog with Keyboard Input + Емуляція аналогового вводу з клавіатури + + + + Requires restarting yuzu + Потребує перезапуску yuzu + + + + Enable XInput 8 player support (disables web applet) + Увімкнути підтримку 8-ми гравців на XInput (відключає веб-аплет) + + + + Enable UDP controllers (not needed for motion) + Увімкнути UDP контролери (не обов'язково для руху) + + + + Controller navigation + Навігація контролера + + + + Enable mouse panning + Увімкнути панорамування миші + + + + Mouse sensitivity + Чутливість миші + + + + % + % + + + + Motion / Touch + Рух і сенсор + + + + ConfigureInputPlayer + + + Configure Input + Налаштування вводу + + + + Connect Controller + Підключити контролер + + + + Input Device + Пристрій вводу + + + + Profile + Профіль + + + + Save + Зберегти + + + + New + Новий + + + + Delete + Видалити + + + + + Left Stick + Лівий міні-джойстик + + + + + + + + + Up + Вгору + + + + + + + + + + Left + Вліво + + + + + + + + + + Right + Вправо + + + + + + + + + Down + Вниз + + + + + + + Pressed + Натиснення + + + + + + + Modifier + Модифікатор + + + + + Range + Діапазон + + + + + % + % + + + + + Deadzone: 0% + Мертва зона: 0% + + + + + Modifier Range: 0% + Діапазон модифікатора: 0% + + + + D-Pad + Кнопки напрямків + + + + + + L + L + + + + + + ZL + ZL + + + + + Minus + Мінус + + + + + Capture + Захоплення + + + + + + Plus + Плюс + + + + + Home + Home + + + + + + + R + R + + + + + + ZR + ZR + + + + + SL + SL + + + + + SR + SR + + + + Motion 1 + Рух 1 + + + + Motion 2 + Рух 2 + + + + Face Buttons + Кнопки A/B/X/Y + + + + + X + X + + + + + Y + Y + + + + + A + A + + + + + B + B + + + + + Right Stick + Правий міні-джойстик + + + + + + + Clear + Очистити + + + + + + + + [not set] + [не задано] + + + + + Invert button + Інвертувати кнопку + + + + + Toggle button + Переключити кнопку + + + + + Invert axis + Інвертувати осі + + + + + + Set threshold + Встановити поріг + + + + + Choose a value between 0% and 100% + Оберіть значення між 0% і 100% + + + + Toggle axis + Переключити осі + + + + Set gyro threshold + Встановити поріг гіроскопа + + + + Map Analog Stick + Задати аналоговий міні-джойстик + + + + After pressing OK, first move your joystick horizontally, and then vertically. +To invert the axes, first move your joystick vertically, and then horizontally. + Після натискання на ОК, рухайте ваш міні-джойстик горизонтально, а потім вертикально. +Щоб інвертувати осі, спочатку рухайте ваш міні-джойстик вертикально, а потім горизонтально. + + + + Center axis + Центрувати осі + + + + + Deadzone: %1% + Мертва зона: %1% + + + + + Modifier Range: %1% + Діапазон модифікатора: %1% + + + + + Pro Controller + Контролер Pro + + + + Dual Joycons + Подвійні Joy-Con'и + + + + Left Joycon + Лівий Joy-Con + + + + Right Joycon + Правий Joy-Con + + + + Handheld + Портативний + + + + GameCube Controller + Контролер GameCube + + + + Poke Ball Plus + Poke Ball Plus + + + + NES Controller + Контролер NES + + + + SNES Controller + Контролер SNES + + + + N64 Controller + Контролер N64 + + + + Sega Genesis + Sega Genesis + + + + Start / Pause + Старт / Пауза + + + + Z + Z + + + + Control Stick + Міні-джойстик керування + + + + C-Stick + C-Джойстик + + + + Shake! + Потрусіть! + + + + [waiting] + [очікування] + + + + New Profile + Новий профіль + + + + Enter a profile name: + Введіть ім'я профілю: + + + + + Create Input Profile + Створити профіль контролю + + + + The given profile name is not valid! + Задане ім'я профілю недійсне! + + + + Failed to create the input profile "%1" + Не вдалося створити профіль контролю "%1" + + + + Delete Input Profile + Видалити профіль контролю + + + + Failed to delete the input profile "%1" + Не вдалося видалити профіль контролю "%1" + + + + Load Input Profile + Завантажити профіль контролю + + + + Failed to load the input profile "%1" + Не вдалося завантажити профіль контролю "%1" + + + + Save Input Profile + Зберегти профіль контролю + + + + Failed to save the input profile "%1" + Не вдалося зберегти профіль контролю "%1" + + + + ConfigureInputProfileDialog + + + Create Input Profile + Створити профіль контролю + + + + Clear + Очистити + + + + Defaults + За замовчуванням + + + + ConfigureMotionTouch + + + Configure Motion / Touch + Налаштування руху та сенсора + + + + Touch + Сенсор + + + + UDP Calibration: + Калібрація UDP: + + + + (100, 50) - (1800, 850) + (100, 50) - (1800, 850) + + + + + + Configure + Налаштувати + + + + Touch from button profile: + Торкніться з профілю кнопки: + + + + CemuhookUDP Config + Налаштування CemuhookUDP + + + + You may use any Cemuhook compatible UDP input source to provide motion and touch input. + Ви можете використовувати будь-яке сумісне з Cemuhook джерело UDP сигналу для руху і сенсора. + + + + Server: + Сервер: + + + + Port: + Порт: + + + + Learn More + Дізнатися більше + + + + + Test + Тест + + + + Add Server + Додати сервер + + + + Remove Server + Видалити сервер + + + + <a href='https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> + <a href='https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Дізнатися більше</span></a> + + + + %1:%2 + %1:%2 + + + + + + + + + yuzu + yuzu + + + + Port number has invalid characters + Номер порту містить неприпустимі символи + + + + Port has to be in range 0 and 65353 + Порт повинен бути в районі від 0 до 65353 + + + + IP address is not valid + IP-адреса недійсна + + + + This UDP server already exists + Цей UDP сервер уже існує + + + + Unable to add more than 8 servers + Неможливо додати більше 8 серверів + + + + Testing + Тестування + + + + Configuring + Налаштування + + + + Test Successful + Тест успішний + + + + Successfully received data from the server. + Успішно отримано інформацію із сервера + + + + Test Failed + Тест провалено + + + + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. + Не вдалося отримати дійсні дані з сервера.<br>Переконайтеся, що сервер правильно налаштований, а також перевірте адресу та порт. + + + + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. + Тест UDP або калібрація в процесі.<br>Будь ласка, зачекайте завершення. + + + + ConfigureNetwork + + + Form + Форма + + + + Network + Мережа + + + + General + Загальні + + + + Network Interface + Інтерфейс мережі + + + + None + Нічого + + + + ConfigurePerGame + + + Dialog + Діалог + + + + Info + Інформація + + + + Name + Назва + + + + Title ID + Ідентифікатор гри + + + + Filename + Ім'я файлу + + + + Format + Формат + + + + Version + Версія + + + + Size + Розмір + + + + Developer + Розробник + + + + Add-Ons + Доповнення + + + + General + Загальні + + + + System + Система + + + + CPU + ЦП + + + + Graphics + Графіка + + + + Adv. Graphics + Розш. Графіка + + + + Audio + Аудіо + + + + Properties + Властивості + + + + Use global configuration (%1) + Використовувати глобальне налаштування (%1) + + + + ConfigurePerGameAddons + + + Form + Форма + + + + Add-Ons + Доповнення + + + + Patch Name + Назва патчу + + + + Version + Версія + + + + ConfigureProfileManager + + + Form + Форма + + + + Profiles + Профілі + + + + Profile Manager + Керування профілями + + + + Current User + Поточний користувач + + + + Username + Ім'я користувача + + + + Set Image + Обрати зображення + + + + Add + Додати + + + + Rename + Перейменувати + + + + Remove + Видалити + + + + Profile management is available only when game is not running. + Керування профілями недоступне, поки запущена гра. + + + + %1 +%2 + %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) + %1 +%2 + + + + Enter Username + Введіть ім'я користувача + + + + Users + Користувачі + + + + Enter a username for the new user: + Введіть ім'я користувача для нового профілю: + + + + Enter a new username: + Введіть нове ім'я користувача: + + + + Confirm Delete + Підтвердити видалення + + + + You are about to delete user with name "%1". Are you sure? + Ви збираєтеся видалити користувача "%1". Ви впевнені? + + + + Select User Image + Оберіть зображення користувача + + + + JPEG Images (*.jpg *.jpeg) + Зображення JPEG (*.jpg *.jpeg) + + + + Error deleting image + Помилка під час видалення зображення + + + + Error occurred attempting to overwrite previous image at: %1. + Помилка під час спроби перезапису попереднього зображення в: %1. + + + + Error deleting file + Помилка під час видалення файлу + + + + Unable to delete existing file: %1. + Не вдалося видалити наявний файл: %1. + + + + Error creating user image directory + Помилка під час створення папки користувацьких зображень + + + + Unable to create directory %1 for storing user images. + Не вийшло створити папку %1 для зберігання зображень користувача. + + + + Error copying user image + Помилка під час копіювання зображення користувача + + + + Unable to copy image from %1 to %2 + Не вийшло скопіювати зображення з %1 у %2 + + + + Error resizing user image + Помилка під час зміни розміру зображення користувача + + + + Unable to resize image + Неможливо змінити розмір зображення + + + + ConfigureRingController + + + Configure Ring Controller + Налаштування контролера Ring + + + + If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly. + Якщо ви хочете використовувати цей контролер, налаштуйте гравця 1 як правий контролер, а гравця 2 як подвійний Joy-Соп перед початком гри, щоб цей контролер був виявлений правильно. + + + + Ring Sensor Parameters + Параметри сенсора Ring + + + + + Pull + Потягнути + + + + + Push + Натиснути + + + + Deadzone: 0% + Мертва зона: 0% + + + + Restore Defaults + За замовчуванням + + + + Clear + Очистити + + + + [not set] + [не задано] + + + + Invert axis + Інвертувати осі + + + + + Deadzone: %1% + Мертва зона: %1% + + + + [waiting] + [очікування] + + + + ConfigureSystem + + + Form + Форма + + + + System + Система + + + + System Settings + Налаштування системи + + + + Region: + Регіон: + + + + Auto + Авто + + + + Default + За замовчуванням + + + + CET + CET + + + + CST6CDT + CST6CDT + + + + Cuba + Куба + + + + EET + EET + + + + Egypt + Єгипет + + + + Eire + Ейре + + + + EST + EST + + + + EST5EDT + EST5EDT + + + + GB + GB + + + + GB-Eire + GB-Ейре + + + + GMT + GMT + + + + GMT+0 + GMT+0 + + + + GMT-0 + GMT-0 + + + + GMT0 + GMT0 + + + + Greenwich + Гринвіч + + + + Hongkong + Гонконг + + + + HST + HST + + + + Iceland + Ісландія + + + + Iran + Іран + + + + Israel + Ізраїль + + + + Jamaica + Ямайка + + + + + Japan + Японія + + + + Kwajalein + Кваджалейн + + + + Libya + Лівія + + + + MET + MET + + + + MST + MST + + + + MST7MDT + MST7MDT + + + + Navajo + Навахо + + + + NZ + NZ + + + + NZ-CHAT + NZ-CHAT + + + + Poland + Польща + + + + Portugal + Португалія + + + + PRC + PRC + + + + PST8PDT + PST8PDT + + + + ROC + ROC + + + + ROK + ROK + + + + Singapore + Сінгапур + + + + Turkey + Туреччина + + + + UCT + UCT + + + + Universal + Універсальний + + + + UTC + UTC + + + + W-SU + W-SU + + + + WET + WET + + + + Zulu + Зулуси + + + + USA + США + + + + Europe + Європа + + + + Australia + Австралія + + + + China + Китай + + + + Korea + Корея + + + + Taiwan + Тайвань + + + + Time Zone: + Часовий пояс: + + + + Note: this can be overridden when region setting is auto-select + Примітка: може бути перезаписано якщо регіон вибирається автоматично + + + + Japanese (日本語) + Японська (日本語) + + + + English + Англійська (English) + + + + French (français) + Французька (français) + + + + German (Deutsch) + Німецька (Deutsch) + + + + Italian (italiano) + Італійська (italiano) + + + + Spanish (español) + Іспанська (español) + + + + Chinese + Китайська + + + + Korean (한국어) + Корейська (한국어) + + + + Dutch (Nederlands) + Голландська (Nederlands) + + + + Portuguese (português) + Португальська (português) + + + + Russian (Русский) + Російська (Русский) + + + + Taiwanese + Тайванська + + + + British English + Британська Англійська + + + + Canadian French + Канадська Французька + + + + Latin American Spanish + Латиноамериканська Іспанська + + + + Simplified Chinese + Спрощена Китайська + + + + Traditional Chinese (正體中文) + Традиційна Китайська (正體中文) + + + + Brazilian Portuguese (português do Brasil) + Бразильська Португальська (português do Brasil) + + + + Custom RTC + Користувацький RTC + + + + Language + Мова + + + + RNG Seed + Сід RNG + + + + Mono + Моно + + + + Stereo + Стерео + + + + Surround + Об'ємний звук + + + + Console ID: + Ідентифікатор консолі: + + + + Sound output mode + Режим виводу звуку + + + + Regenerate + Перегенерувати + + + + System settings are available only when game is not running. + Налаштування системи доступні тільки тоді, коли гру не запущено. + + + + This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? + Це замінить ваш поточний віртуальний Switch новим. Ваш поточний віртуальний Switch буде безповоротно втрачено. Це може мати несподівані наслідки в іграх. Може не спрацювати, якщо ви використовуєте застарілу конфігурацію збережених ігор. Продовжити? + + + + Warning + Увага + + + + Console ID: 0x%1 + Ідентифікатор консолі: 0x%1 + + + + ConfigureTas + + + TAS + TAS + + + + <html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the yuzu website.</p></body></html> + <html><head/><body><p>Зчитує вхідні дані контролера зі скриптів у тому ж форматі, що і скрипти TAS-nx.<br/>Для більш детального пояснення зверніться до <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">сторінки допомоги</span></a> на сайті yuzu.</p></body></html> + + + + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). + Щоб перевірити, які гарячі клавіші керують відтворенням/записом, зверніться до налаштувань гарячих клавіш (Налаштування - Загальні -> Гарячі клавіші). + + + + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. + ПОПЕРЕДЖЕННЯ: Це експериментальна функція.<br/>Вона не буде ідеально відтворювати кадри сценаріїв за поточного недосконалого методу синхронізації. + + + + Settings + Налаштування + + + + Enable TAS features + Увімкнути функції TAS + + + + Loop script + Зациклити скрипт + + + + Pause execution during loads + Призупинити виконання під час завантаження + + + + Script Directory + Папка для скриптів + + + + Path + Шлях + + + + ... + ... + + + + ConfigureTasDialog + + + TAS Configuration + Налаштування TAS + + + + Select TAS Load Directory... + Обрати папку завантаження TAS... + + + + ConfigureTouchFromButton + + + Configure Touchscreen Mappings + Налаштування відображення сенсорного екрана + + + + Mapping: + Прив'язки: + + + + New + Новий + + + + Delete + Видалити + + + + Rename + Перейменувати + + + + Click the bottom area to add a point, then press a button to bind. +Drag points to change position, or double-click table cells to edit values. + Натисніть на нижній області, щоб додати точку, після чого натисніть кнопку для прив'язки. +Перетягніть точки, щоб змінити позицію, або натисніть двічі на комірки таблиці для зміни значень. + + + + Delete Point + Видалити точку + + + + Button + Кнопка + + + + X + X axis + X + + + + Y + Y axis + Y + + + + New Profile + Новий профіль + + + + Enter the name for the new profile. + Введіть ім'я вашого нового профілю. + + + + Delete Profile + Видалити профіль + + + + Delete profile %1? + Видалити профіль %1? + + + + Rename Profile + Перейменувати профіль + + + + New name: + Нова назва: + + + + [press key] + [натисніть клавішу] + + + + ConfigureTouchscreenAdvanced + + + Configure Touchscreen + Налаштування сенсорного екрана + + + + Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing. + Увага: Налаштування на цій сторінці впливають на внутрішню роботу емульованого сенсорного екрана yuzu. Їх зміна може призвести до небажаної поведінки, як часткова або повна непрацездатність сенсорного екрана. Використовуйте цю сторінку лише якщо ви знаєте, що робите. + + + + Touch Parameters + Параметри сенсора + + + + Touch Diameter Y + Діаметр сенсора Y + + + + Touch Diameter X + Діаметр сенсора X + + + + Rotational Angle + Кут повороту + + + + Restore Defaults + За замовчуванням + + + + ConfigureUI + + + + + None + Нічого + + + + Small (32x32) + Маленький (32х32) + + + + Standard (64x64) + Стандартний (64х64) + + + + Large (128x128) + Великий (128х128) + + + + Full Size (256x256) + Повнорозмірний (256х256) + + + + Small (24x24) + Маленький (24х24) + + + + Standard (48x48) + Стандартний (48х48) + + + + Large (72x72) + Великий (72х72) + + + + Filename + Ім'я файлу + + + + Filetype + Тип файлу + + + + Title ID + Ідентифікатор гри + + + + Title Name + Назва гри + + + + ConfigureUi + + + Form + Форма + + + + UI + Інтерфейс + + + + General + Загальні + + + + Note: Changing language will apply your configuration. + Примітка: Зміна мови призведе до застосування налаштувань. + + + + Interface language: + Мова інтерфейсу: + + + + Theme: + Тема: + + + + Game List + Список ігор + + + + Show Compatibility List + Показати список сумісності + + + + Show Add-Ons Column + Показувати стовпець доповнень + + + + Game Icon Size: + Розмір іконки гри: + + + + Folder Icon Size: + Розмір іконки папки: + + + + Row 1 Text: + Текст 1-го рядку: + + + + Row 2 Text: + Текст 2-го рядку: + + + + Screenshots + Знімки екрану + + + + Ask Where To Save Screenshots (Windows Only) + Запитувати куди зберігати знімки екрану (Тільки для Windows) + + + + Screenshots Path: + Папка для знімків екрану: + + + + ... + ... + + + + Select Screenshots Path... + Виберіть папку для знімків екрану... + + + + <System> + <System> + + + + ConfigureVibration + + + Configure Vibration + Налаштування вібрації + + + + Press any controller button to vibrate the controller. + Натисніть будь-яку кнопку контролера, щоб викликати вібрацію. + + + + Vibration + Вібрація + + + + Player 1 + Гравець 1 + + + + + + + + + + + % + % + + + + Player 2 + Гравець 2 + + + + Player 3 + Гравець 3 + + + + Player 4 + Гравець 4 + + + + Player 5 + Гравець 5 + + + + Player 6 + Гравець 6 + + + + Player 7 + Гравець 7 + + + + Player 8 + Гравець 8 + + + + Settings + Налаштування + + + + Enable Accurate Vibration + Увімкнути точну вібрацію + + + + ConfigureWeb + + + Form + Форма + + + + Web + Мережа + + + + yuzu Web Service + Веб-сервіс yuzu + + + + By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information. + Надаючи своє ім'я користувача і токен, ви погоджуєтеся дозволити yuzu збирати додаткові дані про використання, які можуть включати інформацію, що ідентифікує користувача. + + + + + Verify + Підтвердити + + + + Sign up + Реєстрація + + + + Token: + Токен: + + + + Username: + Ім'я користувача: + + + + What is my token? + Що таке токен і де його знайти? + + + + Web Service configuration can only be changed when a public room isn't being hosted. + Налаштування веб-служби можуть бути змінені тільки в тому випадку, коли не хоститься публічна кімната. + + + + Telemetry + Телеметрія + + + + Share anonymous usage data with the yuzu team + Ділитися анонімною інформацією про використання з командою yuzu + + + + Learn more + Дізнатися більше + + + + Telemetry ID: + Ідентифікатор телеметрії: + + + + Regenerate + Перегенерувати + + + + Discord Presence + Discord Presence + + + + Show Current Game in your Discord Status + Показувати поточну гру у вашому статусі Discord + + + + <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> + <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Дізнатися більше</span></a> + + + + <a href='https://profile.yuzu-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.yuzu-emu.org/'><span style="text-decoration: underline; color:#039be5;">Реєстрація</span></a> + + + + <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> + <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">Що таке токен і де його знайти?</span></a> + + + + + Telemetry ID: 0x%1 + Ідентифікатор телеметрії: 0x%1 + + + + + Unspecified + Відсутній + + + + Token not verified + Токен не підтверджено + + + + Token was not verified. The change to your token has not been saved. + Токен не було підтверджено. Зміну вашого токена не було збережено. + + + + Unverified, please click Verify before saving configuration + Tooltip + Не підтверджено, будь ласка, натисніть кнопку Підтвердити, перш ніж зберігати конфігурацію. + + + + + Verifying... + Підтверждення... + + + + Verified + Tooltip + Підтверджено + + + + Verification failed + Tooltip + Підтверждення не було успішним + + + + Verification failed + Підтверждення не було успішним + + + + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. + Підтверждення не було успішним. Переконайтеся, що ви правильно ввели свій токен і що ваше інтернет-з'єднання працює. + + + + ControllerDialog + + + Controller P1 + Контролер P1 + + + + &Controller P1 + [&C] Контролер P1 + + + + DirectConnect + + + Direct Connect + Пряме підключення + + + + IP Address + IP-адреса + + + + IP + IP + + + + <html><head/><body><p>IPv4 address of the host</p></body></html> + <html><head/><body><p>IPv4 адреса хоста</p></body></html> + + + + Port + Порт + + + + <html><head/><body><p>Port number the host is listening on</p></body></html> + <html><head/><body><p>Номер порту, який прослуховується хостом</p></body></html> + + + + Nickname + Псевдонім + + + + Password + Пароль + + + + Connect + Підключитися + + + + DirectConnectWindow + + + Connecting + Підключення + + + + Connect + Підключитися + + + + GMainWindow + + + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Анонімні дані збираються для того,</a> щоб допомогти поліпшити роботу yuzu. <br/><br/>Хотіли б ви ділитися даними про використання з нами? + + + + Telemetry + Телеметрія + + + + Broken Vulkan Installation Detected + Виявлено пошкоджену інсталяцію Vulkan + + + + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. + Не вдалося виконати ініціалізацію Vulkan під час завантаження.<br><br>Натисніть <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>тут для отримання інструкцій щодо усунення проблеми</a>. + + + + Loading Web Applet... + Завантаження веб-аплета... + + + + + Disable Web Applet + Вимкнути веб-аплет + + + + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? +(This can be re-enabled in the Debug settings.) + Вимкнення веб-апплета може призвести до несподіваної поведінки, і його слід вимикати лише заради Super Mario 3D All-Stars. Ви впевнені, що хочете вимкнути веб-апплет? +(Його можна знову ввімкнути в налаштуваннях налагодження.) + + + + The amount of shaders currently being built + Кількість створюваних шейдерів на цей момент + + + + The current selected resolution scaling multiplier. + Поточний обраний множник масштабування роздільної здатності. + + + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. + Поточна швидкість емуляції. Значення вище або нижче 100% вказують на те, що емуляція йде швидше або повільніше, ніж на Switch. + + + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. + Кількість кадрів на секунду в цей момент. Значення буде змінюватися між іграми та сценами. + + + + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. + Час, який потрібен для емуляції 1 кадру Switch, не беручи до уваги обмеження FPS або вертикальну синхронізацію. Для емуляції в повній швидкості значення має бути не більше 16,67 мс. + + + + VULKAN + VULKAN + + + + OPENGL + OPENGL + + + + &Clear Recent Files + [&C] Очистити нещодавні файли + + + + &Continue + [&C] Продовжити + + + + &Pause + [&P] Пауза + + + + yuzu is running a game + TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping + В yuzu запущено гру + + + + Warning Outdated Game Format + Попередження застарілий формат гри + + + + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. + Для цієї гри ви використовуєте розархівований формат ROM'а, який є застарілим і був замінений іншими, такими як NCA, NAX, XCI або NSP. У розархівованих каталогах ROM'а відсутні іконки, метадані та підтримка оновлень. <br><br>Для отримання інформації про різні формати Switch, підтримувані yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>перегляньте нашу вікі</a>. Це повідомлення більше не буде відображатися. + + + + + Error while loading ROM! + Помилка під час завантаження ROM! + + + + The ROM format is not supported. + Формат ROM'а не підтримується. + + + + An error occurred initializing the video core. + Сталася помилка під час ініціалізації відеоядра. + + + + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. + yuzu зіткнувся з помилкою під час запуску відеоядра. Зазвичай це спричинено застарілими драйверами ГП, включно з інтегрованими. Перевірте журнал для отримання більш детальної інформації. Додаткову інформацію про доступ до журналу дивіться на наступній сторінці: <a href='https://yuzu-emu.org/help/reference/log-files/'>Як завантажити файл журналу</a>. + + + + Error while loading ROM! %1 + %1 signifies a numeric error code. + Помилка під час завантаження ROM'а! %1 + + + + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. + %1 signifies an error string. + %1<br>Будь ласка, дотримуйтесь <a href='https://yuzu-emu.org/help/quickstart/'>короткого керівництва користувача yuzu</a> щоб пере-дампити ваші файли<br>Ви можете звернутися до вікі yuzu</a> або Discord yuzu</a> для допомоги + + + + An unknown error occurred. Please see the log for more details. + Сталася невідома помилка. Будь ласка, перевірте журнал для подробиць. + + + + (64-bit) + (64-бітний) + + + + (32-bit) + (32-бітний) + + + + %1 %2 + %1 is the title name. %2 indicates if the title is 64-bit or 32-bit + %1 %2 + + + + Save Data + Збереження + + + + Mod Data + Дані модів + + + + Error Opening %1 Folder + Помилка під час відкриття папки %1 + + + + + Folder does not exist! + Папка не існує! + + + + Error Opening Transferable Shader Cache + Помилка під час відкриття переносного кешу шейдерів + + + + Failed to create the shader cache directory for this title. + Не вдалося створити папку кешу шейдерів для цієї гри. + + + + Contents + Зміст + + + + Update + Оновлення + + + + DLC + DLC + + + + Remove Entry + Видалити запис + + + + Remove Installed Game %1? + Видалити встановлену гру %1? + + + + + + + + + Successfully Removed + Успішно видалено + + + + Successfully removed the installed base game. + Встановлену гру успішно видалено. + + + + + + Error Removing %1 + Помилка під час видалення %1 + + + + The base game is not installed in the NAND and cannot be removed. + Гру не встановлено в NAND і не може буде видалено. + + + + Successfully removed the installed update. + Встановлене оновлення успішно видалено. + + + + There is no update installed for this title. + Для цієї гри не було встановлено оновлення. + + + + There are no DLC installed for this title. + Для цієї гри не було встановлено DLC. + + + + Successfully removed %1 installed DLC. + Встановлений DLC %1 було успішно видалено + + + + Delete OpenGL Transferable Shader Cache? + Видалити переносний кеш шейдерів OpenGL? + + + + Delete Vulkan Transferable Shader Cache? + Видалити переносний кеш шейдерів Vulakn? + + + + Delete All Transferable Shader Caches? + Видалити весь переносний кеш шейдерів? + + + + Remove Custom Game Configuration? + Видалити користувацьке налаштування гри? + + + + Remove File + Видалити файл + + + + + Error Removing Transferable Shader Cache + Помилка під час видалення переносного кешу шейдерів + + + + + A shader cache for this title does not exist. + Кеш шейдерів для цієї гри не існує. + + + + Successfully removed the transferable shader cache. + Переносний кеш шейдерів успішно видалено. + + + + Failed to remove the transferable shader cache. + Не вдалося видалити переносний кеш шейдерів. + + + + + Error Removing Transferable Shader Caches + Помилка під час видалення переносного кешу шейдерів + + + + Successfully removed the transferable shader caches. + Переносний кеш шейдерів успішно видалено. + + + + Failed to remove the transferable shader cache directory. + Помилка під час видалення папки переносного кешу шейдерів. + + + + + Error Removing Custom Configuration + Помилка під час видалення користувацького налаштування + + + + A custom configuration for this title does not exist. + Користувацьких налаштувань для цієї гри не існує. + + + + Successfully removed the custom game configuration. + Користувацьке налаштування гри успішно видалено. + + + + Failed to remove the custom game configuration. + Не вдалося видалити користувацьке налаштування гри. + + + + + RomFS Extraction Failed! + Не вдалося вилучити RomFS! + + + + There was an error copying the RomFS files or the user cancelled the operation. + Сталася помилка під час копіювання файлів RomFS або користувач скасував операцію. + + + + Full + Повний + + + + Skeleton + Скелет + + + + Select RomFS Dump Mode + Виберіть режим дампа RomFS + + + + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. + Будь ласка, виберіть, як ви хочете виконати дамп RomFS <br>Повний скопіює всі файли в нову папку, тоді як <br>скелет створить лише структуру папок. + + + + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root + В %1 недостатньо вільного місця для вилучення RomFS. Будь ласка, звільніть місце або виберіть іншу папку для дампа в Емуляція > Налаштування > Система > Файлова система > Корінь дампа + + + + Extracting RomFS... + Вилучення RomFS... + + + + + Cancel + Скасувати + + + + RomFS Extraction Succeeded! + Вилучення RomFS пройшло успішно! + + + + The operation completed successfully. + Операція завершилася успішно. + + + + Error Opening %1 + Помилка відкриття %1 + + + + Select Directory + Обрати папку + + + + Properties + Властивості + + + + The game properties could not be loaded. + Не вдалося завантажити властивості гри. + + + + Switch Executable (%1);;All Files (*.*) + %1 is an identifier for the Switch executable file extensions. + Виконуваний файл Switch (%1);;Усі файли (*.*) + + + + Load File + Завантажити файл + + + + Open Extracted ROM Directory + Відкрити папку вилученого ROM'а + + + + Invalid Directory Selected + Вибрано неприпустиму папку + + + + The directory you have selected does not contain a 'main' file. + Папка, яку ви вибрали, не містить файлу 'main'. + + + + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) + Встановлюваний файл Switch (*.nca, *.nsp, *.xci);;Архів контенту Nintendo (*.nca);;Пакет подачі Nintendo (*.nsp);;Образ картриджа NX (*.xci) + + + + Install Files + Встановити файли + + + + %n file(s) remaining + Залишився %n файлЗалишилося %n файл(ів)Залишилося %n файл(ів)Залишилося %n файл(ів) + + + + Installing file "%1"... + Встановлення файлу "%1"... + + + + + Install Results + Результати встановлення + + + + To avoid possible conflicts, we discourage users from installing base games to the NAND. +Please, only use this feature to install updates and DLC. + Щоб уникнути можливих конфліктів, ми не рекомендуємо користувачам встановлювати ігри в NAND. +Будь ласка, використовуйте цю функцію тільки для встановлення оновлень і завантажуваного контенту. + + + + %n file(s) were newly installed + + %n файл було нещодавно встановлено +%n файл(ів) було нещодавно встановлено +%n файл(ів) було нещодавно встановлено +%n файл(ів) було нещодавно встановлено + + + + + %n file(s) were overwritten + + %n файл було перезаписано +%n файл(ів) було перезаписано +%n файл(ів) було перезаписано +%n файл(ів) було перезаписано + + + + + %n file(s) failed to install + + %n файл не вдалося встановити +%n файл(ів) не вдалося встановити +%n файл(ів) не вдалося встановити +%n файл(ів) не вдалося встановити + + + + + System Application + Системний додаток + + + + System Archive + Системний архів + + + + System Application Update + Оновлення системного додатку + + + + Firmware Package (Type A) + Пакет прошивки (Тип А) + + + + Firmware Package (Type B) + Пакет прошивки (Тип Б) + + + + Game + Гра + + + + Game Update + Оновлення гри + + + + Game DLC + DLC до гри + + + + Delta Title + Дельта-титул + + + + Select NCA Install Type... + Виберіть тип установки NCA... + + + + Please select the type of title you would like to install this NCA as: +(In most instances, the default 'Game' is fine.) + Будь ласка, виберіть тип додатку, який ви хочете встановити для цього NCA: +(У більшості випадків, підходить стандартний вибір "Гра".) + + + + Failed to Install + Помилка встановлення + + + + The title type you selected for the NCA is invalid. + Тип додатку, який ви вибрали для NCA, недійсний. + + + + File not found + Файл не знайдено + + + + File "%1" not found + Файл "%1" не знайдено + + + + OK + ОК + + + + Missing yuzu Account + Відсутній обліковий запис yuzu + + + + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. + Щоб надіслати звіт про сумісність гри, необхідно прив'язати свій обліковий запис yuzu. <br><br/>Щоб прив'язати свій обліковий запис yuzu, перейдіть у розділ Емуляція &gt; Параметри &gt; Мережа. + + + + Error opening URL + Помилка під час відкриття URL + + + + Unable to open the URL "%1". + Не вдалося відкрити URL: "%1". + + + + TAS Recording + Запис TAS + + + + Overwrite file of player 1? + Перезаписати файл гравця 1? + + + + Invalid config detected + Виявлено неприпустиму конфігурацію + + + + Handheld controller can't be used on docked mode. Pro controller will be selected. + Портативний контролер не може бути використаний у режимі док-станції. Буде обрано контролер Pro. + + + + + Amiibo + Amiibo + + + + + The current amiibo has been removed + Поточний amiibo було прибрано + + + + Error + Помилка + + + + + The current game is not looking for amiibos + Поточна гра не шукає amiibo + + + + Amiibo File (%1);; All Files (*.*) + Файл Amiibo (%1);; Всі Файли (*.*) + + + + Load Amiibo + Завантажити Amiibo + + + + Error loading Amiibo data + Помилка під час завантаження даних Amiibo + + + + The selected file is not a valid amiibo + Обраний файл не є допустимим amiibo + + + + The selected file is already on use + Обраний файл уже використовується + + + + An unknown error occurred + Виникла невідома помилка + + + + Capture Screenshot + Зробити знімок екрану + + + + PNG Image (*.png) + Зображення PNG (*.png) + + + + TAS state: Running %1/%2 + Стан TAS: Виконується %1/%2 + + + + TAS state: Recording %1 + Стан TAS: Записується %1 + + + + TAS state: Idle %1/%2 + Стан TAS: Простий %1/%2 + + + + TAS State: Invalid + Стан TAS: Неприпустимий + + + + &Stop Running + [&S] Зупинка + + + + &Start + [&S] Почати + + + + Stop R&ecording + [&E] Закінчити запис + + + + R&ecord + [&E] Запис + + + + Building: %n shader(s) + Побудова: %n шейдерПобудова: %n шейдер(ів)Побудова: %n шейдер(ів)Побудова: %n шейдер(ів) + + + + Scale: %1x + %1 is the resolution scaling factor + Масштаб: %1x + + + + Speed: %1% / %2% + Швидкість: %1% / %2% + + + + Speed: %1% + Швидкість: %1% + + + + Game: %1 FPS (Unlocked) + Гра: %1 FPS (Необмежено) + + + + Game: %1 FPS + Гра: %1 FPS + + + + Frame: %1 ms + Кадр: %1 мс + + + + GPU NORMAL + ГП НОРМАЛЬНО + + + + GPU HIGH + ГП ВИСОКО + + + + GPU EXTREME + ГП ЕКСТРИМ + + + + GPU ERROR + ГП ПОМИЛКА + + + + DOCKED + В ДОК-СТАНЦІЇ + + + + HANDHELD + ПОРТАТИВНИЙ + + + + NEAREST + НАЙБЛИЖЧІЙ + + + + + BILINEAR + БІЛІНІЙНИЙ + + + + BICUBIC + БІКУБІЧНИЙ + + + + GAUSSIAN + ГАУС + + + + SCALEFORCE + SCALEFORCE + + + + FSR + FSR + + + + + NO AA + БЕЗ ЗГЛАДЖУВАННЯ + + + + FXAA + FXAA + + + + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. + Гра, яку ви намагаєтеся завантажити, вимагає, щоб додаткові файли були здамплені з вашого Switch перед початком гри. <br/><br/>Для отримання додаткової інформації про дамп цих файлів див. наступну вікі: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Дамп системних архівів і загальних шрифтів з консолі</a>. <br/><br/>Хочете повернутися до списку ігор? Продовження емуляції може призвести до збоїв, пошкодження збережених даних або інших помилок. + + + + yuzu was unable to locate a Switch system archive. %1 + yuzu не вдалося знайти системний архів Switch. %1 + + + + yuzu was unable to locate a Switch system archive: %1. %2 + yuzu не вдалося знайти системний архів Switch: %1. %2 + + + + System Archive Not Found + Системний архів не знайдено + + + + System Archive Missing + Відсутній системний архів + + + + yuzu was unable to locate the Switch shared fonts. %1 + yuzu не вдалося знайти загальні шрифти Switch. %1 + + + + Shared Fonts Not Found + Загальні шрифти не знайдено + + + + Shared Font Missing + Загальні шрифти відсутні + + + + Fatal Error + Фатальна помилка + + + + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. + yuzu зіткнувся з фатальною помилкою, перевірте журнал для отримання більш детальної інформації. Для отримання додаткової інформації про доступ до журналу відкрийте наступну сторінку: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Як завантажити файл журналу</a>.<br/><br/>Ви хочете повернутися до списку ігор? Продовження емуляції може призвести до збоїв, пошкодження збережень або інших помилок. + + + + Fatal Error encountered + Сталася фатальна помилка + + + + Confirm Key Rederivation + Підтвердіть перерахунок ключа + + + + You are about to force rederive all of your keys. +If you do not know what this means or what you are doing, +this is a potentially destructive action. +Please make sure this is what you want +and optionally make backups. + +This will delete your autogenerated key files and re-run the key derivation module. + Ви збираєтеся примусово перерахувати всі ваші ключі. +Якщо ви не знаєте, що це означає або що ви робите, +це потенційно руйнівна дія. +Будь ласка, переконайтеся, що це те, що ви хочете +і, по бажанню, зробіть резервні копії. + +Це видалить ваші автоматично згенеровані файли ключів і повторно запустить модуль розрахунку ключів. + + + + Missing fuses + Відсутні запобіжники + + + + - Missing BOOT0 + - Відсутній BOOT0 + + + + - Missing BCPKG2-1-Normal-Main + - Відсутній BCPKG2-1-Normal-Main + + + + - Missing PRODINFO + - Відсутній PRODINFO + + + + Derivation Components Missing + Компоненти розрахунку відсутні + + + + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> + Ключі шифрування відсутні.<br>Будь ласка, дотримуйтесь <a href='https://yuzu-emu.org/help/quickstart/'>короткого керівництва користувача yuzu</a>, щоб отримати всі ваші ключі, прошивку та ігри<br><br><small>(%1)</small> + + + + Deriving keys... +This may take up to a minute depending +on your system's performance. + Отримання ключів... +Це може зайняти до хвилини залежно від +від продуктивності вашої системи. + + + + Deriving Keys + Отримання ключів + + + + Select RomFS Dump Target + Оберіть ціль для дампа RomFS + + + + Please select which RomFS you would like to dump. + Будь ласка, виберіть, який RomFS ви хочете здампити. + + + + Are you sure you want to close yuzu? + Ви впевнені, що хочете закрити yuzu? + + + + + + yuzu + yuzu + + + + Are you sure you want to stop the emulation? Any unsaved progress will be lost. + Ви впевнені, що хочете зупинити емуляцію? Будь-який незбережений прогрес буде втрачено. + + + + The currently running application has requested yuzu to not exit. + +Would you like to bypass this and exit anyway? + Запущений на даний момент додаток просить yuzu не завершувати роботу. + +Чи хочете ви обійти це і вийти в будь-якому випадку? + + + + GRenderWindow + + + OpenGL not available! + OpenGL недоступний! + + + + yuzu has not been compiled with OpenGL support. + yuzu не було зібрано з підтримкою OpenGL. + + + + + Error while initializing OpenGL! + Помилка під час ініціалізації OpenGL! + + + + Your GPU may not support OpenGL, or you do not have the latest graphics driver. + Ваш ГП може не підтримувати OpenGL, або у вас встановлено застарілий графічний драйвер. + + + + Error while initializing OpenGL 4.6! + Помилка під час ініціалізації OpenGL 4.6! + + + + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 + Ваш ГП може не підтримувати OpenGL 4.6, або у вас встановлено застарілий графічний драйвер.<br><br>Рендерер GL:<br>%1 + + + + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 + Ваш ГП може не підтримувати одне або кілька необхідних розширень OpenGL. Будь ласка, переконайтеся в тому, що у вас встановлено останній графічний драйвер.<br><br>Рендерер GL:<br>%1<br><br>Розширення, що не підтримуються:<br>%2 + + + + GameList + + + Favorite + Улюблені + + + + Start Game + Запустити гру + + + + Start Game without Custom Configuration + Запустити гру без користувацького налаштування + + + + Open Save Data Location + Відкрити папку для збережень + + + + Open Mod Data Location + Відкрити папку для модів + + + + Open Transferable Pipeline Cache + Відкрити переносний кеш конвеєра + + + + Remove + Видалити + + + + Remove Installed Update + Видалити встановлене оновлення + + + + Remove All Installed DLC + Видалити усі DLC + + + + Remove Custom Configuration + Видалити користувацьке налаштування + + + + Remove OpenGL Pipeline Cache + Видалити кеш конвеєра OpenGL + + + + Remove Vulkan Pipeline Cache + Видалити кеш конвеєра Vulkan + + + + Remove All Pipeline Caches + Видалити весь кеш конвеєра + + + + Remove All Installed Contents + Видалити весь встановлений вміст + + + + + Dump RomFS + Дамп RomFS + + + + Dump RomFS to SDMC + Здампити RomFS у SDMC + + + + Copy Title ID to Clipboard + Скопіювати ідентифікатор додатку в буфер обміну + + + + Navigate to GameDB entry + Перейти до сторінки GameDB + + + + Properties + Властивості + + + + Scan Subfolders + Сканувати підпапки + + + + Remove Game Directory + Видалити директорію гри + + + + ▲ Move Up + ▲ Перемістити вверх + + + + ▼ Move Down + ▼ Перемістити вниз + + + + Open Directory Location + Відкрити розташування папки + + + + Clear + Очистити + + + + Name + Назва + + + + Compatibility + Сумісність + + + + Add-ons + Доповнення + + + + File type + Тип файлу + + + + Size + Розмір + + + + GameListItemCompat + + + Perfect + Ідеально + + + + Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without +any workarounds needed. + Гра працює бездоганно, без звукових або графічних артефактів, усі протестовані функції працюють без обхідних шляхів. + + + + Great + Чудово + + + + Game functions with minor graphical or audio glitches and is playable from start to finish. May require some +workarounds. + Гра працює з невеликими графічними або звуковими артефактами і може бути пройдена від + початку до кінця. Можуть знадобитися обхідні шляхи. + + + + Okay + Добре + + + + Game functions with major graphical or audio glitches, but game is playable from start to finish with +workarounds. + Гра працює з істотними графічними або звуковими артефактами, але може бути пройдена +з використанням обхідних шляхів. + + + + Bad + Погано + + + + Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches +even with workarounds. + Гра працює, але з істотними графічними або звуковими артефактами. +У деяких частинах неможливо просунутися навіть з обхідними шляхами. + + + + Intro/Menu + Вступ/Меню + + + + Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start +Screen. + У гру неможливо грати через графічні або звукові артефакти. +Неможливо просунутися далі стартового екрана. + + + + Won't Boot + Не запускається + + + + The game crashes when attempting to startup. + Гра вилітає під час запуску. + + + + Not Tested + Не перевірено + + + + The game has not yet been tested. + Гру ще не перевіряли на сумісність. + + + + GameListPlaceholder + + + Double-click to add a new folder to the game list + Натисніть двічі, щоб додати нову папку до списку ігор + + + + GameListSearchField + + + %1 of %n result(s) + %1 із %n результат(ів)%1 із %n результат(ів)%1 із %n результат(ів)%1 із %n результат(ів) + + + + Filter: + Пошук: + + + + Enter pattern to filter + Введіть текст для пошуку + + + + HostRoom + + + Create Room + Створити кімнату + + + + Room Name + Назва кімнати + + + + Preferred Game + Переважна гра + + + + Max Players + Максимальна кількість гравців + + + + Username + Ім'я користувача + + + + (Leave blank for open game) + (Залиште порожнім для відкритої гри) + + + + Password + Пароль + + + + Port + Порт + + + + Room Description + Опис кімнати + + + + Load Previous Ban List + Завантажити попередній список заблокованих + + + + Public + Публічна + + + + Unlisted + Прихована + + + + Host Room + Створити кімнату + + + + HostRoomWindow + + + Error + Помилка + + + + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. +Debug Message: + Не вдалося оголосити кімнату в публічному фойє. Щоб хостити публічну кімнату, у вас має бути діючий обліковий запис yuzu, налаштований в Емуляція -> Налаштування -> Мережа. Якщо ви не хочете оголошувати кімнату в публічному лобі, виберіть замість цього прихований тип. +Повідомлення налагодження: + + + + Hotkeys + + + Audio Mute/Unmute + Увімкнення/вимкнення звуку + + + + + + + + + + + + + + + + + + + + + + + + + Main Window + Основне вікно + + + + Audio Volume Down + Зменшити гучність звуку + + + + Audio Volume Up + Підвищити гучність звуку + + + + Capture Screenshot + Зробити знімок екрану + + + + Change Adapting Filter + Змінити адаптуючий фільтр + + + + Change Docked Mode + Змінити режим консолі + + + + Change GPU Accuracy + Змінити точність ГП + + + + Continue/Pause Emulation + Продовження/Пауза емуляції + + + + Exit Fullscreen + Вийти з повноекранного режиму + + + + Exit yuzu + Вийти з yuzu + + + + Fullscreen + Повний екран + + + + Load File + Завантажити файл + + + + Load/Remove Amiibo + Завантажити/видалити Amiibo + + + + Restart Emulation + Перезапустити емуляцію + + + + Stop Emulation + Зупинити емуляцію + + + + TAS Record + Запис TAS + + + + TAS Reset + Скидання TAS + + + + TAS Start/Stop + Старт/Стоп TAS + + + + Toggle Filter Bar + Переключити панель пошуку + + + + Toggle Framerate Limit + Переключити обмеження частоти кадрів + + + + Toggle Mouse Panning + Переключити панорамування миші + + + + Toggle Status Bar + Переключити панель стану + + + + InstallDialog + + + Please confirm these are the files you wish to install. + Будь ласка, переконайтеся, що це ті файли, які ви хочете встановити. + + + + Installing an Update or DLC will overwrite the previously installed one. + Встановлення оновлення або завантажуваного контенту перезапише раніше встановлене. + + + + Install + Встановити + + + + Install Files to NAND + Встановити файли в NAND + + + + LimitableInputDialog + + + The text can't contain any of the following characters: +%1 + У тексті неприпустимі такі символи: +%1 + + + + LoadingScreen + + + Loading Shaders 387 / 1628 + Завантаження шейдерів 387 / 1628 + + + + Loading Shaders %v out of %m + Завантаження шейдерів %v із %m + + + + Estimated Time 5m 4s + Залишилося приблизно 5м 4с + + + + Loading... + Завантаження... + + + + Loading Shaders %1 / %2 + Завантаження шейдерів %1 / %2 + + + + Launching... + Запуск... + + + + Estimated Time %1 + Залишилося приблизно %1 + + + + Lobby + + + Public Room Browser + Браузер публічних кімнат + + + + + Nickname + Псевдонім + + + + Filters + Фільтри + + + + Search + Пошук + + + + Games I Own + Ігри, якими я володію + + + + Hide Full Rooms + Приховати повні кімнати + + + + Refresh Lobby + Оновити фойє + + + + Password Required to Join + Для входу необхідний пароль + + + + Password: + Пароль: + + + + Players + Гравці + + + + Room Name + Назва кімнати + + + + Preferred Game + Переважна гра + + + + Host + Хост + + + + Refreshing + Оновлення + + + + Refresh List + Оновити список + + + + MainWindow + + + yuzu + yuzu + + + + &File + [&F] Файл + + + + &Recent Files + [&R] Нещодавні файли + + + + &Emulation + [&E] Емуляція + + + + &View + [&V] Вигляд + + + + &Reset Window Size + [&R] Скинути розмір вікна + + + + &Debugging + [&D] Налагодження + + + + Reset Window Size to &720p + Скинути розмір вікна до &720p + + + + Reset Window Size to 720p + Скинути розмір вікна до 720p + + + + Reset Window Size to &900p + Скинути розмір вікна до &900p + + + + Reset Window Size to 900p + Скинути розмір вікна до 900p + + + + Reset Window Size to &1080p + Скинути розмір вікна до &1080p + + + + Reset Window Size to 1080p + Скинути розмір вікна до 1080p + + + + &Multiplayer + [&M] Мультиплеєр + + + + &Tools + [&T] Інструменти + + + + &TAS + [&T] TAS + + + + &Help + [&H] Допомога + + + + &Install Files to NAND... + [&I] Встановити файли в NAND... + + + + L&oad File... + [&O] Завантажити файл... + + + + Load &Folder... + [&F] Завантажити папку... + + + + E&xit + [&X] Вихід + + + + &Pause + [&P] Пауза + + + + &Stop + [&S] Стоп + + + + &Reinitialize keys... + [&R] Переініціалізувати ключі... + + + + &About yuzu + [&A] Про yuzu + + + + Single &Window Mode + [&W] Режим одного вікна + + + + Con&figure... + [&F] Налаштування... + + + + Display D&ock Widget Headers + [&O] Відображати заголовки віджетів дока + + + + Show &Filter Bar + [&F] Показати панель пошуку + + + + Show &Status Bar + [&S] Показати панель статусу + + + + Show Status Bar + Показати панель статусу + + + + &Browse Public Game Lobby + [&B] Переглянути публічні ігрові фойє + + + + &Create Room + [&C] Створити кімнату + + + + &Leave Room + [&L] Залишити кімнату + + + + &Direct Connect to Room + [&D] Пряме під'єднання до кімнати + + + + &Show Current Room + [&S] Показати поточну кімнату + + + + F&ullscreen + [&U] Повноекранний + + + + &Restart + [&R] Перезапустити + + + + Load/Remove &Amiibo... + [&A] Завантажити/Видалити Amiibo... + + + + &Report Compatibility + [&R] Повідомити про сумісність + + + + Open &Mods Page + [&M] Відкрити сторінку модів + + + + Open &Quickstart Guide + [&Q] Відкрити посібник користувача + + + + &FAQ + [&F] ЧАП + + + + Open &yuzu Folder + [&Y] Відкрити папку yuzu + + + + &Capture Screenshot + [&C] Зробити знімок екрану + + + + &Configure TAS... + [&C] Налаштування TAS... + + + + Configure C&urrent Game... + [&U] Налаштувати поточну гру... + + + + &Start + [&S] Почати + + + + &Reset + [&S] Скинути + + + + R&ecord + [&E] Запис + + + + MicroProfileDialog + + + &MicroProfile + [&M] MicroProfile + + + + ModerationDialog + + + Moderation + Модерація + + + + Ban List + Список заблокованих + + + + + Refreshing + Оновлення + + + + Unban + Розблокувати + + + + Subject + Суб'єкт + + + + Type + Тип + + + + Forum Username + Ім'я користувача на форумі + + + + IP Address + IP-адреса + + + + Refresh + Оновити + + + + MultiplayerState + + + Current connection status + Поточний стан з'єднання + + + + Not Connected. Click here to find a room! + Не з'єднано. Натисніть тут, щоб знайти кімнату! + + + + Not Connected + Не з'єднано + + + + Connected + З'єднано + + + + New Messages Received + Отримано нові повідомлення + + + + Error + Помилка + + + + Failed to update the room information. Please check your Internet connection and try hosting the room again. +Debug Message: + Не вдалося оновити інформацію про кімнату. Будь ласка, перевірте підключення до Інтернету та спробуйте знову зайти в кімнату. +Повідомлення налагодження: + + + + NetworkMessage + + + Username is not valid. Must be 4 to 20 alphanumeric characters. + Ім'я користувача неприпустиме. Має бути від 4 до 20 буквено-цифрових символів. + + + + Room name is not valid. Must be 4 to 20 alphanumeric characters. + Назва кімнати неприпустима. Має бути від 4 до 20 буквено-цифрових символів. + + + + Username is already in use or not valid. Please choose another. + Ім'я користувача вже використовується або недійсне. Будь ласка, виберіть інше. + + + + IP is not a valid IPv4 address. + IP-адреса не є дійсною адресою IPv4. + + + + Port must be a number between 0 to 65535. + Порт повинен бути числом від 0 до 65535. + + + + You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. + Ви повинні вибрати бажану гру, щоб хостити кімнату. Якщо у вашому списку ігор ще немає жодної гри, додайте папку з грою, натиснувши на значок плюса у списку ігор. + + + + Unable to find an internet connection. Check your internet settings. + Неможливо знайти підключення до Інтернету. Перевірте налаштування інтернету. + + + + Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. + Неможливо підключитися до хоста. Перевірте правильність налаштувань підключення. Якщо під'єднання, як і раніше, неможливе, зв'яжіться з хостом кімнати та переконайтеся, що хост правильно налаштований із прокинутим зовнішнім портом. + + + + Unable to connect to the room because it is already full. + Неможливо підключитися до кімнати, оскільки вона вже заповнена. + + + + Creating a room failed. Please retry. Restarting yuzu might be necessary. + Створення кімнати не вдалося. Будь ласка, повторіть спробу. Можливо, потрібно перезапустити yuzu. + + + + The host of the room has banned you. Speak with the host to unban you or try a different room. + Хост кімнати заблокував вас. Поговоріть із хостом, щоб він розблокував вас, або спробуйте іншу кімнату. + + + + Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. + Невідповідність версій! Будь ласка, оновіть yuzu до останньої версії. Якщо проблема не зникне, зверніться до хосту кімнати і попросіть його оновити сервер. + + + + Incorrect password. + Невірний пароль. + + + + An unknown error occurred. If this error continues to occur, please open an issue + Сталася невідома помилка. Якщо ця помилка продовжує виникати, будь ласка, відкрийте проблему + + + + Connection to room lost. Try to reconnect. + З'єднання з кімнатою втрачено. Спробуйте підключитися знову. + + + + You have been kicked by the room host. + Вас вигнав хост кімнати. + + + + IP address is already in use. Please choose another. + IP-адреса вже використовується. Будь ласка, виберіть іншу. + + + + You do not have enough permission to perform this action. + У вас немає достатніх дозволів для виконання цієї дії. + + + + The user you are trying to kick/ban could not be found. +They may have left the room. + Користувача, якого ви намагаєтеся вигнати/заблокувати, не знайдено. +Можливо, вони покинули кімнату. + + + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Не вибрано припустимий інтерфейс мережі. +Будь ласка, перейдіть у Налаштування -> Система -> Мережа та зробіть вибір. + + + + Game already running + Гру вже запущено + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Приєднуватися до кімнати, коли гру вже запущено, не рекомендується, це може призвести до неправильної роботи функції кімнати. +Все одно продовжити? + + + + Leave Room + Залишити кімнату + + + + You are about to close the room. Any network connections will be closed. + Ви збираєтеся закрити кімнату. Усі мережеві підключення буде закрито. + + + + Disconnect + Від'єднатися + + + + You are about to leave the room. Any network connections will be closed. + Ви збираєтеся покинути кімнату. Усі мережеві підключення буде закрито. + + + + NetworkMessage::ErrorManager + + + Error + Помилка + + + + OverlayDialog + + + Dialog + Діалог + + + + + Cancel + Скасувати + + + + + OK + ОК + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + PlayerControlPreview + + + START/PAUSE + СТАРТ/ПАУЗА + + + + QObject + + + %1 is not playing a game + %1 не грає у гру + + + + %1 is playing %2 + %1 грає в %2 + + + + Not playing a game + Не грає в гру + + + + Installed SD Titles + Встановлені SD ігри + + + + Installed NAND Titles + Встановлені NAND ігри + + + + System Titles + Системні ігри + + + + Add New Game Directory + Додати нову папку з іграми + + + + Favorites + Улюблені + + + + + + Shift + Shift + + + + + + Ctrl + Ctrl + + + + + + Alt + Alt + + + + + + + + [not set] + [не задано] + + + + Hat %1 %2 + Напр. %1 %2 + + + + + + + + + + + + Axis %1%2 + Ось %1%2 + + + + Button %1 + Кнопка %1 + + + + + + + + + + [unknown] + [невідомо] + + + + + + Left + Вліво + + + + + + Right + Вправо + + + + + + Down + Вниз + + + + + + Up + Вгору + + + + + Z + Z + + + + + R + R + + + + + L + L + + + + + A + A + + + + + B + B + + + + + X + X + + + + + Y + Y + + + + + Start + Start + + + + + L1 + L1 + + + + + L2 + L2 + + + + + L3 + L3 + + + + + R1 + R1 + + + + + R2 + R2 + + + + + R3 + R3 + + + + + Circle + Кружечок + + + + + Cross + Хрестик + + + + + Square + Квадратик + + + + + Triangle + Трикутничок + + + + + Share + Share + + + + + Options + Options + + + + + [undefined] + [невизначено] + + + + %1%2 + %1%2 + + + + + [invalid] + [неприпустимо] + + + + + + + %1%2Hat %3 + %1%2Напр. %3 + + + + + + + + + %1%2Axis %3 + %1%2Ось %3 + + + + + %1%2Axis %3,%4,%5 + %1%2Ось %3,%4,%5 + + + + + %1%2Motion %3 + %1%2Рух %3 + + + + + + + %1%2Button %3 + %1%2Кнопка %3 + + + + + [unused] + [не використаний] + + + + Home + Home + + + + Touch + Сенсор + + + + Wheel + Indicates the mouse wheel + Коліщатко + + + + Backward + Назад + + + + Forward + Вперед + + + + Task + Задача + + + + Extra + Додаткова + + + + %1%2%3 + %1%2%3 + + + + QtControllerSelectorDialog + + + Controller Applet + Аплет контролера + + + + Supported Controller Types: + Підтримувані типи контролерів: + + + + Players: + Гравці: + + + + 1 - 8 + 1 - 8 + + + + P4 + P4 + + + + + + + + + + + + Pro Controller + Контролер Pro + + + + + + + + + + + + Dual Joycons + Подвійні Joy-Con'и + + + + + + + + + + + + Left Joycon + Лівий Joy-Con + + + + + + + + + + + + Right Joycon + Правий Joy-Con + + + + + + + + + + + Use Current Config + Використовувати поточну конфігурацію + + + + P2 + P2 + + + + P1 + P1 + + + + + + Handheld + Портативний + + + + P3 + P3 + + + + P7 + P7 + + + + P8 + P8 + + + + P5 + P5 + + + + P6 + P6 + + + + Console Mode + Режим консолі + + + + Docked + У док-станції + + + + Vibration + Вібрація + + + + + Configure + Налаштувати + + + + Motion + Рух + + + + Profiles + Профілі + + + + Create + Створити + + + + Controllers + Контролери + + + + 1 + 1 + + + + 2 + 2 + + + + 4 + 4 + + + + 3 + 3 + + + + Connected + З'єднано + + + + 5 + 5 + + + + 7 + 7 + + + + 6 + 6 + + + + 8 + 8 + + + + GameCube Controller + Контролер GameCube + + + + Poke Ball Plus + Poke Ball Plus + + + + NES Controller + Контролер NES + + + + SNES Controller + Контролер SNES + + + + N64 Controller + Контролер N64 + + + + Sega Genesis + Sega Genesis + + + + QtErrorDisplay + + + + + Error Code: %1-%2 (0x%3) + Код помилки: %1-%2 (0x%3) + + + + An error has occurred. +Please try again or contact the developer of the software. + Сталася помилка. +Будь ласка, спробуйте ще раз або зв'яжіться з розробником ПЗ. + + + + An error occurred on %1 at %2. +Please try again or contact the developer of the software. + Сталася помилка на %1 у %2. +Будь ласка, спробуйте ще раз або зв'яжіться з розробником ПЗ. + + + + An error has occurred. + +%1 + +%2 + Сталася помилка. + +%1 + +%2 + + + + QtProfileSelectionDialog + + + %1 +%2 + %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) + %1 +%2 + + + + Select a user: + Оберить користувача + + + + Users + Користувачі + + + + Profile Selector + Вибір профілю + + + + QtSoftwareKeyboardDialog + + + Software Keyboard + Віртуальна клавіатура + + + + Enter Text + Введіть текст + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + OK + ОК + + + + Cancel + Скасувати + + + + SequenceDialog + + + Enter a hotkey + Введіть комбінацію + + + + WaitTreeCallstack + + + Call stack + Стек викликів + + + + WaitTreeMutexInfo + + + waiting for mutex 0x%1 + + + + + has waiters: %1 + + + + + owner handle: 0x%1 + + + + + WaitTreeObjectList + + + waiting for all objects + + + + + waiting for one of the following objects + + + + + WaitTreeSynchronizationObject + + + [%1] %2 %3 + [%1] %2 %3 + + + + waited by no thread + + + + + WaitTreeThread + + + runnable + + + + + paused + + + + + sleeping + + + + + waiting for IPC reply + + + + + waiting for objects + + + + + waiting for condition variable + + + + + waiting for address arbiter + + + + + waiting for suspend resume + + + + + waiting + + + + + initialized + + + + + terminated + + + + + unknown + + + + + PC = 0x%1 LR = 0x%2 + PC = 0x%1 LR = 0x%2 + + + + ideal + + + + + core %1 + + + + + processor = %1 + + + + + ideal core = %1 + + + + + affinity mask = %1 + + + + + thread id = %1 + + + + + priority = %1(current) / %2(normal) + + + + + last running ticks = %1 + + + + + not waiting for mutex + + + + + WaitTreeThreadList + + + waited by thread + + + + + WaitTreeWidget + + + &Wait Tree + [&W] Дерево очікування + + + \ No newline at end of file From f325fcb131e2c703fa465d3ebb1d824cfe2810d1 Mon Sep 17 00:00:00 2001 From: Ludovic <9815926+driskiou@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:33:53 +0100 Subject: [PATCH 244/245] remove unnecessary sepator in file menu (main.ui) --- src/yuzu/main.ui | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 74d49dbd41..e670acc302 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -55,7 +55,6 @@ - From 75ab52f05b39a625c039705f9290278efdad98f7 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 30 Oct 2022 16:37:28 -0700 Subject: [PATCH 245/245] core: hle: service: acc: Fix ListOpenContextStoredUsers/StoreOpenContext. - These APIs are used to capture the opened users and allow that state to be persisted across processes. - They are not intended to just return the system opened users, that is what ListOpenUsers is for. - Fixes the launch hang with Bayonetta 3. --- src/core/hle/service/acc/acc.cpp | 34 ++++++++------------ src/core/hle/service/acc/acc.h | 1 - src/core/hle/service/acc/acc_u0.cpp | 2 +- src/core/hle/service/acc/profile_manager.cpp | 25 ++++++++++++++ src/core/hle/service/acc/profile_manager.h | 3 ++ 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index bb838e2850..85a3f08023 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -512,10 +512,11 @@ protected: class IManagerForApplication final : public ServiceFramework { public: - explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) + explicit IManagerForApplication(Core::System& system_, + const std::shared_ptr& profile_manager_) : ServiceFramework{system_, "IManagerForApplication"}, ensure_token_id{std::make_shared(system)}, - user_id{user_id_} { + profile_manager{profile_manager_} { // clang-format off static const FunctionInfo functions[] = { {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, @@ -545,7 +546,7 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushRaw(user_id.Hash()); + rb.PushRaw(profile_manager->GetLastOpenedUser().Hash()); } void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { @@ -575,17 +576,20 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushRaw(user_id.Hash()); + rb.PushRaw(profile_manager->GetLastOpenedUser().Hash()); } void StoreOpenContext(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "called"); + + profile_manager->StoreOpenedUsers(); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } std::shared_ptr ensure_token_id{}; - Common::UUID user_id{}; + std::shared_ptr profile_manager; }; // 6.0.0+ @@ -790,7 +794,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, profile_manager->GetLastOpenedUser()); + rb.PushIpcInterface(system, profile_manager); } void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { @@ -849,22 +853,10 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - - // This is similar to GetBaasAccountManagerForApplication - // This command is used concurrently with ListOpenContextStoredUsers - // TODO: Find the differences between this and GetBaasAccountManagerForApplication - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, profile_manager->GetLastOpenedUser()); -} - void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "called"); - // TODO(ogniK): Handle open contexts - ctx.WriteBuffer(profile_manager->GetOpenUsers()); + ctx.WriteBuffer(profile_manager->GetStoredOpenedUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 1621e7c0ac..9411b0b92d 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -35,7 +35,6 @@ public: void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx); void GetProfileEditor(Kernel::HLERequestContext& ctx); void ListQualifiedUsers(Kernel::HLERequestContext& ctx); - void LoadOpenContext(Kernel::HLERequestContext& ctx); void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 65023b8c22..54844bfe7d 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -28,7 +28,7 @@ ACC_U0::ACC_U0(std::shared_ptr module_, std::shared_ptr {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, - {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ + {130, nullptr, "LoadOpenContext"}, // 5.0.0+ {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index a58da4d5f5..481e0d1410 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -261,6 +261,31 @@ UUID ProfileManager::GetLastOpenedUser() const { return last_opened_user; } +/// Gets the list of stored opened users. +UserIDArray ProfileManager::GetStoredOpenedUsers() const { + UserIDArray output{}; + std::ranges::transform(stored_opened_profiles, output.begin(), [](const ProfileInfo& p) { + if (p.is_open) + return p.user_uuid; + return Common::InvalidUUID; + }); + std::stable_partition(output.begin(), output.end(), + [](const UUID& uuid) { return uuid.IsValid(); }); + return output; +} + +/// Captures the opened users, which can be queried across process launches with +/// ListOpenContextStoredUsers. +void ProfileManager::StoreOpenedUsers() { + size_t profile_index{}; + stored_opened_profiles = {}; + std::for_each(profiles.begin(), profiles.end(), [&](const auto& profile) { + if (profile.is_open) { + stored_opened_profiles[profile_index++] = profile; + } + }); +} + /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(std::optional index, ProfileBase& profile, UserData& data) const { diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 135f7d0d59..993a5a57af 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -86,6 +86,8 @@ public: UserIDArray GetOpenUsers() const; UserIDArray GetAllUsers() const; Common::UUID GetLastOpenedUser() const; + UserIDArray GetStoredOpenedUsers() const; + void StoreOpenedUsers(); bool CanSystemRegisterUser() const; @@ -101,6 +103,7 @@ private: bool RemoveProfileAtIndex(std::size_t index); std::array profiles{}; + std::array stored_opened_profiles{}; std::size_t user_count{}; Common::UUID last_opened_user{}; };